Implement the reviewed UI icon system and durable review workflow (#853)

Thanks @Nathan-Dane.
This commit is contained in:
Nathan Dane authored and GitHub committed 2026-10-17 04:07:16 -04:00
1 parent c599238653
commit 64966bab88
374 files changed
+8187 -2221

No files matched your search

+44
View File
@@ -300,6 +300,14 @@ thread_local! {
/// The result carries its exact history ([`Body::history`]) over the operands `[a, b]`,
/// whichever way it was made, or none where a step it took does not report one.
pub fn boolean(a: &Body, b: &Body, op: BoolOp) -> Result<Option<Body>> {
// Earlier coarse bores can leave a junction a later pattern copy cannot intersect.
// Resolve conical-cut junctions from the first copy, while the operands are unchanged.
if op == BoolOp::Cut && !APART.with(|c| c.get()) && conical_cut_junction(a, b) {
let refined = |a: &Body, b: &Body, op| boolean_whole_with_refinement(a, b, op, true);
if let Ok(result) = boolean_via(a, b, op, &refined) {
return Ok(result);
}
}
let r = boolean_via(a, b, op, &boolean_whole);
if r.is_ok() || !crate::tangent_cylinders::needed(a, b) {
return r;
@@ -312,6 +320,19 @@ pub fn boolean(a: &Body, b: &Body, op: BoolOp) -> Result<Option<Body>> {
boolean_via(a, b, op, &split).or(r)
}
/// A conical cutter meeting a body with an earlier conical cut needs finer tracing at
/// their junctions. Guard the analytic recognition as well as the Boolean itself.
fn conical_cut_junction(a: &Body, b: &Body) -> bool {
guard("conical cut junction", || {
let conical = |body: &Body| {
let tol = (body.size() * 1e-5).max(1e-7);
body.solid.face_iter().any(|face| matches!(crate::offset::surf_of(face, tol), Some(crate::offset::Surf::Cone { .. })))
};
Ok(conical(b) && conical(a))
})
.unwrap_or(false)
}
/// [`boolean`], the boolean of the whole bodies taken by `whole`.
fn boolean_via(a: &Body, b: &Body, op: BoolOp, whole: &dyn Fn(&Body, &Body, BoolOp) -> Result<Option<Body>>) -> Result<Option<Body>> {
let (mut r, mut diagnostics) = capture_boolean_diagnostics(|| whole(a, b, op));
@@ -548,6 +569,10 @@ fn swept_prism(a: &Body, b: &Body, op: BoolOp) -> Option<Body> {
}
fn boolean_whole(a: &Body, b: &Body, op: BoolOp) -> Result<Option<Body>> {
boolean_whole_with_refinement(a, b, op, false)
}
fn boolean_whole_with_refinement(a: &Body, b: &Body, op: BoolOp, refine: bool) -> Result<Option<Body>> {
if let Some(body) = swept_prism(a, b, op) {
return Ok(Some(body));
}
@@ -622,6 +647,25 @@ fn boolean_whole(a: &Body, b: &Body, op: BoolOp) -> Result<Option<Body>> {
};
(consistent, v)
};
if refine {
let keep: Vec<Vec<Vec3>> = a.split_keep().iter().chain(b.split_keep()).cloned().collect();
// Bounded to two finer meshes; no tool displacement or relaxed acceptance tests.
for k in [1e-5, 1e-6] {
let tol = (size * k).max(1e-6);
if let Ok((solid, history)) = truck_boolean(a, b, op, None, tol)
&& !solid.boundaries().is_empty()
&& let Ok(body) = guard("refined boolean result", || recorded::truck_result(solid, history, size, &keep, &counts))
&& check(&body).0
&& crate::boolean_guard::check_retry(a, b, op, Some(&body)).is_ok()
{
let mut event = BooleanFallback::new(BooleanStrategy::RefinedTolerance);
event.tolerance_mm = tol;
record(event);
return Ok(Some(body));
}
}
return Err(KernelError::Failed("no reliable result at refined tolerances".into()));
}
// One body clear of or inside the other: no intersection needed.
// Kept for the end when the facetings disagree with it: bodies that touch along curved
// faces can pass through each other slightly, and often can't be intersected.
@@ -5,3 +5,4 @@
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc 739d5cbd9e471e0ff65bc2b7c1b9a323c2e769939c73def2e74fb17b0536c1d0 # shrinks to a = [0.0, 2.5, 30.0], sa = [22.5, 5.0, 5.0], b = [22.5, 7.5, 15.0], sb = [5.0, 5.0, 17.5]
cc 2adfe5ac6b6916fa22a415348bdd7ece29ad33e8114b94dea86ba8bc39156dee # shrinks to a = Box { at: [15.0, 22.5, 0.0], size: [5.0, 5.0, 17.5] }, b = Box { at: [2.5, 12.5, 0.0], size: [12.5, 10.0, 5.0] }
+51 -5
View File
@@ -67,6 +67,18 @@ fn prim(c: impl Strategy<Value = f64> + Clone, s: impl Strategy<Value = f64> + C
fn grid_prim() -> impl Strategy<Value = Prim> {
prim(grid(), grid_size())
}
/// A union across only a box edge or corner is non-manifold, so the kernel may
/// refuse it. This is the same analytic boundary case allowed by box_box_exact.
fn edge_only_boxes(a: &Prim, b: &Prim) -> bool {
let (Prim::Box { at: a, size: sa }, Prim::Box { at: b, size: sb }) = (a, b) else { return false };
let gap = [0, 1, 2].map(|i| a[i].max(b[i]) - (a[i] + sa[i]).min(b[i] + sb[i]));
gap.iter().all(|g| *g <= 0.0) && gap.iter().filter(|g| **g == 0.0).count() >= 2
}
fn edge_contact_refusal(a: &Prim, b: &Prim, op: BoolOp, result: &Result<f64, TestCaseError>) -> bool {
op == BoolOp::Union && edge_only_boxes(a, b) && result.as_ref().is_err_and(|e| e.to_string().contains("zero-thickness edge contact"))
}
fn free_prim() -> impl Strategy<Value = Prim> {
prim(free(), free_size())
}
@@ -95,8 +107,13 @@ fn close(x: f64, y: f64, scale: f64) -> bool {
fn identity(a: &Prim, b: &Prim) -> Result<(), TestCaseError> {
let (ba, bb) = (a.body(), b.body());
let (va, vb) = (vol(&ba), vol(&bb));
let (vu, vi, vc) = (run(&ba, &bb, BoolOp::Union)?, run(&ba, &bb, BoolOp::Intersect)?, run(&ba, &bb, BoolOp::Cut)?);
prop_assert!(close(vu + vi, va + vb, va + vb), "union+intersect {} != A+B {} (A {va:.3}, B {vb:.3}, U {vu:.3}, I {vi:.3})", vu + vi, va + vb);
let (vu, vi, vc) = (run(&ba, &bb, BoolOp::Union), run(&ba, &bb, BoolOp::Intersect)?, run(&ba, &bb, BoolOp::Cut)?);
if edge_contact_refusal(a, b, BoolOp::Union, &vu) {
prop_assert!(close(vi, 0.0, va + vb), "edge-only contact must have no intersection volume");
} else {
let vu = vu?;
prop_assert!(close(vu + vi, va + vb, va + vb), "union+intersect {} != A+B {} (A {va:.3}, B {vb:.3}, U {vu:.3}, I {vi:.3})", vu + vi, va + vb);
}
prop_assert!(close(vc, va - vi, va + vb), "cut {vc:.3} != A-I {:.3}", va - vi);
Ok(())
}
@@ -137,7 +154,11 @@ proptest! {
let (ba, bb) = (a.body(), b.body());
let s = vol(&ba) + vol(&bb);
for op in [BoolOp::Union, BoolOp::Intersect] {
let (ab, ba_) = (run(&ba, &bb, op)?, run(&bb, &ba, op)?);
let (ab, ba_) = (run(&ba, &bb, op), run(&bb, &ba, op));
if edge_contact_refusal(&a, &b, op, &ab) && edge_contact_refusal(&a, &b, op, &ba_) {
continue;
}
let (ab, ba_) = (ab?, ba_?);
prop_assert!(close(ab, ba_, s), "{op:?}: A,B gives {ab:.4} but B,A gives {ba_:.4}");
}
}
@@ -152,7 +173,11 @@ proptest! {
let (ta, tb) = (mv(&ba), mv(&bb));
let s = vol(&ba) + vol(&bb);
for op in [BoolOp::Union, BoolOp::Intersect, BoolOp::Cut] {
let (here, there) = (run(&ba, &bb, op)?, run(&ta, &tb, op)?);
let (here, there) = (run(&ba, &bb, op), run(&ta, &tb, op));
if edge_contact_refusal(&a, &b, op, &here) && edge_contact_refusal(&a, &b, op, &there) {
continue;
}
let (here, there) = (here?, there?);
prop_assert!(close(here, there, s), "{op:?} at origin {here:.4}, moved by {off} gives {there:.4}");
}
}
@@ -169,7 +194,11 @@ proptest! {
let (ba, bb, ka, kb) = (a.body(), b.body(), sc(&a).body(), sc(&b).body());
let s = vol(&ba) + vol(&bb);
for op in [BoolOp::Union, BoolOp::Intersect, BoolOp::Cut] {
let (one, big) = (run(&ba, &bb, op)?, run(&ka, &kb, op)? / (k * k * k));
let (one, big) = (run(&ba, &bb, op), run(&ka, &kb, op));
if edge_contact_refusal(&a, &b, op, &one) && edge_contact_refusal(&a, &b, op, &big) {
continue;
}
let (one, big) = (one?, big? / (k * k * k));
prop_assert!(close(one, big, s), "{op:?} at scale 1 gives {one:.4}, at scale {k} gives {big:.4} (normalised)");
}
}
@@ -233,6 +262,23 @@ proptest! {
// Cases the property tests found, as plain tests. Ignored until the kernel bug is fixed: run them
// with `cargo test -p solidcraft-kernel --test boolean_props -- --ignored`.
/// The saved commutativity case is an intentional non-manifold join refusal,
/// not a failure to join overlapping volumes. Intersection and cut remain valid.
#[test]
fn edge_only_box_join_is_refused_in_both_orders() {
let a = Prim::Box { at: [15.0, 22.5, 0.0], size: [5.0, 5.0, 17.5] };
let b = Prim::Box { at: [2.5, 12.5, 0.0], size: [12.5, 10.0, 5.0] };
assert!(edge_only_boxes(&a, &b));
assert!(!edge_only_boxes(&a, &Prim::Box { at: [15.0, 22.5, 0.0], size: [5.0, 5.0, 5.0] }));
let (a, b) = (a.body(), b.body());
for (left, right) in [(&a, &b), (&b, &a)] {
let error = run(left, right, BoolOp::Union).expect_err("an edge-only join is non-manifold");
assert!(error.to_string().contains("zero-thickness edge contact"), "{error}");
assert_eq!(run(left, right, BoolOp::Intersect).unwrap(), 0.0);
assert!(close(run(left, right, BoolOp::Cut).unwrap(), vol(left), vol(left)));
}
}
fn check(r: Result<(), TestCaseError>) {
if let Err(e) = r {
panic!("{e}");