Boolean: a point at a boundary curve's end is that end, whatever its parameter (text cuts, #391) (#1195)

A point within the point tolerance of a boundary curve's end is that end in the boolean, whatever its parameter, so text cuts no longer leave unmatched sliver vertices (#391). Robustness goldens re-recorded for seeds 0, 34, 39, 41 and 57 with reasons.
This commit is contained in:
phanoob authored and GitHub committed 2026-10-15 13:31:37 -04:00
1 parent aa31e56477
commit 56747edb46
5 files changed
+69 -9

No files matched your search

+4 -1
View File
@@ -527,6 +527,9 @@ mod tests {
assert_eq!(def["definition"]["targets"], json!(["Plate (copy)"]), "{def}");
let st = s.world_state();
let v = |name: &str| solidcraft_kernel::measure(&st.body(name).unwrap().body).unwrap().volume;
assert!((v("Plate") - v("Plate (copy)")).abs() < 1e-6, "each plate carries one spring: {} vs {}", v("Plate"), v("Plate (copy)"));
// A spring on the wrong plate is hundreds of mm³ off. The two plates are measured apart
// (one moved 50 mm), which the boolean and the measure each round in their own way:
// a few 1e-6 mm³ on 5000.
assert!((v("Plate") - v("Plate (copy)")).abs() < 1e-8 * v("Plate"), "each plate carries one spring: {} vs {}", v("Plate"), v("Plate (copy)"));
}
}
@@ -959,6 +959,44 @@ fn fillet_two_edges_meeting_at_a_corner_one_running_into_the_arc_wall() {
assert!(rel(before - after, removed) < 1e-3, "removed {} vs {removed}", before - after);
}
/// A line of text cut into a block (#391): the boolean came out empty on it after two minutes
/// of retries, at 3.5 mm deep. Two walls of a letter met the block's face 2e-7 mm apart, which
/// truck read as two vertices, and the result's shell did not close. The cut works at any
/// depth now, and takes out the same section at each (the text runs off the block's end, so a
/// little less than the letters).
#[test]
fn a_line_of_text_cuts_into_a_block_at_any_depth() {
let mut removed = Vec::new();
let mut area = 0.0;
for depth in [1.0, 3.5] {
let mut s = Session::default();
run(&mut s, "sketch.create", json!({"plane":"XY"}));
run(&mut s, "sketch.rectangle.two_point", json!({"p0":[0,0],"p1":[105,26]}));
run(&mut s, "sketch.finish", json!({}));
run(&mut s, "solid.extrude", json!({"distance":-40}));
run(&mut s, "sketch.create", json!({"plane":"XY"}));
run(&mut s, "sketch.text", json!({"text":"textvraiment tres long","at":[3,9],"height":7.81}));
let state = s.world_state();
let ss = state.sketch(s.active_sketch.unwrap()).unwrap();
// The letters' area, counters left out (a letter's hole is its own profile too).
let sorted = |ids: &[String]| {
let mut ids = ids.to_vec();
ids.sort();
ids
};
let counter = |p: &solidcraft_sketch::Profile| ss.profiles.iter().any(|q| q.hole_curves.iter().any(|h| sorted(h) == sorted(&p.outer_curves)));
area = ss.profiles.iter().filter(|p| !counter(p)).map(|p| p.area).sum();
run(&mut s, "sketch.finish", json!({}));
run(&mut s, "solid.extrude", json!({"distance":-depth,"operation":"cut"}));
let volume = run(&mut s, "inspect.measure", json!({}))["total"]["volume_mm3"].as_f64().unwrap();
removed.push((105.0 * 26.0 * 40.0 - volume) / depth);
assert_eq!(s.world_state().bodies.len(), 1);
}
let (shallow, deep) = (removed[0], removed[1]);
assert!((deep - shallow).abs() < 1e-3 * shallow, "{shallow} mm² taken out at 1 mm, {deep} mm² at 3.5 mm");
assert!(shallow > 0.85 * area && shallow < area, "{shallow} mm² taken out, the letters are {area} mm²");
}
/// A design saved before tapered sides were built through `taper_loop` (#1014), by the build
/// before it: two tapered bosses apart from each other (now joined in one boolean), a fillet on
/// a sloping side edge of one and on the top edge of the other, both saved by edge name. They