feat(kernel): bodies joined apart report their history (#136)

union_apart joins bodies whose boxes are clearly apart (many pattern tools,
#261) without any intersection work: their shells side by side. Each face
and edge of the result is its piece's own, unchanged, and the result now
says so (a history over the pieces), so a pattern's cut keeps the exact
names of every copy's faces.

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 08d797235d
commit 2bda050872
2 files changed
+33 -1

No files matched your search

+16 -1
View File
@@ -672,7 +672,22 @@ pub fn union_apart(bodies: &[Body]) -> Result<Option<Body>> {
}
}
}
join_pieces(bodies.to_vec())
// The pieces stay as they are, side by side: each face and edge of the result is its piece's
// own (the history over `bodies`).
let Some(joined) = join_pieces(bodies.to_vec())? else { return Ok(None) };
let operands: Vec<&Body> = bodies.iter().collect();
let faces = bodies
.iter()
.enumerate()
.flat_map(|(operand, b)| {
(0..b.face_count()).map(move |face| (std::collections::BTreeSet::from([crate::history::FaceOrigin::Face { operand, face }]), true))
})
.collect();
let mut edges = HashMap::new();
for (operand, b) in bodies.iter().enumerate() {
edges.extend(record::edge_origins(&b.solid, operand));
}
Ok(Some(record::traced(joined, &record::counts(&operands), Some(record::regions(faces)), Some(&edges))))
}
/// Bodies (apart from each other) as one body of several pieces.