Computational Geometry/Geometric Primitives & Orientation

Lesson 11.11,495 words

Geometric Primitives & Orientation

Computational geometry is built on a single reliable primitive — the orientation test, a sign of a cross product that tells whether three points turn left, right, or lie collinear. From points-as-vectors and the dot and cross products we derive orientation, segment intersection, the shoelace area formula, and point-in-polygon tests, keeping all arithmetic exact and integer so that no floating-point rounding can corrupt a sign.

╌╌╌╌

This lesson opens the computational geometry module, where the objects are points, segments, and polygons in the plane rather than numbers or graphs. The algorithms ahead, including convex hulls, sweep-line intersection, and closest pairs, look involved, but they nearly all rest on one tiny operation asked over and over: given three points, does the path through them turn left or right? Get that primitive exactly right and the rest is bookkeeping; get it subtly wrong and every structure built on top inherits the error.

The central design decision of this lesson is therefore exact arithmetic. The natural geometric quantities (angles, lengths, slopes) are irrational and force floating point, where a quantity that should be zero comes out as and a collinearity test flips the wrong way. We avoid them. With integer input coordinates, the orientation and area primitives below are polynomials in the coordinates, so they evaluate to exact integers and their signs are never in doubt.1 Slopes, square roots, and never appear.

Points as vectors

We identify a point with the vector from the origin to it, which lets us do arithmetic on geometry. For points and and a scalar :

The subtraction is the one that does the work: it is the displacement vector pointing from to , and almost every primitive below is phrased in terms of such difference vectors anchored at a common point. If , have integer coordinates, so does , and exactness is preserved by every one of these operations.

The dot product: angle and projection

The dot product of two vectors measures how much they point the same way:

where is the angle between them. The second equality is the useful one in reverse: because , the sign of the dot product is the sign of , so it classifies the angle without ever computing it.

Three uses recur. Projection: the scalar projection of onto is , the signed length of 's shadow along . Perpendicularity: , an exact integer test. Angle: when the actual angle is genuinely needed (the one place a square root sneaks in). Note the dot product is symmetric and tells us nothing about which side one vector lies on; for that we need its companion.

The dot product is the signed length of 's shadow on times

The cross product: signed area and orientation

In the plane the cross product of two vectors is a single scalar:

Its magnitude equals the area of the parallelogram spanned by and (and twice the area of the triangle they form). Its sign is the sign of , which encodes orientation: positive when lies counterclockwise from , negative when clockwise, zero when the two are parallel (collinear). Unlike the dot product, the cross product is antisymmetric: . This single quantity, an exact integer for integer inputs, is the seed of nearly everything that follows.

The orientation test

Anchor two difference vectors at a common point and take their cross product. This is the orientation (or ccw) test, the fundamental primitive of planar computational geometry:

It reports the sense of the turn made by the directed path :

is the turn direction at

Three properties make this primitive so powerful. It uses only additions and multiplications of the input coordinates, so for integer inputs it is exact. It is antisymmetric in a way that respects the geometry: swapping and flips the sign, matching the reversal of the turn. And it answers, in , the question every higher geometric algorithm reduces to — which side of the line does lie on? The collinearity test are , , on one line? is just , with no division by a slope and hence no vertical-line special case.

Algorithm:Orientation(A,B,C)\textsc{Orientation}(A, B, C) — sign of the turn, exact integer arithmetic
  1. 1
    d(bxax)(cyay)(byay)(cxax)d \gets (b_x - a_x)(c_y - a_y) - (b_y - a_y)(c_x - a_x)
  2. 2
    if d>0d > 0 then
  3. 3
    return +1+1
    left turn
  4. 4
    else if d<0d < 0 then
  5. 5
    return 1-1
    right turn
  6. 6
    else
  7. 7
    return 00
    collinear

Segment intersection

When do two segments and cross? The slope-and-solve approach drags in division and degenerate cases; orientation makes it a handful of sign comparisons. The key idea is straddling: segment straddles the line through and when its endpoints fall on opposite sides of that line, that is, when and have opposite signs.

Why. If and are strictly on opposite sides of line , the open segment crosses that line exactly once, at some point ; the symmetric condition forces to also lie strictly between and . Both products being negative pins the crossing to the interior of both segments. The two ccw evaluations are exact integers, so this test has no rounding error and never computes the intersection point itself.

The boundary, when some ccw is and three points are collinear, needs care, and is where naive implementations break. The case logic:

  • All four products nonzero (the generic case): intersect iff both products are strictly negative, as above.
  • Exactly one is , say : then lies on the line ; the segments touch iff lies on the segment, i.e. 's coordinates are within the bounding box of and (an on-segment check: and likewise for ).
  • The segments are collinear (all four ccw vanish): they overlap iff their -D projections onto the -axis (or , if vertical) overlap, again a bounding-box / interval-overlap test, no geometry beyond comparisons.
Algorithm:SegmentsIntersect(A,B,C,D)\textsc{SegmentsIntersect}(A,B,C,D) — proper crossings plus collinear/touching
  1. 1
    d1Orientation(C,D,A);  d2Orientation(C,D,B)d_1 \gets \textsc{Orientation}(C,D,A);\ \ d_2 \gets \textsc{Orientation}(C,D,B)
  2. 2
    d3Orientation(A,B,C);  d4Orientation(A,B,D)d_3 \gets \textsc{Orientation}(A,B,C);\ \ d_4 \gets \textsc{Orientation}(A,B,D)
  3. 3
    if d1d2<0d_1 d_2 < 0 and d3d4<0d_3 d_4 < 0 then
  4. 4
    return true
    proper crossing
  5. 5
    if d1=0d_1 = 0 and OnSegment(C,D,A)\textsc{OnSegment}(C,D,A) then return true
  6. 6
    if d2=0d_2 = 0 and OnSegment(C,D,B)\textsc{OnSegment}(C,D,B) then return true
  7. 7
    if d3=0d_3 = 0 and OnSegment(A,B,C)\textsc{OnSegment}(A,B,C) then return true
  8. 8
    if d4=0d_4 = 0 and OnSegment(A,B,D)\textsc{OnSegment}(A,B,D) then return true
  9. 9
    return false
cross iff each straddles the other

Polygon area: the shoelace formula

Given a polygon as an ordered list of vertices (indices modulo ), its area is the shoelace formula:

Each term is exactly the cross product , so the area is a sum of cross products, halved.

Drop the absolute value and the sign of the sum carries orientation: positive means the vertices are listed counterclockwise, negative means clockwise. This is the cheapest way to detect the winding direction of a polygon, and because the sum is an integer for integer vertices, the area comes out as an exact half-integer.2

Shoelace sums signed triangles from ; exterior sweeps cancel

Point in polygon

Is a query point inside a polygon? Two exact strategies, both built from the primitives above. Ray casting shoots a ray from in a fixed direction (say ) and counts how many polygon edges it crosses: an odd count means is inside, even means outside — the Jordan-curve parity argument. Each ray-vs-edge crossing is decided with the same straddle/orientation tests, with careful tie-breaking when the ray grazes a vertex (count an edge only if exactly one endpoint is strictly above the ray). The winding number alternative sums the signed angles the polygon's edges subtend at (computed from cross- and dot-product signs, not actual angles); a total winding of means outside, means inside, and unlike parity it stays correct for self-intersecting polygons. For a convex polygon both can be sped up to : binary-search the vertex fan around to find the wedge containing using orientation tests, then one final ccw against the bounding edge decides inside vs. outside.3

Ray casting: a ray crossing an odd count of edges means inside

For a convex polygon the fan of diagonals from cuts the interior into triangular wedges in angular order, so a single binary search on locates the wedge that could contain ; one last orientation test against the edge decides inside vs. outside.

Convex point location: binary-search the fan from for 's wedge, then one on the far edge

Takeaways

  • Treat points as vectors; the difference is the displacement that almost every primitive is built from, and integer inputs stay integer.
  • The dot product has the sign of : positive/zero/negative acute/right/obtuse — it handles projection, perpendicularity, and angle.
  • The cross product gives signed parallelogram area in its magnitude and orientation in its sign.
  • The orientation test reports left/right/collinear in exact integer arithmetic — the primitive that hull, intersection, and point-location all reduce to.
  • Segments cross iff each straddles the other's line (opposite ccw signs on both); collinear and touching cases fall to on-segment bounding-box checks.
  • The shoelace formula is a sum of cross products giving area, and its sign reveals CCW vs. CW winding.
  • Point-in-polygon is ray-casting parity or winding number; a convex polygon admits an orientation-based binary search.

Footnotes

  1. CLRS, Ch. 33 — Computational Geometry (§33.1): cross-product primitives, the orientation/turn test, and segment-intersection via straddling, all in exact arithmetic to avoid round-off.
  2. Skiena, § — Computational Geometry: the shoelace (surveyor's) formula for polygon area as a sum of cross products, whose sign gives vertex orientation.
  3. Erickson, Ch. — (geometry): point-in-polygon by ray-crossing parity and winding number, and the convex case via orientation-guided binary search.
Practice

╌╌ END ╌╌