15. Computational Geometry Topics An examination of some important CG topics; A taster of a very large research area. Contest Algorithms: 15. CG Topics
Overview Intersection of Multiple Line Segments the sweeping algorithm Finding the Convex Hull Graham Scan, Jarvis' March (Gift Wrapping) Finding the Closest Pair of Points divide-and-conquer (O(n· log n)
1. Intersection of Multiple Line Segments 1.1. Multiple Line Segments 1.2. A Brute-Force Algorithm 1.3. The Sweeping Algorithm 1.4. Implementing Sweeping
1.1. Multiple Line Segments Input: a set of n line segments in the plane. Output: all intersections, and for each intersection the involved segments. .
1.2. A Brute-Force Algorithm Look at each pair of segments, and check if they intersect. If so, output the intersection. n(n-1)/2 comparison are needed in the worst case, so the running time is O(n2) But the lines are sparsly distributed in practice: Most segments do not intersect, or if they do, only with a few other segments Need a faster algorithm that deals with such situations!
Code see SegmentsIntersect.java public static ArrayList<Pair<Segment,Segment>> findIntersectsNaive(ArrayList<Segment> segs) /* Determines whether any two segs in the given set intersect using the brute force approach in O(n^2). */ { ArrayList<Pair<Segment,Segment>> segPairs = new ArrayList<>(); for (int i = 0; i < segs.size()-1; i++) { for (int j = i + 1; j < segs.size(); j++) { if (segs.get(i).intersects(segs.get(j))) segPairs.add( new Pair<Segment,Segment>(segs.get(i), segs.get(j)) ); } return segPairs; } // end of findIntersectsNaive() Contest Algorithms
Usage ArrayList<Segment> segs = new ArrayList<>(); segs.add( new Segment( new Pt(1,1), new Pt(4,3))); segs.add( new Segment( new Pt(2,4), new Pt(6,1))); segs.add( new Segment( new Pt(3,5), new Pt(6,3))); segs.add( new Segment( new Pt(5,1), new Pt(7,5))); ArrayList<Pair<Segment,Segment>> segPairs = SegmentsIntersect.findIntersectsNaive(segs); System.out.println("Intersecting segments:"); for(Pair<Segment,Segment> pair : segPairs) System.out.println(" " + pair.getX() + " with " + pair.getY()); Contest Algorithms:15. CG Topics
Results Contest Algorithms:15. CG Topics
1.3. The Sweeping Algorithm Avoid testing pairs of segments that are far apart. O(n log n) Idea: imagine a vertical sweep line passes through the given set of line segments, from left to right. Sweep line also known as the "Bentley-Ottmann" Algorithm and the Sweep Line Algorithm
Non-Degeneracy Assumptions No segment is vertical. // this means that the sweep line will always hit a segment at a point. If an input segment is vertical, then it is rotated clockwise by a tiny angle.
Sweep Line Status The set of segments intersecting the sweep line. It changes as the sweep line moves, but not continuously. endpoints Updates of status happen only at event points. intersections A G C T event points
Ordering Segments A total order over the segments that intersect the current position of the sweep line: later C > D (B drops out of the ordering) B > C > D (A and E not in the ordering) B E A C D At an event point, the sequence of segments changes: Update the status. Detect the intersections.
Status Update (1) Event point is the left endpoint of a segment. A new segment L intersecting the sweep line K O L Check if L intersects with the segment above (K) and the segment below (M). M new event point N Intersection(s) are new event points. K, M, N K, L, M, N
Status Update (2) Event point is an intersection. The two intersecting segments (L and M) change order. K O L Check intersection with new neighbors (M with O and L with N). M Intersection(s) are new event points. N O, L, M, N O, M, L, N
Status Update (3) Event point is a lower endpoint of a segment. The two neighbors (O and L) become adjacent. K O L Check if they (O and L) intersect. M Intersection is new event point. N O, M, L, N O, L, N
1.4. Implementing Sweeping The algorithm manages two kinds of data: 1. The sweep line status gives the relationships among the objects intersected by the sweep line. 2. The event point queue is a sequence of event points that defines the halting positions of the sweep line.
Event Point Queue Operations Manages the ordering of event points: by x-coordinates by y-coordinates in case of a tie in x-coordinates Supports the following operations on a segment s. m = no. of event points currently being managed fetching the next event // O(log m) inserting an event // O(log m) Every event point p is stored with all segments starting at p. Data structure: a balanced binary search tree (e.g., red-black tree).
Sweep Line Operations The sweep line status (T) requires the following operations: INSERT(T, s): insert segment s into T. DELETE(T, s): delete segment s from T. ABOVE(T, s): return the segment immediately above segment s in T. BELOW(T, s): return the segment immediately below segment s in T. Use a balanced binary search tree for T (e.g. Red-black trees): O(log n) for each operation
Segments intersect Pseudocode ANY-SEGMENTS-INTERSECT(S) 1 T = Ø 2 sort the endpoints of the segments in S from left to right, breaking ties by putting left endpoints before right endpoints and breaking further ties by putting points with lower y-coordinates first 3 for (each point p in the sorted list of endpoints) { 4 if (p is the left endpoint of a segment s) { 5 INSERT(T, s) 6 if (ABOVE(T, s) exists and intersects s) or (BELOW(T, s) exists and intersects s) 7 return TRUE } 8 if (p is the right endpoint of a segment s) { 9 if (both ABOVE(T, s) and BELOW(T, s) exist) and (ABOVE(T, s) intersects BELOW(T, s)) 10 return TRUE 11 DELETE(T, s) } } 12 return FALSE
Execution Example e d b the intersection of segments d and b is detected when segment c is deleted
Java Code https://github.com/bkiers/CompGeom/blob/master/src/main/compgeom/util/SweepLine.java and EventQueue.java, Event.java (This code does not give correct answers in every case, but perhaps this is due to the many changes I made to get the code to work with my CG classes.) Contest Algorithms:15. CG Topics
1.5. An Alternative to Sweeping My SegmentsIntersect.findIntersects() orders the segments' start and end points in left-to-right order. It adds and removes segments to a list based on their start and end points, and the current point being considered. The running time in the worst case is O(n2), but is much faster on average. The coding is much shorter than using SweepLine It relies on a Pt object linking to its parent Segment object Contest Algorithms:15. CG Topics
Code public static ArrayList<Pair<Segment,Segment>> findIntersects(ArrayList<Segment> segs) { ArrayList<Pair<Segment,Segment>> segPairs = new ArrayList<>(); // to hold the pairs of lines that interect ArrayList<Segment> segsList = new ArrayList<Segment>(); ArrayList<Pt> points = new ArrayList<Pt>(); for (Segment s : segs) { s.p1.setSegment(s); // link start-point p1 to its segment s points.add(s.p1); s.p2.setSegment(s); // link end-point p2 to its segment s points.add(s.p2); } Collections.sort(points); System.out.println("Sorted points: " + Arrays.toString(points.toArray())); : Contest Algorithms:15. CG Topics
// look at segments based on their left-to right point order for (Pt pt : points) { Segment pSeg = pt.getSegment(); if (pt.isStartPoint()) { // check seg against every seg in list for(Segment s : segsList) { if (pSeg.intersects(s)) segPairs.add( new Pair<Segment,Segment>(pSeg,s) ); // store intersection pair of segment } segsList.add(pSeg); // add seg to list since reached its start-point else // is end-point, so remove the segment from the list segsList.remove(pSeg); return segPairs; } // end of findIntersects() Contest Algorithms:15. CG Topics
Results Contest Algorithms:15. CG Topics
2. Finding the Convex Hull 2.1. Convex & Concave Sets 2.2. The Convex Hull 2.3. The Graham Scan 2.4. Jarvis’ March
2.1. Convex and Concave Sets A planar region R is called convex if and only if for any pair of points p, q in R, the line segment pq lies completely in R. Otherwise, it is called concave. p q R 2 Concave Convex p q R 1
2.2. The Convex Hull The convex hull of a set of points P is the smallest convex region that contains all of P. Rubber band When P is finite, its convex hull is the unique convex polygon whose vertices are from P and that contains all points of P.
The Convex Hull Problem Input: a set P = { p , p , …, p } of points 1 2 n Output: a list of vertices in the convex hull in counterclockwise order. Example 8 p 9 2 10 5 7 6 1 3 4 Not all the points in P are in the hull hull = [ p5, p9, p2, p8, p10, p7 ]
2.3. The Graham Scan O(n· log n) p sort by polar angle 1 2 3 4 5 6 7 8 9 10 11 O(n· log n) sort by polar angle The center point has the minimum y-coordinate p How to break a tie? Labels are in the polar angle order. (What if two points have the same polar angle?)
A Turning Algorithm Consider each of the points in the sorted sequence. For each point, is moving from the two previously considered points to this point a "left turn" or "right turn"? "Right turn": this means that the second-to-last point is not part of the convex hull and should be removed. this process is continued for as long as the set of the last three points is a "right turn" "Left turn": the algorithm moves to the next point in the sequence.
Turning in Action X C is a left turn compared to A – B; add C D is a right turn compared to B - C; remove C X D is a left turn compared to A - B; add D
Code public static ArrayList<Pt> grahamScan(Pt[] points) { // points[numPts-1] is different from points[0] // defensive copy int numPts = points.length; Pt[] pts = new Pt[numPts]; for (int i = 0; i < numPts; i++) pts[i] = points[i]; Pt lowPt = getLowestPoint(pts); // sort by polar angle with respect to lowPt, // breaking ties by distance to lowPt Arrays.sort(pts, new PolarOrder(lowPt)); System.out.println("sorted pts: " + Arrays.toString(pts)); Stack<Pt> stk = new Stack<Pt>(); stk.push(pts[0]); // lowest point : see ConvexHullTests.java Contest Algorithms
// find index k1 of first point not equal to pts[0] int k1; for (k1 = 1; k1 < numPts; k1++) if (!pts[0].equals(pts[k1])) break; if (k1 == numPts) { System.out.println("All points are the same"); return null; // all pts equal } // find index k2 of first point not collinear with pts[0] and pts[k1] int k2; for (k2 = k1 + 1; k2 < numPts; k2++) if (Pt.ccw(pts[0], pts[k1], pts[k2]) != 0) stk.push(pts[k2 - 1]); // pts[k2-1] is second extreme point : Contest Algorithms:15. CG Topics
// Graham scan; for (int i = k2; i < numPts; i++) { Pt top = stk // Graham scan; for (int i = k2; i < numPts; i++) { Pt top = stk.pop(); while (Pt.ccw(stk.peek(),top, pts[i]) <= 0) top = stk.pop(); stk.push(top); stk.push(pts[i]); } ArrayList<Pt> hull = new ArrayList<Pt>(); while(!stk.empty()) hull.add(0, stk.pop()); // reverse order return hull; } // end of grahamScan() Contest Algorithms:15. CG Topics
public class PolarOrder implements Comparator<Pt> { private Pt lowPt; public PolarOrder(Pt lowPt) { this.lowPt = lowPt; } public int compare(Pt p1, Pt p2) double cp = Pt.ccw(lowPt, p1, p2); if (cp > 0) return -1; if (cp < 0) return 1; // collinear; order by distance from lowPt double d1 = lowPt.dist(p1); double d2 = lowPt.dist(p2); if (d1 < d2) if (d1 > d2) return 0; } // end of compare() } // end of PolarOrder class see PolarOrder.java Contest Algorithms:15. CG Topics
Usage see ConvexHullTexts.java public static void main(String[] args) throws Exception { if (args.length != 1) { System.out.println("Usage: java ConvexHullTests <data-file>"); return; } Scanner sc = new Scanner(new File(args[0])); int numPts = sc.nextInt(); Pt[] points = new Pt[numPts]; for (int i = 0; i < numPts; i++) points[i] = new Pt(sc.nextInt(), sc.nextInt()); ArrayList<Pt> hull = grahamScan(points); System.out.println("Graham Scan convex hull: "); for (Pt p : hull) System.out.println(" " + p); : Contest Algorithms:15. CG Topics
Input Data hull3.txt 8 0 3 4 2 3 5 5 3 3 0 1 1 1 2 2 2 Contest Algorithms:15. CG Topics
Execution > java ConvexHullTests hull3.txt sorted pts: [(3.000, 0.000), (5.000, 3.000), (4.000, 2.000), (3.000, 5.000), (2.000, 2.000), (1.000, 2.000), (0.000, 3.000), (1.000, 1.000)] Graham Scan convex hull: (3.000, 0.000) (5.000, 3.000) (3.000, 5.000) (0.000, 3.000) (1.000, 1.000) a counter-clockwise convex hull starting at the lowest point Contest Algorithms:15. CG Topics
Stack Usage p p 6 9 p p 7 4 p 11 p S 1 2 p 8 p p 5 10 p 3 p p 2 1 p
p p 6 9 p p 7 4 p 11 S p 8 p p 5 10 p p 3 3 p 1 p p 2 1 p p
p p 6 9 p p 7 4 p 11 S p 8 p p 5 10 p p 3 4 p 1 p p 2 1 p p
p p 6 S 9 p p 7 4 p 11 p p 8 p 5 p 5 10 p p 3 4 p 1 p p 2 1 p p
p p 6 S 9 p p 7 4 p 11 p p 6 8 p p 5 10 p p 3 4 p 1 p p 2 1 p p
S p p 8 p 6 9 p p p 7 4 7 p 11 p p 6 8 p p 5 10 p p 3 4 p 1 p p 2 1 p p
S p p 6 9 p p p 7 4 7 p 11 p p 6 8 p p 5 10 p p 3 4 p 1 p p 2 1 p p
S p p 10 p 6 9 p p 4 9 p p 11 7 p p 6 8 p p 5 10 p p 3 4 p 1 p p 2 1 p p
S p p 11 p 6 9 p p 4 9 p p 11 7 p p 6 8 p p 5 10 p p 3 4 p 1 p p 2 1 p p
Finish S p p p p p p p p p p p p 11 6 9 4 9 11 7 8 6 5 10 3 4 1 2 1 p p
2.4. Jarvis’ March A “package/gift wrapping” technique O(n· h) h is the no. of vertices in the hull (which might be n)
The Operation of Jarvis’ March set p = the leftmost point Repeat until p is again the leftmost point The next point q is chosen such that the triplet (p, q, r) is counterclockwise for any other point r. Store q in the output convex hull set p = q
Code see ConvexHullTexts.java public static ArrayList<Pt> jarvisMarch(Pt[] pointsArr) { // pointsArr[numPts-1] is different from pointsArr[0] ArrayList<Pt> pts = new ArrayList<Pt>(); // copy just in case for (int i=0; i < pointsArr.length; i++) pts.add(pointsArr[i]); ArrayList<Pt> hull = new ArrayList<Pt>(); Pt leftPt = getLeftMost(pointsArr); : Contest Algorithms:15. CG Topics
// Start from leftmost point, keep moving ccw (left) // until reach the start point again. Pt next = null; Pt prev = leftPt; do { // O(h) next = pts.get(0); // compare next with all other pts for(int i = 1; i < pts.size(); i++) { Pt cand = pts.get(i); if ((Pt.ccw(prev, cand, next) > 0) || (Pt.ccw(prev, cand, next) == 0 && prev.dist(cand) > prev.dist(next)) ) { next = cand; // cand is less left than next, so update next } hull.add(next); pts.remove(next); prev = next; } while(next != leftPt); return hull; } // end of jarvisMarch() Contest Algorithms:15. CG Topics
Execution > java ConvexHullTests hull3.txt Javis March convex hull: (1.000, 1.000) (3.000, 0.000) (5.000, 3.000) (3.000, 5.000) (0.000, 3.000) same counter-clockwise convex hull as Graham Scan, but ending at the left-most point. Contest Algorithms:15. CG Topics
Using Polygon.convexHull() see ConvexHullTexts.java Polygon p = new Polygon(points); Polygon hullPoly = p.convexHull(); System.out.println("Polygon class' convex hull: " + hullPoly); Contest Algorithms:15. CG Topics
Graham Scan is faster than Jarvis March Polygon.convexHull() is a slightly different implementation of the Graham Scan algorithm instead of using a stack, it employs ArrayList the lowest point is stored first in the hull, not last Graham Scan is faster than Jarvis March O(n log n) compared to O(n h) If the points are already sorted by one of the coordinates or by the angle to a fixed vector, then the algorithm takes O(n) time Contest Algorithms:15. CG Topics
3. Finding the Closest Pair of Points There are a set of n points P = { p1,…pn }. Find a pair of points p, q such that |p – q| is the minimum of all |pi – pj| Easy to do in O(n2) time for all pi ≠ pj, compute ║pi - pj║ on all the pairs and choose the minimum, which involves n(n-1)/2 comparisons We will aim for O(n log n) time
Code see ClosestPair.java public static int[] bfClosestPair(Pt[] pts) // brute-force version of the search = O(n^2) { int numPts = pts.length; if (numPts < 2) return null; int[] pair = {0, 1}; // store indicies of pts double minDist = pts[0].dist(pts[1]); if (numPts > 2) { for (int i = 0; i < numPts-1; i++) { for (int j = i+1; j < numPts; j++) { double dist = pts[i].dist(pts[j]); if (dist < minDist) { pair[0] = i; pair[1] = j; minDist = dist; } return pair; } // end of bfClosestPair() see ClosestPair.java Contest Algorithms:15. CG Topics
Usage public static void main(String[] args) throws Exception { Scanner sc = new Scanner(new File("pointsData.txt")); int numPts = sc.nextInt(); Pt[] pts = new Pt[numPts]; for (int i = 0; i < numPts; i++) pts[i] = new Pt(sc.nextInt(), sc.nextInt()); : // divide and conquer code, see later int[] pair = bfClosestPair(pts); Pt pt0 = pts[pair[0]]; Pt pt1 = pts[pair[1]]; System.out.println(" " + pt0 + " -- " + pt1 + ": " + pt0.dist(pt1)); } // end of main() Contest Algorithms:15. CG Topics
Input Data pointsData.txt 10 5 9 9 3 2 0 8 4 7 4 9 10 1 9 8 2 0 10 9 6 > java ClosestPair pointsData.txt (7.000, 4.000) -- (8.000, 4.000): 1.0 Contest Algorithms:15. CG Topics
Divide and Conquer Divide: Compute the median of the x-coordinates Split the points into a left half PL and right half PR, each of size n/2 Conquer: compute the closest pairs for PL and PR Combine the results (the hard part) median line PL PR
public class ClosestPair { // closest pair of pts and their Euclidean distance private Pt best1, best2; private double bestDistance = Double.POSITIVE_INFINITY; public ClosestPair(Pt[] pts) int numPts = pts.length; if (numPts < 2) return; // sort by x-coordinate (breaking ties by y-coordinate) Pt[] ptsX = new Pt[numPts]; for (int i = 0; i < numPts; i++) ptsX[i] = pts[i]; Arrays.sort(ptsX, new XOrder()); : see ClosestPair.java Contest Algorithms: 4. Backtracking
// check for coincident pts for (int i = 0; i < numPts - 1; i++) { if (ptsX[i].equals(ptsX[i + 1])) { bestDistance = 0.0; best1 = ptsX[i]; best2 = ptsX[i + 1]; return; } // sort by y-coordinate (but not yet sorted) Pt[] ptsY = new Pt[numPts]; for (int i = 0; i < numPts; i++) ptsY[i] = ptsX[i]; Pt[] aux = new Pt[numPts]; // auxiliary (temp) array closest(ptsX, ptsY, aux, 0, numPts - 1); } // end of ClosestPair() Contest Algorithms:15. CG Topics
divide conquer combine private double closest(Pt[] ptsX, Pt[] ptsY, Pt[] aux, int lo, int hi) // find closest pair of pts in ptsX[lo..hi] { if (hi <= lo) return Double.POSITIVE_INFINITY; int mid = lo + (hi - lo) / 2; Pt median = ptsX[mid]; // compute closest pair with both endpoints in left subarray // or both in right subarray double delta1 = closest(ptsX, ptsY, aux, lo, mid); double delta2 = closest(ptsX, ptsY, aux, mid + 1, hi); double delta = Math.min(delta1, delta2); // merge back so that ptsY[lo..hi] are sorted by y-coordinate ClosestPair.merge(ptsY, aux, lo, mid, hi); // v.similar to merge in mergeSort : // more in a few slides divide conquer combine Contest Algorithms:15. CG Topics
Check y-strip? median line Apart from the left and right halves of the space, we must also check pairs that cross the median line Only interested in pairs within distance < d of each other It's enough to look at only the points in the 2d wide strip running up the median line less than d? PL PR
Scanning the Strip median line d d Sort all points in the strip by their increasing y-coordinate values Examine each point moving up the strip. Examine a point by considering the d x 2d volume above it only points inside this volume need to be considered there can only be a maximum of 7 points d
How many points in the rectangle? Imagine that the rectangle is divided into eight d/2 x d/2 squares Aside from the point we are looking at, their can only be a maximum of one point in each square This means our code need only look at a maximum of 7 points above the current point d/2 d/2 d/2 d/2 Contest Algorithms:15. CG Topics
closest() Continued… This code does not have // aux[0..M-1] = sequence of pts closer than delta, sorted by y-coordinate int M = 0; for (int i = lo; i <= hi; i++) { if (Math.abs(ptsY[i].x - median.x) < delta) aux[M++] = ptsY[i]; } // compare each point to its neighbors with y-coordinate closer than delta for (int i = 0; i < M; i++) { // a geometric packing argument shows that this loop iterates at most 7 times for (int j = i + 1; (j < M) && (aux[j].y - aux[i].y < delta); j++) { double distance = aux[i].dist(aux[j]); if (distance < delta) { delta = distance; if (distance < bestDistance) { bestDistance = delta; best1 = aux[i]; best2 = aux[j]; return delta; This code does not have running time O(n2) but O(n∙7) == O(n) Contest Algorithms:15. CG Topics
Running time T(n) = 2* T(n/2) + O(n) the two recursive calls to closest() the examination of the strip T(n) = 2* T(n/2) + O(n) the same as Mergesort, which is O(n log n) Contest Algorithms:15. CG Topics
Other methods in ClosestPair class // globals in ClosestPair class private Pt best1, best2; private double bestDistance = Double.POSITIVE_INFINITY; public Pt either() { return best1; } public Pt other() { return best2; } public double distance() { return bestDistance; } Contest Algorithms:15. CG Topics
Usage see ClosestPair.java ClosestPair closest = new ClosestPair(pts); System.out.println(" " + closest.either() + " -- " + closest.other() + ": " + closest.distance()); Contest Algorithms:15. CG Topics