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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QWhMwdqJYQKQq8RCLSL1LT
This commit is contained in:
jyviko
2026-10-21 02:58:14 -04:00
parent 204b650d9f
commit 19cf3d94ac
3 changed files with 12 additions and 7 deletions
+5 -3
View File
@@ -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<Loop>,
}
+1 -1
View File
@@ -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));
+6 -3
View File
@@ -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.