mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 02:25:55 +00:00
Variable rounds: graded by the union of their balls (their envelope), not the balls' great circles
The smlib crate's laws and ladder 005, 006, 007 and 012 take the volumes the balls' envelope removes; the law with a corner is its own test, listed as failing until the corner step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -286,37 +286,30 @@ fn blends_take_a_size_per_edge() {
|
||||
assert!(c.blend(Blend::Round, &tops, &[2.0], 0.1).is_err());
|
||||
}
|
||||
|
||||
/// A radius running linearly from 3 to 1 along a cube edge: (1 - pi/4) L (r0² + r0 r1 + r1²) / 3
|
||||
/// taken away, whichever end is the start.
|
||||
/// A variable round is the union of its balls: in each plane square to the edge, the corner cut off
|
||||
/// by rays from it, each meeting the union first at the least entry over the balls, integrated
|
||||
/// along the edge (a linear law's balls envelop a cone). The balls' great circles, (1 - pi/4) r²
|
||||
/// per unit length, take more: 18.5988 for 3 -> 1, 39.0796 for 4, 1, 4 and 22.8945 for 4 -> 0.
|
||||
/// On the 20 cube's edge y = z = 20, 3 -> 1 takes 18.5377834 whichever end is the start.
|
||||
#[test]
|
||||
fn a_variable_round_follows_its_law() {
|
||||
let c = cube(20.0);
|
||||
let q = 1.0 - std::f64::consts::FRAC_PI_4;
|
||||
let knots = [0.0, 0.0, 1.0, 1.0];
|
||||
let r = c.fillet_variable([10.0, 20.0, 20.0], 0.1, [0.0, 20.0, 20.0], 1, &knots, &[3.0, 1.0]).unwrap();
|
||||
shot(&r, "variable-3-1");
|
||||
assert_close(volume(&r), 8000.0 - q * 20.0 * 13.0 / 3.0, 1e-5, "linear law");
|
||||
assert_close(volume(&r), 8000.0 - 18.5377834, 1e-5, "linear law");
|
||||
let back = c.fillet_variable([10.0, 20.0, 20.0], 0.1, [20.0, 20.0, 20.0], 1, &knots, &[1.0, 3.0]).unwrap();
|
||||
assert_close(volume(&back), volume(&r), 1e-7, "reversed");
|
||||
assert!(c.fillet_variable([10.0, 20.0, 20.0], 0.1, [0.0, 20.0, 20.0], 1, &[0.0, 0.5, 1.0, 1.0], &[3.0, 1.0]).is_err());
|
||||
assert!(matches!(c.fillet_variable([90.0, 90.0, 90.0], 0.1, [0.0; 3], 1, &knots, &[3.0, 1.0]), Err(Error::NoMatch { .. })));
|
||||
// A quadratic law with control radii 4, 1, 4: r(t) = 4(1 − t)² + 2t(1 − t) + 4t².
|
||||
let law = |t: f64| 4.0 * (1.0 - t).powi(2) + 2.0 * t * (1.0 - t) + 4.0 * t * t;
|
||||
let n = 4000;
|
||||
let r2 = (0..n).map(|i| law((i as f64 + 0.5) / n as f64).powi(2)).sum::<f64>() / n as f64;
|
||||
// A quadratic law with control radii 4, 1, 4, r(t) = 4(1 − t)² + 2t(1 − t) + 4t²: 39.019820.
|
||||
let quad = c.fillet_variable([10.0, 20.0, 20.0], 0.1, [0.0, 20.0, 20.0], 2, &[0.0, 0.0, 0.0, 1.0, 1.0, 1.0], &[4.0, 1.0, 4.0]).unwrap();
|
||||
shot(&quad, "variable-quadratic");
|
||||
assert_close(volume(&quad), 8000.0 - q * 20.0 * r2, 1e-5, "quadratic law");
|
||||
// Down to zero at the far end: the round closes to the edge's end point.
|
||||
assert_close(volume(&quad), 8000.0 - 39.019820, 1e-5, "quadratic law");
|
||||
// Down to zero at the far end: the round closes to the edge's end point, 22.594877.
|
||||
let zero = c.fillet_variable([10.0, 20.0, 20.0], 0.1, [0.0, 20.0, 20.0], 1, &knots, &[4.0, 0.0]).unwrap();
|
||||
shot(&zero, "variable-to-zero");
|
||||
assert_close(volume(&zero), 8000.0 - q * 20.0 * 16.0 / 3.0, 1e-5, "down to zero");
|
||||
// A law with a corner (4 → 2 → 4, straight on both halves): the round turns at the corner,
|
||||
// each half taking away (1 - pi/4) (L / 2) (16 + 8 + 4) / 3; the same points joined smoothly
|
||||
// (C1 cubic pieces, as RadiusSpline::through makes them) are built too.
|
||||
let corner = c.fillet_variable([10.0, 20.0, 20.0], 0.1, [0.0, 20.0, 20.0], 1, &[0.0, 0.0, 0.5, 1.0, 1.0], &[4.0, 2.0, 4.0]).unwrap();
|
||||
shot(&corner, "variable-corner-4-2-4");
|
||||
assert_close(volume(&corner), 8000.0 - q * 20.0 * 28.0 / 3.0, 1e-6, "corner law");
|
||||
assert_close(volume(&zero), 8000.0 - 22.594877, 1e-5, "down to zero");
|
||||
// A law down to zero inside the edge would pinch the round to a point: refused.
|
||||
let pinch = c.fillet_variable([10.0, 20.0, 20.0], 0.1, [0.0, 20.0, 20.0], 1, &[0.0, 0.0, 0.5, 1.0, 1.0], &[4.0, 0.0, 4.0]);
|
||||
assert!(pinch.is_err(), "a law pinching to zero inside the edge");
|
||||
@@ -325,6 +318,17 @@ fn a_variable_round_follows_its_law() {
|
||||
shot(&smooth.unwrap(), "variable-smooth-4-2-4");
|
||||
}
|
||||
|
||||
/// A law with a corner (4 → 2 → 4, straight on both halves) on the 20 cube's edge y = z = 20: the
|
||||
/// union of the balls, where the two halves' rounds cross and each ends, takes 39.542073 (the
|
||||
/// halves' great circles would take 40.0590).
|
||||
#[test]
|
||||
fn a_variable_round_with_a_corner_in_its_law_is_its_balls_union() {
|
||||
let c = cube(20.0);
|
||||
let corner = c.fillet_variable([10.0, 20.0, 20.0], 0.1, [0.0, 20.0, 20.0], 1, &[0.0, 0.0, 0.5, 1.0, 1.0], &[4.0, 2.0, 4.0]).unwrap();
|
||||
shot(&corner, "variable-corner-4-2-4");
|
||||
assert_close(volume(&corner), 8000.0 - 39.542073, 1e-5, "corner law");
|
||||
}
|
||||
|
||||
/// Unequal chamfers of a cube's top edge y = 20: 5 back along the face facing `first` most and 2
|
||||
/// along the other take away a triangle 5 × 2 / 2 along the 20 mm edge, either way round; 5 at
|
||||
/// 30° to the first face is 5 and 5 tan 30°.
|
||||
|
||||
@@ -18,3 +18,4 @@ solidcraft-kernel::corner_g2::tests::a_long_setback_keeps_the_g2_patch_inside_th
|
||||
solidcraft-kernel::corner_g2::tests::curvature_rounds_meet_at_a_g2_corner the explicit G2 corner patch is not yet G2 to the new order-2 section's rounds across the whole seam (the corner step replaces it)
|
||||
solidcraft-kernel::corner_g2::tests::the_box_corner_g2_patch_is_a_closed_solid_curvature_continuous_to_its_neighbours the fitted G2 corner patch bulges 1.7e-5 past the box, which SMLib's exact tight box now sees (the explicit G2 corner builds it inside)
|
||||
solidcraft-kernel::tests::fillet_tabbed_plate_outer_loop_and_partial_selection_after_hole the arc and tab rounds together remove 9.20067 against 9.20420 (3.8e-4 off, 1e-5 allowed)
|
||||
solidcraft-smlib::ops::tests::a_variable_round_with_a_corner_in_its_law_is_its_balls_union the two halves' rounds end on their own touch circles at the law's corner, not where they cross: 39.6456 removed against 39.5421
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"sources": [],
|
||||
"what": "a variable round on the top front edge, r5 at x=0 to r1 at x=60",
|
||||
"source": "analytic",
|
||||
"method": "linear radius, quarter-round sections (tests/oracle/77): V = abc - (1-pi/4) L (a^2 + ab + b^2)/3",
|
||||
"method": "linear radius: the balls' envelope, a cone (union of the balls: in each plane x = const the corner cut off by rays from it, each meeting the union first at the least entry over the balls, integrated over x): 132.858501 removed (the balls' great circles, (1-pi/4) L (a^2 + ab + b^2)/3 (tests/oracle/77), would take 133.053)",
|
||||
"body_count": 1,
|
||||
"volume_mm3": 47866.946861306,
|
||||
"volume_mm3": 47867.141499,
|
||||
"faces": 7
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"sources": [],
|
||||
"what": "a variable round on the top front edge, r5 at x=0 down to 0 at the x=60 vertex",
|
||||
"source": "analytic",
|
||||
"method": "as 005 with b=0 (tests/oracle/78): the round closes to the corner, which stays",
|
||||
"method": "as 005 with b=0 (tests/oracle/78): the cone's apex at the x=60 vertex, which stays; 107.055974 removed (great circles: 107.301)",
|
||||
"body_count": 1,
|
||||
"volume_mm3": 47892.699081699,
|
||||
"volume_mm3": 47892.944026,
|
||||
"faces": 7
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"sources": [],
|
||||
"what": "a variable round on the top front edge, r5 down to 1e-3 mm",
|
||||
"source": "analytic",
|
||||
"method": "as 005 with b = 1e-3: the round's end is a 1e-3 mm quarter circle on the end face",
|
||||
"method": "as 005 with b = 1e-3: the round's end a 1e-3 mm circle on the end face; 107.077487 removed (great circles: 107.322)",
|
||||
"body_count": 1,
|
||||
"volume_mm3": 47892.677617223,
|
||||
"volume_mm3": 47892.922513,
|
||||
"faces": 7
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
"sources": [],
|
||||
"what": "a vertical r5 round, then a variable round r1 -> r5 along the top back edge into it",
|
||||
"source": "analytic",
|
||||
"method": "a variable round takes only its picked edge (no tangent chain: crates/doc/src/eval.rs), y = 40 from x = 0 (r1) to x = 55 (r5), where it meets the vertical r5 round's tangent line: removed (1-pi/4) 55 (1 + 5 + 25)/3 (005) and (1-pi/4) 5^2 20. Past x = 55 the round's end runs on into the vertical round's quarter column, trimmed by its cylinder: as a constant r5 tube that takes int_0^5 sqrt(25 - v^2)(5 - sqrt(25 - v^2)) dv = 125 pi/4 - 250/3 = 14.841437; with its radius still growing at 4/55 per mm, 15.854054 (numerical, the round's section against the cylinder). V lies between: graded as the interval's middle with its half width as the tolerance. Faces 6 + 2; edges 18, vertices 12 (5 round the bottom, 5 vertical, 5 round the top face, the round's line on the back wall, its end arc on x = 0, its trim on the cylinder)",
|
||||
"method": "a variable round takes only its picked edge (no tangent chain: crates/doc/src/eval.rs), y = 40 from x = 0 (r1) to x = 55 (r5), where it meets the vertical r5 round's tangent line. The linear law's balls envelop a cone (union of the balls: in each plane x = const the corner cut off by rays from it, each meeting the union first at the least entry over the balls, integrated over x): 121.753137 removed along 55 (the great circles' (1-pi/4) 55 (1 + 5 + 25)/3 would take 121.965377), and (1-pi/4) 5^2 20 by the vertical round. Past x = 55 the round's end runs on into the vertical round's quarter column, trimmed by its cylinder: as a constant r5 tube that takes int_0^5 sqrt(25 - v^2)(5 - sqrt(25 - v^2)) dv = 125 pi/4 - 250/3 = 14.841437; with its radius still growing at 4/55 per mm (the cone going on), 15.820019 (the union of the balls, cut by the cylinder). V lies between: graded as the interval's middle with its half width as the tolerance. Faces 6 + 2; edges 18, vertices 12 (5 round the bottom, 5 vertical, 5 round the top face, the round's line on the back wall, its end arc on x = 0, its trim on the cylinder)",
|
||||
"body_count": 1,
|
||||
"volume_mm3": 47755.385959062,
|
||||
"volume_mm3": 47755.615217,
|
||||
"faces": 8,
|
||||
"edges": 18,
|
||||
"vertices": 12,
|
||||
@@ -21,6 +21,6 @@
|
||||
]
|
||||
},
|
||||
"tolerance": {
|
||||
"rel": 1.1602121611875055e-05
|
||||
"rel": 1.024573e-05
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user