Persistent face names: keep exact history when a prism boolean meets stacked pieces of one cylinder (#1209) (#2205)
* claim: #1209 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011YdMi9BDdjWZ8twfeBHAXz * Prism boolean: a curved edge at one level crosses the walls there only (#1209) The prism boolean sews its result slab by slab and keeps, for each section segment, the operand wall it was read beside; its history is built from those tags. `Stations::new` took every curved edge between two walls as crossing them at every level, so where two pieces of one cylinder are stacked (an arm's round end under a boss of that radius) the circle between them cleared the wall tags of every slab. `prism_lineage::sewn` then gave up, the join or cut came out with no history, and every face of the body was named from geometry (`exact: false`). A curve whose control points lie at one level (a B-spline, or a NURBS curve with positive weights, which lies in their convex hull) is at that level: it crosses the walls there only. Any other curve still spans every level. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011YdMi9BDdjWZ8twfeBHAXz --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: bherbruck <brennen.herbruck@gmail.com>
This commit is contained in:
3 files changed
+136
-3
No files matched your search
@@ -501,3 +501,26 @@ fn bodies_apart_joined_keep_their_faces() {
|
||||
}
|
||||
assert!(faces_of(h).iter().all(|(change, origins)| *change == Change::Kept && origins.len() == 1), "{}", show(h));
|
||||
}
|
||||
|
||||
/// A plate with a round end and a boss of that radius standing on it (#1209): the boss's wall
|
||||
/// and the plate's round end are two pieces of one cylinder, one over the other, meeting along a
|
||||
/// circle at the plate's top. That circle crosses the walls at its level only, so a second boss
|
||||
/// joined to the plate and a cut through the first, both prism booleans sewn slab by slab, keep
|
||||
/// every face of the plate and its boss, with their history.
|
||||
#[test]
|
||||
fn stacked_pieces_of_one_cylinder_keep_their_walls() {
|
||||
let plate =
|
||||
boolean(&boxed(0.0, -6.0, 0.0, 40.0, 6.0, 5.0), &crate::cylinder(Vec3::ZERO, Vec3::Z, 10.0, 5.0).unwrap(), BoolOp::Union).unwrap().unwrap();
|
||||
let arm = boolean(&plate, &crate::cylinder(Vec3::ZERO, Vec3::Z, 10.0, 8.0).unwrap(), BoolOp::Union).unwrap().unwrap();
|
||||
verified(&arm, &[&plate, &crate::cylinder(Vec3::ZERO, Vec3::Z, 10.0, 8.0).unwrap()], 0.01);
|
||||
// Standing over the plate's far end, as the boss at the arm's other end does.
|
||||
let second = crate::cylinder(Vec3::new(40.0, 0.0, 0.0), Vec3::Z, 4.0, 7.0).unwrap();
|
||||
let bore = boxed(-3.0, -3.0, -1.0, 3.0, 3.0, 10.0);
|
||||
for (tool, op) in [(&second, BoolOp::Union), (&bore, BoolOp::Cut)] {
|
||||
let r = boolean(&arm, tool, op).unwrap().unwrap();
|
||||
let h = verified(&r, &[&arm, tool], 0.01);
|
||||
for f in 0..arm.face_count() {
|
||||
assert!(!from(h, face(0, f)).is_empty(), "{op:?}: face {f} of the plate and boss is lost\n{}", show(h));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user