Kernel tests: modifier histories are graded by what each face is, not how many changed

Three history tests pinned truck's sewing in tallies:
- shell_of_a_painted_body wanted changes [0, 2, 8, 0, 5], the side faces
  split across the rim's level; SMLib's shell leaves them whole. It now
  checks what paint follows: the painted face 0 comes through as exactly
  one face (its geometry checked against face 0 by verified()), and each of
  the five inner walls is one Generated Offset of the face it lines, the
  open top none, as shell_lines_the_walls_with_offsets has it.
- fillet_after_box_cuts wanted the painted cuts to leave faces in pieces
  and the fillet's edges to meet merged faces; it keeps the outcome for
  every cut on both blocks: the round stands in place of the picked edge,
  and composed with the cut the history holds over the block and the tool.
  merged_meetings has no use left and goes.
- surfaces_keep_their_faces wanted every stitched face Kept; whether
  sewing changes a face's class is not the contract. It now checks each
  stitched face has one origin, the face it was sewn from (geometry checked
  by checked()), the five walls the open box's faces 0-4 and the lid the
  patch, and the four seams naming two edges each.

split_a_face is left as it is (the owner's ruling: a face whose boundary
only gained a vertex is Kept), failing until SolidCraft's split history
says so ([3, 2, 2, 0, 0] against [5, 0, 2, 0, 0]). The three tests above
pass today.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jyviko
2026-10-30 06:38:59 -04:00
committed by Irakli Kourtis
parent 4ff38619c1
commit a3848d1d0c
+31 -24
View File
@@ -263,15 +263,23 @@ fn shell_of_a_stepped_body_joins_its_walls() {
assert_eq!(offsets, b.face_count() - 1, "{h:?}");
}
/// A shell of a painted block: its faces are used as they are (unhealed), keeping the paint's
/// face order, with the history of that copy. Paint is an authored boundary, so the cavity Cut
/// keeps the side faces it sews across the rim's level in pieces (Split).
/// A shell of a painted block: the painted face (face 0) comes through as one face on its own
/// support, so the paint has one face to follow, and each of the five inner walls is an offset of
/// the face it lines, as on the plain block (`shell_lines_the_walls_with_offsets`).
#[test]
fn shell_of_a_painted_body() {
let b = painted_cube();
let r = crate::shell(&b, &[Vec3::new(20.0, 15.0, 20.0)], 2.0).unwrap();
let top = Vec3::new(20.0, 15.0, 20.0);
let r = crate::shell(&b, &[top], 2.0).unwrap();
let h = verified(&r, &b, reach(2.0));
assert_eq!(changes(h), [0, 2, 8, 0, 5], "{h:?}");
assert_eq!(with(h, &[face(0)]).len(), 1, "the painted face is one face: {h:?}");
let top = face_at(&b, top);
for f in (0..6).filter(|f| *f != top) {
let walls = with(h, &[FaceOrigin::Offset { operand: 0, face: f }]);
assert_eq!(walls.len(), 1, "{h:?}");
assert_eq!(h.faces[walls[0]].change, Change::Generated);
}
assert!(with(h, &[FaceOrigin::Offset { operand: 0, face: top }]).is_empty());
}
#[test]
@@ -370,10 +378,22 @@ fn surfaces_keep_their_faces() {
let h = checked(&lid, &[&open], reach(0.0), &|_| true);
assert_eq!(h.faces[0].change, Change::Generated);
assert!(h.edges.iter().all(|e| matches!(e.origins.as_slice(), [EdgeOrigin::Edge { operand: 0, .. }])));
// Stitched: each face the one it was sewn from, the seam the edges sewn together.
// Stitched: each face the one it was sewn from (its geometry checked against it), the five
// walls each one of the open box's and the lid the patch, and the seam the edges sewn
// together.
let closed = crate::stitch(&[&open, &lid], 0.0).unwrap();
let h = checked(&closed, &[&open, &lid], reach(0.0), &|_| false);
assert!(h.faces.iter().all(|f| f.change == Change::Kept && f.origins.len() == 1));
let mut sewn: Vec<FaceOrigin> = h
.faces
.iter()
.map(|f| match f.origins[..] {
[o] => o,
_ => panic!("a stitched face from one face: {f:?}"),
})
.collect();
sewn.sort();
let want = [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0)].map(|(operand, face)| FaceOrigin::Face { operand, face });
assert_eq!(sewn, want, "{h:?}");
assert_eq!(h.edges.iter().filter(|e| e.origins.len() == 2).count(), 4);
let patch = crate::patch_region(&Plane::XY, &Region2 { outer: square(0.0, 0.0, 10.0, 10.0), holes: vec![] }).unwrap();
assert_eq!(patch.history().unwrap().operands, 0);
@@ -432,29 +452,16 @@ fn box_cuts_of(b: &Body) -> Vec<(&'static str, Body, Body)> {
.collect()
}
/// Meetings with a side of several origins (a face the modifier's heal merged).
fn merged_meetings(h: &History) -> usize {
h.edges.iter().flat_map(|e| &e.origins).filter(|o| matches!(o, EdgeOrigin::Meet { a, b } if a.single().is_none() || b.single().is_none())).count()
}
/// A fillet right after a box cut. The cut joins the walls it sews in pieces across its slabs
/// on their common supports, so the fillet finds whole faces. Where the block is painted the cut
/// keeps those pieces (it has more faces), the fillet's heal merges them, and the edges where
/// the round runs into such a face meet all of it. Composed with the cut, over the block and
/// the tool.
/// A fillet right after a box cut, on the plain and the painted block: the round stands in place
/// of the picked edge, and composed with the cut its history holds over the block and the tool.
#[test]
fn fillet_after_box_cuts() {
let at = Vec3::new(5.0, 0.0, 20.0);
let plain = box_cuts_of(&cube());
for (b, painted) in [(cube(), false), (painted_cube(), true)] {
let cuts = if painted { box_cuts_of(&b) } else { plain.clone() };
for ((name, cut, tool), (_, whole, _)) in cuts.into_iter().zip(&plain) {
for (name, cut, tool) in box_cuts_of(&b) {
let r = crate::fillet(&cut, &[at], 2.0).unwrap();
let h = verified(&r, &cut, reach(2.0 * 1.5));
assert_eq!(blends(h), [vec![edge(edge_at(&cut, at))]], "{name}");
let pieces = cut.face_count() > whole.face_count();
assert_eq!(pieces, painted && name != "notch" && name != "hole", "{name} (painted: {painted})");
assert_eq!(merged_meetings(h) > 0, pieces, "{name} (painted: {painted}): {h:?}");
assert_eq!(blends(h), [vec![edge(edge_at(&cut, at))]], "{name} (painted: {painted})");
let mut through = h.clone();
through.substitute(0, cut.history().unwrap()).unwrap();
through.check(&r, &[&b, &tool]).unwrap();