Fuzzer: a chamfer may add material, as a fillet may (#3868)

The fuzzer chamfers randomly picked edges and expected every chamfer to
shrink the body, so a chamfer of a concave edge, which fills the corner,
was reported as a wrong volume: 53 of the 6,000 designs of a run (seed
7100465: a box, a shell, a chamfer of an inside edge). Fillets already
expect any volume change for the same reason; chamfers now do too.

The regression test checks both directions on a shelled 20 mm box (an
inside-edge chamfer of 1 mm adds 8 mm³, an outside one takes 10 mm³
away) and that seed 7100465 is no longer a volume finding.

Co-authored-by: Tyler Chiriboga <18270301+Tyler-Chiriboga@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Tyler Chiriboga
2026-10-21 18:56:51 +00:00
committed by GitHub
parent ac1768e08b
commit 915ee8a178
2 changed files with 30 additions and 1 deletions
+3 -1
View File
@@ -246,7 +246,9 @@ fn next_step(r: &mut Rng, s: &Session, last_feature: &Option<String>) -> Option<
let edges = body.body.edges(tol).ok()?;
let n = 1 + r.below(3);
let picks: Vec<[f64; 3]> = (0..n).filter_map(|_| edges.get(r.below(edges.len()))).map(|e| [e.mid.x, e.mid.y, e.mid.z]).collect();
step("chamfer", Expect::Shrink, vec![("solid.chamfer", json!({"edges": picks, "distance": r.pick(&[0.5, 1.0, 2.0])?}))])
// Any, as for fillets: the edges are picked at random, and a chamfer on a concave edge
// fills the corner, so it adds material.
step("chamfer", Expect::Any, vec![("solid.chamfer", json!({"edges": picks, "distance": r.pick(&[0.5, 1.0, 2.0])?}))])
}
8 => {
let faces: Vec<_> = body.body.faces(tol).ok()?.into_iter().filter(|f| f.plane_normal.is_some()).collect();
+27
View File
@@ -575,3 +575,30 @@ fn seed_33_display_plan_and_measure_agree() {
assert!((planned - measured).abs() < 2e-5 * measured, "{}: planned {planned}, measured {measured}", b.name);
}
}
/// A chamfer on a concave edge fills the corner, so it adds material, as a fillet there does. The
/// fuzzer expected every chamfer to shrink the body and reported these as a wrong volume (about
/// 1 % of the designs of a 6,000-seed run; seed 7100465: a box, a shell, a chamfer of an inside
/// edge). On a shelled 20 mm box, a 1 mm chamfer of an inside bottom edge adds its 16 mm long
/// half-square prism, and one of an outside bottom edge takes 20 mm of it away.
#[test]
fn chamfer_of_a_concave_edge_adds_material() {
let shelled = || {
vec![
json!({"command": "solid.box", "params": {"corner": [0, 0, 0], "length": 20, "width": 20, "height": 20}}),
json!({"command": "solid.shell", "params": {"faces": [[10, 10, 20]], "thickness": 2}}),
]
};
let base = total_volume(&script(Value::Array(shelled())));
let chamfered = |edge: [f64; 3]| {
let mut steps = shelled();
steps.push(json!({"command": "solid.chamfer", "params": {"edges": [edge], "distance": 1}}));
total_volume(&script(Value::Array(steps)))
};
let inside = chamfered([10.0, 2.0, 2.0]);
let outside = chamfered([10.0, 0.0, 0.0]);
assert!((inside - (base + 16.0 * 0.5)).abs() < 1e-6, "inside edge: {base} -> {inside}");
assert!((outside - (base - 20.0 * 0.5)).abs() < 1e-6, "outside edge: {base} -> {outside}");
let r = fuzz::run(7100465, 8);
assert!(r.outcome.kind != "volume", "seed 7100465: {:?}\n{}", r.outcome, r.script());
}