feat(shapeops): face and edge history from booleans

and_with_history / or_with_history return, with the solid, the operand face
each result face is a region of (and whether it is that face untouched) and,
for each result edge, the operand edge it is (a piece of) or the pair of
operand faces whose intersection made it. Faces and edges are recorded where
they are created (face division, intersection edges, split edges, edges
rebuilt on a moved vertex), resolved to operand terms at creation, so the
passes folding in extra shells compose without bookkeeping. and / or wrap
the same pass and drop the history; a missing record never fails them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J53sR6gEaVt56soFDpTck8
This commit is contained in:
Iraklis KourtisandClaude Opus 5.5 authored and bherbruck committed 2026-10-12 11:52:53 -04:00
1 parent c87efa1f35
commit cc4955a8da
12 files changed
+363 -47

No files matched your search

+6 -2
View File
@@ -1,6 +1,7 @@
#![allow(clippy::many_single_char_names)]
use super::faces_classification::FacesClassification;
use super::history::Lineage;
use super::loops_store::*;
use rustc_hash::FxHashMap as HashMap;
use std::ops::Deref;
@@ -128,6 +129,7 @@ pub fn divide_faces<C, S>(
shell: &Shell<Point3, C, S>,
loops_store: &LoopsStore<Point3, C>,
tol: f64,
lineage: &mut Lineage<C, S>,
) -> Option<FacesClassification<Point3, C, S>>
where
C: BoundedCurve<Point = Point3> + ParameterDivision1D<Point = Point3>,
@@ -145,8 +147,10 @@ where
res.push(face.clone(), ShapesOpStatus::Unknown);
} else {
let vec = crate::tr!(divide_one_face(face, loops, tol), "divide one face");
vec.into_iter()
.for_each(|(face, status)| res.push(face, status));
vec.into_iter().for_each(|(piece, status)| {
lineage.face_piece(face.id(), piece.id());
res.push(piece, status)
});
}
Some(())
})?;
+3 -3
View File
@@ -198,11 +198,11 @@ fn independent_intersection() {
geom_loops_store0: loops_store0,
geom_loops_store1: loops_store1,
..
} = create_loops_stores(&shell0, &poly_shell0, &shell1, &poly_shell1, TOL).unwrap();
let [and0, or0, unknown0] = divide_faces(&shell0, &loops_store0, TOL)
} = create_loops_stores(&shell0, &poly_shell0, &shell1, &poly_shell1, TOL, &mut Default::default()).unwrap();
let [and0, or0, unknown0] = divide_faces(&shell0, &loops_store0, TOL, &mut Default::default())
.unwrap()
.and_or_unknown();
let [and1, or1, unknown1] = divide_faces(&shell1, &loops_store1, TOL)
let [and1, or1, unknown1] = divide_faces(&shell1, &loops_store1, TOL, &mut Default::default())
.unwrap()
.and_or_unknown();
assert_eq!(and0.len(), 1);