import Mathlib import CSG.DataStructures /-! # Semantics of triangle meshes This file formalizes the geometric interpretation of triangle meshes representing solids, needed for the formal specification. We define well-formedness conditions on meshes and use the signed crossing count of rays to define the solids they bound. -/ namespace CSG open scoped Classical BigOperators /-- The set of points of the filled (closed) triangle. Defined in terms of Mathlib's `convexHull`. -/ def triangleSet (t : Triangle) : Set Point := convexHull ℚ {t.v₀, t.v₁, t.v₂} /-- The set of points on the edges of a triangle. Defined in terms of Mathlib's `segment`. -/ def edgeSet (t : Triangle) : Set Point := segment ℚ t.v₀ t.v₁ ∪ segment ℚ t.v₁ t.v₂ ∪ segment ℚ t.v₂ t.v₀ /-- The oriented normal of a face. The cross product `(v₁ - v₀) × (v₂ - v₀)` of two edge vectors. Note that the indices of type `Fin 3` are automatically taken modulo `3`. It is `0` exactly when the three vertices are collinear. -/ def faceNormal (t : Triangle) : Point := fun i => (t.v₁ (i + 1) - t.v₀ (i + 1)) * (t.v₂ (i + 2) - t.v₀ (i + 2)) - (t.v₁ (i + 2) - t.v₀ (i + 2)) * (t.v₂ (i + 1) - t.v₀ (i + 1)) /-- The dot product of `ℚ³`. -/ def dot (a b : Point) : ℚ := ∑ i, a i * b i /-- The open ray from `Q` in direction `d` meets the (closed) face `t`. -/ def RayHits (Q d : Point) (t : Triangle) : Prop := ∃ s : ℚ, 0 < s ∧ Q + s • d ∈ triangleSet t /-- The ray from `Q` in direction `d` is admissible for the mesh `M`: the direction is nonzero and the open ray stays clear of every face's edges. This forces every met face to be crossed *transversally through its relative interior*. Also see `exists_admissibleRay` in `CSG/Example.lean`. -/ def AdmissibleRay (M : Mesh) (Q d : Point) : Prop := d ≠ 0 ∧ ∀ i : Fin M.size, ∀ s : ℚ, 0 < s → Q + s • d ∉ edgeSet M[i] /-- The signed crossing count of the ray from point `Q` in direction `d`. Every face met by the open ray contributes the sign of the scalar product of `faceNormal` with `d` — `+1` when the ray crosses along the face's orientation, `-1` against it. For an admissible ray each met face is crossed transversally, so its contribution is `±1`. -/ noncomputable def crossingCount (M : Mesh) (Q d : Point) : ℤ := ∑ i : Fin M.size, if RayHits Q d M[i] then (if 0 < dot (faceNormal M[i]) d then 1 else if dot (faceNormal M[i]) d < 0 then -1 else 0) else 0 /-- The solid bounded by a mesh: The points from which *every* admissible ray reports a nonzero signed crossing count. Consider the equivalent characterization `solid_eq_exists_ray` for well-formed meshes and the example `solid_unitCube` in `CSG/Example.lean`. Note that for a well-formed mesh the surface is not included in the solid (the *open* convention). -/ def solid (M : Mesh) : Set Point := { Q | ∀ d, AdmissibleRay M Q d → crossingCount M Q d ≠ 0 } /-- A mesh has winding number zero or one when every admissible ray from every point in space reports a signed crossing count of `0` or `1`. The mesh bounds its solid with multiplicity one, in its stored orientation. This rules out e.g. doubly-traversed surfaces (winding `2`) and orientation-reversed ones (winding `-1`), so such a mesh represents its solid faithfully, with multiplicity one. -/ def WindingZeroOrOne (M : Mesh) : Prop := ∀ Q d, AdmissibleRay M Q d → crossingCount M Q d = 0 ∨ crossingCount M Q d = 1 /-- The three directed edges of an oriented face, in cyclic order. -/ def faceEdges (t : Triangle) : List (Point × Point) := [(t.v₀, t.v₁), (t.v₁, t.v₂), (t.v₂, t.v₀)] /-- The multiset of all directed edges over all faces of a mesh. -/ def edgeMultiset (M : Mesh) : Multiset (Point × Point) := ↑(M.toList.flatMap faceEdges) /-- A mesh is a closed surface with coherently oriented faces: Its directed-edge multiset is invariant under reversing every edge, i.e. each directed edge `a → b` occurs exactly as often as its reverse `b → a`. It permits any *balanced* multiplicity (2, 4, 6, …) of faces along one edge — for example four faces can meet along a shared edge where two solid components touch — so it is strictly weaker than the 2-manifold condition "exactly two opposite faces per edge". This weakening is needed so that intersections of any two well-formed meshes can again be well-formed. Note that edges subdivided differently on the two sides do *not* match. -/ def IsClosedOrientedSurface (M : Mesh) : Prop := edgeMultiset M = (edgeMultiset M).map Prod.swap /-- No face-interior contact: any two *distinct* faces meet only within the edges of each. Although the statement bounds the intersection only by the edges of face `i`, instantiating it at both `(i, j)` and `(j, i)` confines the intersection to the edges of *both* faces: no face may reach the relative *interior* of another. -/ def NoFaceInteriorContact (M : Mesh) : Prop := ∀ i j : Fin M.size, i ≠ j → triangleSet M[i] ∩ triangleSet M[j] ⊆ edgeSet M[i] /-- No degenerate faces: every face's vertices span a genuine triangle. Nonzero oriented normal, i.e. the vertices are not collinear. -/ def NoDegenerateFaces (M : Mesh) : Prop := ∀ i : Fin M.size, faceNormal M[i] ≠ 0 /-- The well-formedness precondition for a mesh fed to the intersection algorithm. A mesh is well-formed when it is: * `IsClosedOrientedSurface` — a closed, coherently oriented surface; * `WindingZeroOrOne` — every admissible ray from every point reports `0` or `1`; * `NoFaceInteriorContact` — no face reaches the relative interior of any other; * `NoDegenerateFaces` — every face is a genuine triangle. The two middle conditions divide the work of ruling out self-contact: `NoFaceInteriorContact` forbids crossings and overlaps at face interiors, while `WindingZeroOrOne` forbids the surface from penetrating into the interior of the solid. Together they confine self-contact to *touching* along edges and vertices, with consistent inside/outside. For non-vacuousness of the `WellFormedMesh` condition, consider `unitCube_wellFormed` in `CSG/Example.lean` and note that the verified kernel behind the web demo provably rejects exactly the meshes violating this condition — see theorems in `CSG/MeshIntersectWithPreconditionCheck.lean`. -/ def WellFormedMesh (M : Mesh) : Prop := IsClosedOrientedSurface M ∧ WindingZeroOrOne M ∧ NoFaceInteriorContact M ∧ NoDegenerateFaces M end CSG