Sweep Line Algorithm
The sweep line algorithm is a powerful technique in computational geometry that processes geometric objects by sweeping a line across the plane. This method efficiently solves problems involving intervals, line segments, and geometric intersections with optimal time complexity.
Line Segment Intersection
Find all intersections among n line segments using a sweep line that moves from left to right.
Line Segment Intersection Detection
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
#include <map>
using namespace std;
struct Point {
double x, y;
Point(double x = 0, double y = 0) : x(x), y(y) {}
Point operator-(const Point& p) const { return Point(x - p.x, y - p.y); }
double cross(const Point& p) const { return x * p.y - y * p.x; }
};
struct Segment {
Point a, b;
int id;
Segment(Point a, Point b, int id) : a(a), b(b), id(id) {
if (a.x > b.x) swap(this->a, this->b);
}
};
struct Event {
double x;
int type; // 0: start, 1: end
int segId;
bool operator<(const Event& other) const {
if (x != other.x) return x < other.x;
return type < other.type; // Process start events first
}
};
class SweepLine {
private:
vector<Segment> segments;
vector<Event> events;
double getY(const Segment& seg, double x) {
if (seg.a.x == seg.b.x) return seg.a.y;
double t = (x - seg.a.x) / (seg.b.x - seg.a.x);
return seg.a.y + t * (seg.b.y - seg.a.y);
}
bool intersect(const Segment& s1, const Segment& s2) {
Point d1 = s1.b - s1.a;
Point d2 = s2.b - s2.a;
Point d3 = s2.a - s1.a;
double cross1 = d1.cross(d2);
if (abs(cross1) < 1e-9) return false; // Parallel
double t1 = d3.cross(d2) / cross1;
double t2 = d3.cross(d1) / cross1;
return t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1;
}
public:
void addSegment(Point a, Point b) {
segments.push_back(Segment(a, b, segments.size()));
events.push_back({min(a.x, b.x), 0, (int)segments.size() - 1});
events.push_back({max(a.x, b.x), 1, (int)segments.size() - 1});
}
vector<pair<int, int>> findIntersections() {
sort(events.begin(), events.end());
auto cmp = [&](int a, int b) {
double x = events.empty() ? 0 : events[0].x;
return getY(segments[a], x) < getY(segments[b], x);
};
set<int, decltype(cmp)> active(cmp);
vector<pair<int, int>> intersections;
for (const Event& e : events) {
if (e.type == 0) { // Start event
auto it = active.insert(e.segId).first;
// Check intersection with neighbors
if (it != active.begin()) {
auto prev = prev(it);
if (intersect(segments[*prev], segments[e.segId])) {
intersections.push_back({*prev, e.segId});
}
}
auto next_it = next(it);
if (next_it != active.end()) {
if (intersect(segments[e.segId], segments[*next_it])) {
intersections.push_back({e.segId, *next_it});
}
}
} else { // End event
auto it = active.find(e.segId);
// Check if neighbors intersect after removal
if (it != active.begin() && next(it) != active.end()) {
auto prev_it = prev(it);
auto next_it = next(it);
if (intersect(segments[*prev_it], segments[*next_it])) {
intersections.push_back({*prev_it, *next_it});
}
}
active.erase(it);
}
}
return intersections;
}
};
int main() {
SweepLine sweepLine;
// Add test segments
sweepLine.addSegment(Point(0, 0), Point(4, 4));
sweepLine.addSegment(Point(0, 4), Point(4, 0));
sweepLine.addSegment(Point(1, 1), Point(3, 3));
sweepLine.addSegment(Point(2, 0), Point(2, 5));
vector<pair<int, int>> intersections = sweepLine.findIntersections();
cout << "Found " << intersections.size() << " intersections:" << endl;
for (auto& p : intersections) {
cout << "Segments " << p.first << " and " << p.second << " intersect" << endl;
}
return 0;
}The sweep line maintains active segments in a balanced BST ordered by y-coordinate. When a segment starts/ends, we check intersections with its neighbors in the active set.
Rectangle Union Area
Calculate the total area covered by a union of rectangles using coordinate compression and sweep line.
Rectangle Union Area Calculation
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
struct Rectangle {
int x1, y1, x2, y2;
Rectangle(int x1, int y1, int x2, int y2) : x1(x1), y1(y1), x2(x2), y2(y2) {}
};
struct Event {
int x, y1, y2, type; // type: +1 for left edge, -1 for right edge
bool operator<(const Event& other) const {
if (x != other.x) return x < other.x;
return type > other.type; // Process +1 events first
}
};
class SegmentTree {
private:
vector<int> tree, lazy;
vector<int> coords;
int n;
void push(int node, int start, int end) {
if (lazy[node] != 0) {
tree[node] = (lazy[node] > 0) ? coords[end + 1] - coords[start] : 0;
if (start != end) {
lazy[2 * node] += lazy[node];
lazy[2 * node + 1] += lazy[node];
}
lazy[node] = 0;
}
}
void update(int node, int start, int end, int l, int r, int val) {
push(node, start, end);
if (start > r || end < l) return;
if (start >= l && end <= r) {
lazy[node] += val;
push(node, start, end);
return;
}
int mid = (start + end) / 2;
update(2 * node, start, mid, l, r, val);
update(2 * node + 1, mid + 1, end, l, r, val);
push(2 * node, start, mid);
push(2 * node + 1, mid + 1, end);
tree[node] = tree[2 * node] + tree[2 * node + 1];
}
public:
SegmentTree(vector<int>& coordinates) : coords(coordinates) {
n = coords.size() - 1;
tree.resize(4 * n);
lazy.resize(4 * n);
}
void update(int l, int r, int val) {
if (l <= r) update(1, 0, n - 1, l, r, val);
}
int query() {
push(1, 0, n - 1);
return tree[1];
}
};
long long rectangleUnionArea(vector<Rectangle>& rectangles) {
vector<Event> events;
vector<int> yCoords;
// Create events and collect y-coordinates
for (const Rectangle& rect : rectangles) {
events.push_back({rect.x1, rect.y1, rect.y2 - 1, 1});
events.push_back({rect.x2, rect.y1, rect.y2 - 1, -1});
yCoords.push_back(rect.y1);
yCoords.push_back(rect.y2);
}
sort(events.begin(), events.end());
sort(yCoords.begin(), yCoords.end());
yCoords.erase(unique(yCoords.begin(), yCoords.end()), yCoords.end());
// Coordinate compression mapping
map<int, int> yMap;
for (int i = 0; i < yCoords.size(); i++) {
yMap[yCoords[i]] = i;
}
SegmentTree segTree(yCoords);
long long totalArea = 0;
int prevX = events[0].x;
for (const Event& event : events) {
// Add area from previous x to current x
totalArea += (long long)(event.x - prevX) * segTree.query();
// Update segment tree
int y1Idx = yMap[event.y1];
int y2Idx = yMap[event.y2 + 1] - 1;
segTree.update(y1Idx, y2Idx, event.type);
prevX = event.x;
}
return totalArea;
}
int main() {
vector<Rectangle> rectangles = {
Rectangle(0, 0, 3, 3),
Rectangle(1, 1, 4, 4),
Rectangle(2, 2, 5, 5),
Rectangle(6, 0, 8, 2)
};
long long area = rectangleUnionArea(rectangles);
cout << "Total union area: " << area << endl;
// Verify with individual areas
cout << "Individual rectangle areas:" << endl;
for (int i = 0; i < rectangles.size(); i++) {
Rectangle& r = rectangles[i];
int area = (r.x2 - r.x1) * (r.y2 - r.y1);
cout << "Rectangle " << i << ": " << area << endl;
}
return 0;
}We use coordinate compression to map y-coordinates to indices, then maintain a segment tree to track which y-intervals are covered as we sweep the x-coordinate.