Tests: a corner patch of rounds 2/3/4 takes no more than the strips leave in its block

three_rounds_at_a_corner bounded the patch's extra cut at 3 mm³ beyond the strips, a figure
with no derivation; SMLib's all-at-once body at radii 2, 3, 4 takes 3.099 (V 7878.579574
against strips 7881.678). That body is the strips' outside the block its patch lies in: its
rounds are the strips' cylinders ending at a's section y = rc, b's x = 20 − rc and c's rails
where they meet a's and b's (the patch's four corners, now asserted), so the patch takes at
most what the strips leave in that block (11.86 at 2/2/3, 32.69 at 2/3/4). The lower bound is
that block; the upper one, at most the strips' volume, stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jyviko
2026-10-30 18:35:03 -04:00
committed by jyviko
parent f3cf0c4f37
commit 134a34f895
+41 -3
View File
@@ -298,6 +298,30 @@ mod tests {
8000.0 - full + (sum - union) * hh * hh
}
/// What the strips leave of the corner block where a patch over the corner of rounds `ra`,
/// `rb`, `rc` on a, b, c lies: beyond a's end section (y < rc), b's (x > 20 − rc) and c's end
/// (z > 20 − max(ra, rb), where c's rails meet a's and b's), the box's material that no
/// strip removes, by columns along z. Outside that block such a body is the strips' (its
/// rounds are the strips' cylinders up to those ends), so its patch takes at most this
/// beyond them.
fn corner_block(ra: f64, rb: f64, rc: f64) -> f64 {
let len = |d: f64, r: f64| if d < r { r - (r * r - (r - d) * (r - d)).max(0.0).sqrt() } else { 0.0 };
let (n, depth) = (2000, ra.max(rb));
let h = rc / n as f64;
let mut kept = 0.0;
for i in 0..n {
let dx = (i as f64 + 0.5) * h;
for j in 0..n {
let dy = (j as f64 + 0.5) * h;
if (rc - dx).powi(2) + (rc - dy).powi(2) > rc * rc && dx < rc && dy < rc {
continue;
}
kept += depth - len(dx, ra).max(len(dy, rb)).min(depth);
}
}
kept * h * h
}
/// Largest angle (degrees) between the two faces' normals along edges between curved faces.
fn kink(b: &Body) -> f64 {
let rec = Faces::of(b).unwrap();
@@ -484,15 +508,29 @@ mod tests {
assert!(r.validity().is_empty(), "{:?}", r.validity());
assert!((vol(&r) - sphere).abs() < 1e-11 * sphere, "{} vs {sphere}", vol(&r));
assert!(kink(&r) < 0.01);
// Different radii: a smooth patch over the corner, slightly more than the strips take.
// Different radii: a smooth patch over the corner, more than the strips take, and no more
// than they leave in the block it lies in: its rounds end at a's section y = rc, b's
// x = 20 − rc, and c's rails where they meet a's and b's (z = 20 − ra, z = 20 − rb), the
// patch's four corners. (2, 2, 3): 1.168 beyond the strips, of the 11.86 the block keeps;
// (2, 3, 4): 3.099 of 32.69.
for (rs, sel) in [((2.0, 2.0, 3.0), fillet(&ab, &[c], 3.0)), ((2.0, 3.0, 4.0), fillet_radii(&bx, &[(a, 2.0), (b, 3.0), (c, 4.0)]))] {
let r = sel.unwrap();
assert_eq!(r.face_count(), 10);
assert_eq!(r.unmeshed_faces(), 0);
let k = kink(&r);
assert!(k < 0.05, "kink {k}°");
let (v, s) = (vol(&r), strips(rs.0, rs.1, rs.2));
assert!(v < s + 0.05 && v > s - 3.0, "{v} vs {s}");
let (ra, rb, rc) = rs;
let ends = [
Vec3::new(20.0 - ra, rc, 20.0),
Vec3::new(20.0, rc, 20.0 - ra),
Vec3::new(20.0 - rc, rb, 20.0),
Vec3::new(20.0 - rc, 0.0, 20.0 - rb),
];
for p in ends {
assert!(vertices(&r).iter().any(|q| q.dist(p) < 1e-9), "{rs:?}: no patch corner at {p:?}");
}
let (v, s) = (vol(&r), strips(ra, rb, rc));
assert!(v < s + 0.05 && v > s - corner_block(ra, rb, rc), "{v} vs {s}");
}
}