The crossing count of a polygon, and its parity #
This is the arithmetic half of the polygonal Jordan curve theorem. A closed polygon is a list of edges; a point off it gets a number, the count of edges the ray leaving the point meets; and the whole separation statement follows from the fact that this number changes by one exactly when the ray's foot sweeps across an edge.
The frame #
The blueprint rotates the coordinate system so that no edge is horizontal. Rotating a curve
is a change of the curve, which would have to be transported back at the end, so instead the
ray direction is carried as a parameter. A direction u (a unit vector,
Plane.IsDirection) fixes an orthonormal frame through
fwd u z— how far alonguthe pointzlies,hgt u z— how far acrossuit lies, positive on the counterclockwise side.
"Horizontal" becomes "level for u", i.e. hgt u a = hgt u b, and
exists_direction_hgt_ne — a one-line consequence of Plane.exists_isDirection_det_ne_zero
— supplies a u for which no edge of a finite list is level. Nothing below needs the
direction to be produced that way: hgt u P.1 ≠ hgt u P.2 for every piece is carried as a
hypothesis, so no curve is ever moved and nothing has to be transported back.
The count #
An edge is a Piece, i.e. its pair of ends, and a polygon is a List Piece — the same
representation the overlay uses, so a subdivision produced by Schoenflies.subdivide can be
fed straight in. Crosses u P q says that the level line through q meets P strictly
beyond q, with the blueprint's half-open convention at the lower end, which is what makes
the count well defined with no genericity assumption. crossings counts and parity
reduces mod 2.
Closedness of the polygon enters through IsClosedChain: the boundary of the edge list
vanishes mod 2, stated by duality as "every ZMod 2-valued function of the ends sums to
zero over the list". edgesOf builds a closed chain from a cyclic vertex list, and
IsClosedChain survives subdivide.
Blueprint #
crossings,parity— the countπ_Cof the section "The polygonal Jordan and crosscut theorems".exists_direction_hgt_ne— the setup of that section: a ray direction level for no edge.crossings_subdivide,parity_subdivide— Lemma 2.1 (subdivision invariance). Proved for the count, not only its parity, and for the wholesubdivideoperation ofSchoenflies.Subdivide, not only for a single cut.parity_eq_of_mem_ball,parity_eq_of_isPreconnected,parity_eq_of_mem_connectedComponentIn— the first half of Lemma 2.2 (crossing parity):π_Cis locally constant off the polygon, hence constant on each component of the complement.parity_flip— the second half of Lemma 4.4: the two local sides of an edge have opposite parity.parity_eq_zero_of_lt—π_C = 0beyond the polygon in the ray direction; the value on the unbounded region.IsClosedChain,edgesOf,isClosedChain_subdivide— the closedness hypothesis, its supply from a cyclic vertex list, and its survival of subdivision.
The frame attached to a direction #
How far across the direction u the point z lies. Edges on which this is constant are
the blueprint's horizontal edges.
Equations
- Schoenflies.hgt u z = u.det z
Instances For
Where an edge meets a level line #
The point of the line through a and b at height t. Only used when the line is not
level, i.e. hgt u a ≠ hgt u b.
Equations
- Schoenflies.meet u a b t = a + ((t - Schoenflies.hgt u a) / (Schoenflies.hgt u b - Schoenflies.hgt u a)) • (b - a)
Instances For
The crossing count #
The edge P meets the level line through q strictly beyond q.
The height test is half-open at the bottom: an edge whose lower end is exactly at the height
of q counts, one whose upper end is counts not. That convention is what makes the count
well defined at every q off the polygon, with no genericity assumption.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Crossing is decided classically; the count is a noncomputable definition.
Equations
- Schoenflies.decidableCrosses u P q = Classical.dec (Schoenflies.Crosses u P q)
How many edges of L the ray from q crosses.
Equations
- Schoenflies.crossings u L q = (List.map (fun (P : Schoenflies.Piece) => if Schoenflies.Crosses u P q then 1 else 0) L).sum
Instances For
The contribution of one edge to the parity.
Equations
- Schoenflies.mark u P q = if Schoenflies.Crosses u P q then 1 else 0
Instances For
The crossing parity π_C(q).
Equations
- Schoenflies.parity u L q = (List.map (fun (P : Schoenflies.Piece) => Schoenflies.mark u P q) L).sum
Instances For
Subdivision invariance #
Closed chains #
The edge list is a closed chain: its boundary vanishes mod 2. Stated by duality —
every ZMod 2-valued function of the ends sums to zero over the list — which is exactly what
the parity argument consumes, and which isClosedChain_edgesOf supplies for a cyclic vertex
list.
Equations
- Schoenflies.IsClosedChain L = ∀ (f : Schoenflies.Plane → ZMod 2), (List.map (fun (P : Schoenflies.Piece) => f P.1 + f P.2) L).sum = 0
Instances For
The closed edge list of a cyclic vertex list: [v₀v₁, v₁v₂, …, v_{n-1}v₀].
Equations
- Schoenflies.edgesOf vs = vs.zip (vs.rotate 1)
Instances For
Cutting an edge in two does not disturb the boundary: the cut point is added twice.
What a list of edges occupies #
Moving the base point #
A connected piece of an edge that stays between two heights and never meets the level
line fwd = ξ stays on one side of it.
Moving the base point along the ray direction, without meeting the polygon, changes no crossing.
Sweeping across the ray direction #
The marker attached to a vertex when the base point is swept across by s: the vertex
counts when its height lies in the half-open band swept out and it lies beyond the base
point. Summed over both ends of every edge this vanishes, because the polygon is closed;
that is the whole content of crossing parity.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Sweeping the base point across the ray direction, without meeting the polygon, does not change the parity. This is where the polygon has to be closed.
Crossing parity #
The parity is constant on a ball that misses the polygon: reach any nearby point by a move along the ray direction followed by a move across it.
Crossing parity (Lemma 2.2). π_C is constant on every preconnected subset of the
complement of the polygon — in particular on every component.
The parity is constant on the component of the complement containing a point.
The value far along the ray, and the jump across an edge #
Opposite sides of an edge (Lemma 2.2, second half). At an interior point p of one
edge of the list which no other edge of the list passes through, the two points just before
and just after p in the ray direction lie off the polygon and have opposite parity: the
count changes by one as the base point sweeps across the edge.
Choosing the ray direction #
The blueprint's opening move — "rotate so that no edge is horizontal" — without rotating anything: finitely many edge directions rule out only finitely many rays, so some direction is level for none of them.