Winding Numbers and Point-in-Polygon
The Problem
Given a polygon (assumed planar, closed, and simple unless stated otherwise) and a point , determine whether is inside or outside .
Two main approaches exist: ray casting and winding numbers. For simple polygons they produce identical results. For complex (self-intersecting) polygons, they may differ, and the winding number provides a more useful definition.
Ray Casting (Crossing Number)
Shoot a ray (semi-line) from in any direction. Count the number of intersections of the ray with polygon edges:
- Odd count: is inside
- Even count: is outside
Why It Works
The polygon divides the plane into inside and outside regions. Every time the ray crosses an edge, it moves from inside to outside (or vice versa). Since the ray starts at and extends to infinity (which is outside), the parity of crossings determines the initial region.
Awkward Cases
The ray may pass through a vertex or be collinear with an edge. Solutions:
- Shoot a horizontal ray (avoids floating-point ambiguity with vertical comparisons).
- If the ray hits a vertex, check the neighbouring vertices:
- If both neighbours are on the same side of the ray (both above or both below), the ray grazed the vertex — no crossing.
- If neighbours are on opposite sides, the edge was crossed.
- If a neighbour is also on the ray, skip it and check the next vertex in the same direction around the perimeter.
Alternatively, pick a random ray direction; with probability 1 it will miss all vertices (assuming finite precision is not an obstacle).
Complexity
For vertices: time — each edge is tested once.
Winding Number
The winding number of with respect to polygon is the number of times the polygon “winds around” . Formally:
where is the signed angle subtended by edge at point .
- : is outside
- : is inside (for simple polygons)
- : is inside a self-intersecting polygon, wound around multiple times
Implementation Without Trigonometry
Computing actual angles via arctangent is slow and suffers floating-point error. Instead, use cross products and dot products to compute the signed contribution of each edge.
For each edge , the signed angle contribution can be determined from:
The cross product gives the sine of the angle (signed); the dot product gives the cosine. The quadrant of the angle determines the exact contribution to the winding number without needing .
Intuition
Imagine a string attached to a post at point . Walk around the polygon perimeter holding the string. When you return to the start, count how many times the string is wrapped around the post. Odd wraps = inside, even = outside.
Comparison
| Method | Simple Polygons | Complex Polygons | Time | Trig? |
|---|---|---|---|---|
| Ray casting | Ambiguous | No | ||
| Winding number | Well-defined | Can avoid |
Worked Example: Ray Casting
Polygon: square with vertices . Test point with a horizontal ray to the right.
Edges checked:
- : , ray at , no intersection.
- : vertical edge at , ray from hits at . Intersection! Count = 1.
- : , no intersection.
- : vertical edge at , but ray goes right from , so no intersection.
Count = 1 (odd) → inside. Correct.
Summary
| Concept | Detail |
|---|---|
| Ray casting | Count edge crossings; odd = inside |
| Winding number | Sum signed angles / ; 0 = outside |
| Horizontal ray | Simplest to implement |
| Vertex handling | Check neighbours: same side = grazing (no count) |
| Complexity | for both methods |
| No trigonometry | Use cross products and dot products for winding number |