From 19cf3d94acea14b3d2f54897faee5c3a4cd48bdf Mon Sep 17 00:00:00 2001 From: jyviko <2501648+jyviko@users.noreply.github.com> Date: Wed, 21 Oct 2026 02:58:14 -0400 Subject: [PATCH] brep: orientation, not position, tells a face's outer loop truck lists some planar faces' hole loops before the outer loop, so the rule that loops[0] is the outer loop never held for truck's records; moving it in the adapter would renumber those faces' edges, which history and names stand on. The outer loop is the one that runs counter-clockwise about the natural normal (what check_geometry checks); the order is the producer's, and SMLib's import puts the outer loop first, as SMLib needs. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01QWhMwdqJYQKQq8RCLSL1LT --- crates/geom/src/brep.rs | 8 +++++--- crates/geom/src/brep/geometry.rs | 2 +- docs/native-brep.md | 9 ++++++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/crates/geom/src/brep.rs b/crates/geom/src/brep.rs index b5f59d2ccb..d81dd31ea2 100644 --- a/crates/geom/src/brep.rs +++ b/crates/geom/src/brep.rs @@ -85,9 +85,11 @@ pub struct Face { /// The modelled rectangle in surface parameters, `[[u0, u1], [v0, v1]]`, inside the /// surface's natural domain and around every loop. pub domain: [[f64; 2]; 2], - /// `loops[0]` is the outer loop, the rest inner. Loops are stored relative to the surface's - /// natural normal, whatever side a shell uses: seen from that normal, the outer loop runs - /// counter-clockwise and inner loops clockwise. + /// One outer loop and any inner ones, in the producer's order (truck lists some holes + /// first; SMLib's import puts the outer loop first, as SMLib requires). Loops are stored + /// relative to the surface's natural normal, whatever side a shell uses: seen from that + /// normal, the outer loop runs counter-clockwise and inner loops clockwise, which is what + /// tells them apart. The first loop is an edge loop. pub loops: Vec, } diff --git a/crates/geom/src/brep/geometry.rs b/crates/geom/src/brep/geometry.rs index a69dcb01da..247099ea29 100644 --- a/crates/geom/src/brep/geometry.rs +++ b/crates/geom/src/brep/geometry.rs @@ -172,7 +172,7 @@ impl Brep { } } } - // The area the loop encloses, its sign by its place (outer first). Where + // The signed area the loop encloses (positive for the outer loop). Where // uses meet at a pole the ring runs straight along the pole's line. let at = Record::Loop { face, r#loop }; let mut ring: Vec<[f64; 2]> = Vec::with_capacity(edges.len().saturating_mul(AREA_SAMPLES)); diff --git a/docs/native-brep.md b/docs/native-brep.md index edeea5e8ad..911e8130d7 100644 --- a/docs/native-brep.md +++ b/docs/native-brep.md @@ -52,8 +52,11 @@ shells with opposite `forward`. `forward` means the face's normal in that shell its surface's natural normal, ∂S/∂u × ∂S/∂v. In a closed shell, normals point out of the material. -**Loops.** A face's first loop is its outer loop and is an edge loop. A vertex -loop (a pole, a collapsed boundary) is only ever an inner loop. Loops are stored +**Loops.** A face has one outer loop and any number of inner ones, in the +producer's order: truck lists some holes before the outer loop, and an SMLib +import puts the outer loop first, as SMLib requires. The first loop is an edge +loop. A vertex loop (a pole, a collapsed boundary) is only ever an inner loop. +Orientation tells outer from inner. Loops are stored relative to the surface's natural normal, whichever side a shell uses: seen from that normal, the outer loop runs counter-clockwise and inner loops clockwise. This lets one face sit in two shells without contradicting itself. An edge loop @@ -82,7 +85,7 @@ paint and persistent names. The same holds for edges and vertices. 1. Regions: the infinite region, then bounded regions in the producer's order. 2. Shells and faces in order of first appearance, walking regions → shells → oriented faces. -3. Edges in order of first appearance, walking faces → loops (outer first) → +3. Edges in order of first appearance, walking faces → loops (in stored order) → oriented edges, then wire edges walking shells. A face whose first oriented face is reversed is walked backwards: each of its loops from its last oriented edge to its first. That is how a reversed face is traversed.