Booleans build the same body every time: face order no longer follows heap addresses (#748)

truck's face and edge IDs are their addresses, so walking maps keyed by them followed heap layout, and the ribbed lid's side-wall hole passed or failed by chance. The planar boolean's rebuild and glue now take truck's connected components in the shell's order, and the planar boolean's own maps are ordered. Healing's union-find keeps the lesser root and takes a run's ends in order of use. The face classification after an intersection orders each component's faces by the shell first. The lid now builds the same body on every run, with a test that builds it six times in one process. Its 0.5 mm side-wall holes, and one sensor enclosure hole of the same kind, now fail every time and are marked failing in the baseline.

Fixes #707.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
bherbruckandClaude Opus 5.5 authored and GitHub committed 2026-10-13 12:31:23 -04:00
1 parent 58af8accfa
commit a3dfbaa0b0
6 files changed
+95 -32

No files matched your search

+1 -1
View File
@@ -712,7 +712,7 @@ pub(crate) fn glue(a: &Body, b: &Body) -> Option<Result<Body>> {
// Bodies of several pieces stay in several pieces (each face's provenance follows it).
let prov: HashMap<mt::FaceID, FaceProv> = faces.iter().map(|f| f.id()).zip(from).collect();
let shell: mt::Shell = faces.into();
let solid = Solid::try_new(shell.connected_components()).map_err(|e| fail(&e.to_string()))?;
let solid = Solid::try_new(crate::polybool::components_in_order(&shell)).map_err(|e| fail(&e.to_string()))?;
let from: Vec<FaceProv> = solid.face_iter().filter_map(|f| prov.get(&f.id()).cloned()).collect();
// Each edge's origins in the solid's edge order.
let origins = crate::body::edge_order(solid.face_iter()).0.iter().map(|e| origins.get(&e.id()).cloned()).collect();