feat(kernel): an edge's meeting sides are sets of origins (merged faces)

EdgeOrigin::Meet now names each side as a Side: the sorted, de-duplicated
origins of the face there. substitute maps a side to the union of the inner
faces' origins, so a meeting with a face an inner operation merged keeps its
lineage; meeting() derives sides from whole faces. Fillets after box cuts
that leave coplanar pieces now report exact history. Prism lineage meets a
cap of several origins and resolves wall junctions away from shared
vertices by the operand's one edge between the walls. bore_union's recut
reports history only for a one-face bore (pieces of a two-arc bore's recut
wall were given both arcs).

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 08d7037176
commit 43ddde4a5d
11 files changed
+347 -76

No files matched your search

+10 -5
View File
@@ -357,14 +357,15 @@ fn trimmed(x: &Body, h: History, slab: &Body, operands: [&Body; 2], n: Vec3, d:
f.origins = origins.into_iter().collect();
}
}
let cap = crate::history::Side::from(cap);
for e in &mut cut.edges {
for o in &mut e.origins {
let side = match *o {
EdgeOrigin::Meet { a, b } if a == cap => b,
EdgeOrigin::Meet { a, b } if b == cap => a,
let side = match o {
EdgeOrigin::Meet { a, b } if *a == cap => b,
EdgeOrigin::Meet { a, b } if *b == cap => a,
_ => continue,
};
let FaceOrigin::Face { operand, face } = side else { return None };
let Some(FaceOrigin::Face { operand, face }) = side.single() else { return None };
let (plane_faces, bounds) = (in_plane.get(operand)?, bounds.get(operand)?);
let mut along = (0..bounds.len())
.filter(|k| bounds.get(*k).is_some_and(|fs| fs.len() == 2 && fs.contains(&face) && fs.iter().any(|f| plane_faces.contains(f))));
@@ -541,8 +542,12 @@ fn same_cylinder(x: &CylFace, y: &CylFace, tol: f64) -> bool {
/// The history over `[a, b]` of `r`, made in [`bore_union`]: `joined` (the target joined with
/// `narrowed`, the tool with its bore narrowed) less `cut`, the cylinder whose wall restores the
/// tool's bore faces `bore` (the tool is operand `tool_operand`). Nothing of the cylinder but its
/// wall may be left.
/// wall may be left. Only a bore of one face: the cylinder's wall faces are split where its own
/// construction splits them, so which of several bore faces a piece of it lies over is not known.
fn recut(r: &Body, joined: &Body, narrowed: &Body, cut: &Body, bore: &[usize], tool_operand: usize) -> Option<History> {
if bore.len() != 1 {
return None;
}
// Over [target, tool].
let joined = crate::boolean_record::through(joined, 1, narrowed.history())?;
let wall = || record::region(bore.iter().map(|&face| FaceOrigin::Face { operand: 1, face }).collect(), false);