Two-sided polygonal strips #
This module builds the collar of Lemma 1.8: an open neighbourhood N of a simple closed
polygon P whose complement in N splits into two connected open sets, the two local sides.
The blueprint's construction is "a small closed disk about every vertex, a thin rectangular block about every edge, chosen so that consecutive blocks overlap and nonadjacent closures are disjoint". The blocks here are
Plane.cone v A R— the open sector of radiusRabout a vertexvcut out by an arcAof directions, andPlane.strip a u t₁ t₂ s₁ s₂— the open rectangle around a directed edge, written in the edge's own coordinatest = ⟪u, x - a⟫(along) ands = det u (x - a)(across).
Everything about the side matching at a vertex comes from Schoenflies/Direction.lean
through the orientation form; no angle is named. The one new direction fact needed here is the
sign-free repackaging Plane.germs_split': the blueprint's germs_split assumes
0 < det r₁ r₂, but a polygon turns both ways, and it turns out that which named arc carries
the left germs does not depend on the sense of the turn — it is always arcCCW r₂ r₁. Only the
side of the smallness hypothesis moves.
The constants #
The blueprint says "the blocks may be chosen so that consecutive edge and vertex blocks overlap
in the prescribed small rectangles around the radial segments, while the closures of all
nonadjacent blocks are disjoint" without naming the constants. Schoenflies.StripData names
them: a cone radius R, a trim lam by which each edge block stops short of its endpoints, and
a half-width rho. They should be chosen in this order (this is the recipe the unproved
exists_stripData has to follow):
Rfrom the vertex separations, from the distance of each vertex to the nonincident edges (with a factor2of slack, spent indist_core_vertex), and from the prescribed open set;lam := R / 5, which makes2 * lam < R(the cones reach past the ends of the blocks they must overlap) and4 * lam < ‖edge‖(the blocks are nonempty);rhofrom the distance of each trimmed edge to the other edges, and from the germ thresholdrho * (1 + |⟪r₁, r₂⟫|) ≤ lam * |det r₁ r₂|at every vertex — this last is the quantitative form of "make the strips narrow enough that this side matching holds in every vertex disk", and it is the only inequality in the list that is not a separation of compact sets.
Blueprint #
Plane.germs_split'— the sign-free vertex matching inside Lemma 1.8.Schoenflies.ClosedPolygon— a simple closed polygonal curve presented by its cyclic vertex list.Schoenflies.StripData— the choice of constants, as a hypothesis.Schoenflies.StripData.sideL,.sideR,.nbhd,.sideL_disjoint_sideR— the two labelled sides of the collar, and that they are disjoint.
Where the rest of Lemma 1.8 lives #
This module builds the apparatus and proves the hard half: the germ matching at a corner and the disjointness of the two labelled sides. The other two obligations are discharged next door, and were split out only because they were built concurrently:
Schoenflies/StripConstants.lean—exists_stripDataandexists_stripData_subset, which produce the constants, the latter with the collar inside a prescribed open set.Schoenflies/StripConnected.lean— that each side is connected, andnbhd \ carrier = sideL ∪ sideR.
Schoenflies.polygonal_collar in Schoenflies/Compose.lean is the three composed into the
blueprint's Lemma 1.8 (a). ClosedPolygon.collar below is a definition, not the theorem.
What is still missing from Lemma 1.8 as a whole: the local two-sidedness clause (every sufficiently small disk about a point of the curve meets the complement in exactly two components, one in each side), and part (b), the arc case.
A sign-free vertex matching #
Plane.germs_split fixes the orientation with 0 < det r₁ r₂. A polygon turns both ways, so
the collar needs the statement without that hypothesis. The content of the repackaging is that
the conclusion does not move: the two left germs always land on arcCCW r₂ r₁ and the two
right germs on arcCCW r₁ r₂. What moves is which pair needs the smallness hypothesis, so the
sign-free form simply imposes it on both, with absolute values.
The vertex matching, sign-free. With r₁ the ray back along the incoming edge and r₂
the ray out along the outgoing edge, the two left half-strip germs lie on arcCCW r₂ r₁ and
the two right ones on arcCCW r₁ r₂, whichever way the polygon turns. The hypothesis
s * |⟪r₁, r₂⟫| < t * |det r₁ r₂| is the blueprint's "s/t small", written without a division
and without a sign.
The two arcs, without a sign hypothesis #
The two rays and the two arcs exhaust the nonzero vectors, whichever the sign of
det r₁ r₂.
Half-spaces through the origin are convex; det u · is linear.
An arc met with a ball about the origin is connected. When the arc is the short one it is an
intersection of two half-planes, hence convex; when it is the long one it is a union of two
half-planes, and -(u + w) lies in both.
Vertex sectors #
The "small closed disk about a vertex" of the blueprint is replaced by an open sector: the part of a ball about the vertex lying in a prescribed arc of directions. The two sectors cut out by the two arcs are exactly the two components of the ball minus the two incident radial segments, which is what makes the labelling at a vertex well defined.
A sector is the translate of an arc met with a ball at the origin, so it inherits its
connectedness from Plane.isConnected_arcCCW_ball.
Edge blocks #
The block around a directed edge is described in the edge's own frame: coordAlong is the
progress along the edge and coordAcross the signed distance to its line. Both are affine, so
the block is an intersection of four open half-planes — open and convex at a glance.
Progress along the directed edge that starts at a with unit tangent u.
Equations
- a.coordAlong u x = inner ℝ u (x - a)
Instances For
Signed distance from x to the line of the directed edge that starts at a with unit
tangent u; positive on the left.
Equations
- a.coordAcross u x = u.det (x - a)
Instances For
The frame is complete: every point is recovered from its two coordinates.
Both coordinates are 1-Lipschitz, which is how a small ball about a point of the edge
stays inside the block.
The open block around the directed edge from a with unit tangent u: the points whose
progress lies in (t₁, t₂) and whose signed distance lies in (s₁, s₂).
Equations
- a.strip u t₁ t₂ s₁ s₂ = {x : Schoenflies.Plane | t₁ < a.coordAlong u x ∧ a.coordAlong u x < t₂ ∧ s₁ < a.coordAcross u x ∧ a.coordAcross u x < s₂}
Instances For
A point of a block is at distance |coordAcross| from the foot of its perpendicular on the
edge line, which is the point of the edge with the same progress.
Simple closed polygons #
A polygon is presented by its cyclic vertex list. Indexing by ZMod (m + 3) builds in both the
cyclic successor and the requirement that there are at least three vertices, and it makes
NeZero available without a side hypothesis.
A simple closed polygonal curve, presented by its cyclic vertex list.
The vertices, in cyclic order.
- vertex_inj : Function.Injective self.vertex
The vertices are distinct.
- edges_meet (i j : ZMod (m + 3)) : i ≠ j → segment ℝ (self.vertex i) (self.vertex (i + 1)) ∩ segment ℝ (self.vertex j) (self.vertex (j + 1)) ⊆ {self.vertex i, self.vertex (i + 1)}
Simplicity: an edge meets any other edge only at one of its own endpoints.
- corner (i : ZMod (m + 3)) : (self.vertex (i - 1) - self.vertex i).det (self.vertex (i + 1) - self.vertex i) ≠ 0
No redundant vertex: the two edges at a vertex are not collinear. The blueprint deletes such vertices before starting, and the vertex matching needs
det ≠ 0there.
Instances For
Edges, in their own frame #
Every point is the point of some frame position, so the two coordinates identify it.
A point of the edge arriving at i is a nonnegative multiple of the incoming ray away from
the vertex; a point of the edge leaving i is a nonnegative multiple of the outgoing one. This
is what keeps the two incident edges out of both sectors at i.
The constants #
StripData is the blueprint's "choose the blocks so that consecutive ones overlap and
nonadjacent closures are disjoint", with every constant named. The three numbers are a cone
radius R, a trim lam and a half-width rho; the separation hypotheses are exactly the
instances of Lemma 1.4 (b) that the construction consumes, and germ is the vertex-matching
threshold. Producing them is the unproved exists_stripData; see the Status section.
The constants of the collar of a simple closed polygon.
- R : ℝ
The radius of the vertex sectors.
- lam : ℝ
The distance by which an edge block stops short of each endpoint of its edge.
- rho : ℝ
The half-width of an edge block.
The sectors reach past the ends of the blocks they have to overlap.
The blocks are nonempty, with room to spare at both ends.
A sector does not run past the far end of an incident edge.
Distinct vertices are
2Rapart, so distinct sectors are disjoint.- sep_trim_edge (i j : ZMod (m + 3)) : j ≠ i → ∀ c ∈ Set.Icc self.lam (P.len i - self.lam), ∀ y ∈ P.edge j, 2 * self.rho ≤ dist (P.pt i c) y
The trimmed edge
iis2 * rhoaway from every other edge. - germ (i : ZMod (m + 3)) : self.rho * (1 + |inner ℝ (P.rayIn i) (P.tang i)|) ≤ self.lam * |(P.rayIn i).det (P.tang i)|
The vertex-matching threshold. This is the only hypothesis that is not a separation of compact sets: it says the blocks are narrow enough, relative to how far they stop short of the vertices, for
Plane.germs_split'to apply at every corner.
Instances For
The four families of blocks #
The left sector at vertex i: the arc arcCCW (tang i) (rayIn i) is the one carrying both
left germs, by Plane.germs_split'.
Instances For
Where the blocks live #
A block sits inside the rho-neighbourhood of the trimmed edge, and a sector inside the ball of
radius R about its vertex. Every "nonadjacent blocks are disjoint" step below is one of these
two containments against one of the separation hypotheses.
The germ argument at a vertex #
These four lemmas are the whole content of "at a vertex the left sides of the incoming and
outgoing strips enter the same component of the vertex disk minus the two incident rays". Each
is one projection of Plane.germs_split', applied to the frame decomposition of a point of a
block.
A point of the left block of edge i, seen from the vertex it leaves, is the outgoing left
germ, hence in the left arc there.
The same point, seen from the vertex it arrives at, is the incoming left germ.
A point of the right block of edge i, seen from the vertex it leaves, is the outgoing right
germ, hence in the right arc there.
The same point, seen from the vertex it arrives at, is the incoming right germ.
Nonadjacent blocks are disjoint #
Everything here is one of the separation hypotheses of StripData against one of the two
containments exists_foot and sector_subset_ball.
A block stays out of the sectors at every vertex other than its own two.
The two sides are disjoint #
Four families times four families. The pairs with distinct indices are settled by distance; the
pairs at a shared index are settled by Plane.arcCCW_disjoint' through the germ lemmas.