Lab 08 — Advanced Geometry

Goal

Implement two foundational computational geometry primitives:

  1. Convex Hull via Andrew’s monotone chain (O(N log N))
  2. Segment Intersection (Bentley-Ottmann sweep-line, O((N + K) log N))

Background

Computational geometry interviews are rare but exist at:

  • Companies doing CAD, CAM, 3D modeling (Autodesk, Adobe)
  • Games (Unity, Epic) — physics, collision
  • Robotics (path planning, occupancy grids)
  • Maps/GIS (Google Maps, ESRI)
  • Some quant for time-series geometric techniques

The implementation has many sharp edges: floating-point comparison, collinear points, degenerate cases. Robust geometry is a deep subfield.

Interview Context

  • Codeforces / ICPC: geometry round often included
  • Game / CAD / robotics roles: foundational
  • Standard FAANG: almost never

When to Skip This Topic

Skip if any of these are true:

  • You’re not targeting the specific industries above
  • You’re uncomfortable with vector/cross product math
  • You don’t have 2+ weeks to handle the edge cases properly

The first implementation of convex hull will have bugs. Plan for several practice attempts.

Problem Statement

Part A — Convex Hull

Given N points in 2D, return the vertices of their convex hull in counterclockwise order, starting from the lowest-leftmost point.

Part B — Count Segment Intersections

Given N line segments, return the number of intersection points among them.

Constraints

  • A: 1 ≤ N ≤ 10^5. Coordinates fit in int32.
  • B: 1 ≤ N ≤ 10^5. Up to K = O(N²) intersections in pathological cases; for the algorithm to be efficient, K << N².

Clarifying Questions

A:

  • Should collinear hull points be included or skipped?
  • Are duplicates possible?

B:

  • Should overlapping segments count as one intersection or many?
  • Touching at endpoints?

Examples

A

Points: [(0,0), (1,1), (2,0), (1,-1)]
Hull: [(0,0), (2,0), (1,1)] (counterclockwise; (1,-1) is below)
Wait — (1,-1) is also on the hull. Correct hull: [(0,0), (1,-1), (2,0), (1,1)].

B

Segments: [((0,0),(4,4)), ((0,4),(4,0))]
Intersect at (2,2). Answer: 1.

Brute Force

A: gift-wrapping (Jarvis march) — O(N · H) where H = hull size. Worst case O(N²). B: check every pair — O(N²).

Brute Force Complexity

  • A: O(NH) worst-case O(N²)
  • B: O(N²)

For N = 10^5: 10^10 — TLE.

Optimization Path

A — Andrew’s Monotone Chain

  1. Sort points lexicographically by (x, y).
  2. Build upper hull: iterate left to right; pop top of hull while it makes a non-right turn.
  3. Build lower hull: iterate right to left; pop while non-right turn.
  4. Concatenate (excluding shared endpoints).
def cross(O, A, B):
    return (A[0]-O[0]) * (B[1]-O[1]) - (A[1]-O[1]) * (B[0]-O[0])

def convex_hull(points):
    points = sorted(set(points))
    if len(points) <= 1: return points
    lower = []
    for p in points:
        while len(lower) >= 2 and cross(lower[-2], lower[-1], p) <= 0:
            lower.pop()
        lower.append(p)
    upper = []
    for p in reversed(points):
        while len(upper) >= 2 and cross(upper[-2], upper[-1], p) <= 0:
            upper.pop()
        upper.append(p)
    return lower[:-1] + upper[:-1]

B — Bentley-Ottmann Sweep Line

  1. Event queue: segment endpoints + computed intersections, ordered by x.
  2. Status: balanced BST of active segments, ordered by y at the sweep line.
  3. At a “left endpoint” event: insert into status; check intersection with neighbors above/below.
  4. At a “right endpoint” event: remove from status; check intersection between the new neighbors.
  5. At an “intersection” event: swap the two segments in status; check intersections with new neighbors.

O((N + K) log N) where K = number of intersections.

Final Expected Approach

Convex hull: as above.

Bentley-Ottmann: full implementation is 200+ lines with all edge cases. For most interviews, even discussing the structure is enough — full code is unlikely to be required.

Data Structures

  • A: sorted list, stack-like list for hull construction
  • B: priority queue (event queue), balanced BST or sorted list with O(log N) ops (status)

Correctness Argument

A

  • Sorting by x (then y) ensures we visit points in a consistent order.
  • “Right turn” (cross product > 0) means we’re making a convex angle; popping ensures we never include a concave point.
  • Lower and upper hulls cover all hull vertices; concatenation gives full hull in CCW order.

B

  • Two segments can only intersect when they are adjacent in the y-order at some x.
  • The sweep maintains adjacency; new adjacencies arise at endpoints and intersections.
  • Each intersection is detected exactly once (at the moment the segments become adjacent).

Complexity

  • A: O(N log N) for sort + O(N) for chain construction.
  • B: O((N + K) log N).

Implementation Requirements

  • Use integer arithmetic for cross product when possible (avoids floating-point errors)
  • Handle collinear hull points consistently (include or exclude as required)
  • For segment intersection: distinguish proper intersection (interior) from touching (endpoint)
  • Watch for vertical segments (event ordering edge case)

Tests

A

  • Single point → [point]
  • Two points → [both]
  • Three collinear points → [endpoints only] (or all three, depending on convention)
  • Square (4 corners + 5 interior) → 4 corners
  • All points on a circle → all on hull
  • Many duplicates

B

  • No intersections (parallel lines)
  • All intersect at one point (n lines through origin)
  • Random N = 100 vs O(N²) brute force

Follow-up Questions

A:

  • Convex hull in 3D. → Quickhull or gift wrapping in 3D; O(N²) worst.
  • Dynamic convex hull (online insertions). → Overmars-van Leeuwen; O(log² N) per update.
  • Compute area of hull. → Shoelace formula.
  • Diameter of hull (farthest pair). → Rotating calipers, O(N).

B:

  • Report intersections, not just count. → Same structure; collect points.
  • Line-line intersection in projective coords. → Avoids special-casing parallels.
  • Segments may overlap. → More complex event handling.
  • Robust orientation predicate. → Adaptive precision; Shewchuk’s predicates.

Product Extension

  • Mapping: route simplification (Ramer-Douglas-Peucker)
  • Games: collision detection (broad phase uses sweep)
  • CAD: boolean operations on polygons (sweep-based)
  • Robotics: configuration space construction

Language/Runtime Follow-ups

  • C++: geometry libraries (CGAL) are massive and correct but complex.
  • Python: Shapely for production; for interview, implement primitives.
  • Java: java.awt.geom.* has some primitives.

Common Bugs

  1. Floating-point comparison without epsilon: false negatives on coincident points.
  2. cross < 0 vs cross <= 0: different hull conventions (collinear in or out).
  3. Forgot to dedupe input points before convex hull.
  4. Sweep-line: vertical segments treated incorrectly. Add small perturbation or special-case.
  5. Sweep-line: intersection on the sweep line not detected because status ordering is computed at the wrong x.

Debugging Strategy

  • Plot the points and hull (matplotlib or similar)
  • For small cases, enumerate hull by hand and verify
  • For sweep-line: log every event and the status before/after

Mastery Criteria

  • Implement Andrew’s monotone chain in ≤ 30 min from memory
  • Implement basic segment-intersection check in ≤ 15 min
  • Understand the Bentley-Ottmann structure (even without writing 200 lines)
  • Apply rotating calipers for hull diameter
  • Recognize when integer arithmetic suffices vs when floating-point is unavoidable
  • State complexity precisely