mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 10:35:58 +00:00
solidcore: a variable round is built again, as the union of its balls
With #2262 and fillet_gn_section_generator on, solidcore builds a
variable round as the union of its balls, so fillet_variable's
refusal from sync-2 (9f64cce54, solidcore#2250) comes off. Laws with a
corner stay refused (solidcore#430); a law whose balls fold is refused
by solidcore (NoRoomReason::BallsFold) and arrives as Error::NoRoom.
Against closed forms, solidcore and SMLib d01d7b0 agreeing to 1e-11:
- 1 to 3 along 30 mm removes 27.858907 against the balls' union's
27.857434 (great circles 27.898239); a law of one radius 2 removes
25.753574 against (1 - π/4) 4 · 30 = 25.752220: both 5.3e-5 over,
the solver's surface fit, as on the C++ kernel.
- the rig's 1 to 5 cone: 11956.21991 against 11956.22243 (2.1e-7).
- kernel tests: a_linear_radius_runs_from_the_start_end (18.5378),
variable_radius_on_a_box_edge, down_to_zero (22.5949),
taper_follows_the_room (the wedge 5245.68 against the balls'
union) pass.
The refused test becomes a_variable_round_is_the_union_of_its_balls
(the two closed forms, either direction, a folding law refused, a pick
miss named first). the_wedge_round_keeps_its_volume held 5223.0386,
measured before the fork's balls' union; it now takes its closed form
from removed_by_balls.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEu4Lr3moCCisdZShKz
This commit is contained in:
@@ -1,9 +1,14 @@
|
||||
use super::*;
|
||||
|
||||
fn wedge() -> Body {
|
||||
/// A wedge 40 x 20, 15 high, its top front edge along x from 0 to 40.
|
||||
fn wedge_body() -> Body {
|
||||
use solidcraft_geom::{Loop2, Plane, Region2, Vec2};
|
||||
let outer = Loop2::polygon(&[Vec2::new(0., 0.), Vec2::new(40., 0.), Vec2::new(0., 20.)]);
|
||||
let body = crate::extrude(&Plane::XY, &[Region2 { outer, holes: vec![] }], 0., 15.).unwrap().pop().unwrap();
|
||||
crate::extrude(&Plane::XY, &[Region2 { outer, holes: vec![] }], 0., 15.).unwrap().pop().unwrap()
|
||||
}
|
||||
|
||||
fn wedge() -> Body {
|
||||
let body = wedge_body();
|
||||
let at = Vec3::new(20., 0., 15.);
|
||||
// The room's taper, closing to zero at the wedge's tip, given as a spline fillet: R16 itself
|
||||
// is built over the wedge's cliffs (the blend-overflow decision), so a tapered fillet no
|
||||
@@ -12,12 +17,14 @@ fn wedge() -> Body {
|
||||
fillet_variable_spline(&body, at, t.start, &t.spline).unwrap()
|
||||
}
|
||||
|
||||
/// The wedge's variable round: V 5223.0386, on whichever kernel the test runs (the fork's variable
|
||||
/// round follows the fillet between its traced points, 4438859, and solidcore's with it).
|
||||
/// The wedge's variable round takes off the union of its balls (smlib#78, solidcore#2262):
|
||||
/// `removed_by_balls` along the 40 mm edge, about 754.28, on whichever kernel the test runs.
|
||||
#[test]
|
||||
fn the_wedge_round_keeps_its_volume() {
|
||||
let v = crate::measure(&wedge()).unwrap().volume;
|
||||
assert!((v - 5223.0386).abs() < 1e-3, "{v}");
|
||||
let law = taper(&wedge_body(), Vec3::new(20., 0., 15.), 16.).unwrap().spline;
|
||||
let want = 6000.0 - super::tests::removed_by_balls(40.0, &|u| law.eval(u), &|_, _| f64::INFINITY, &[]);
|
||||
assert!(((v - want) / want).abs() < 1e-4, "{v} vs {want}");
|
||||
}
|
||||
|
||||
/// The display mesh of a fillet tapering to zero at a wedge's tip converges to the B-rep: each
|
||||
|
||||
@@ -660,14 +660,6 @@ fn untagged(mut b: Brep) -> Out<Brep> {
|
||||
pub(crate) const CURVATURE_SECTION: &str = "a curvature-continuous fillet on solidcore: the curvature section's equal-area Gⁿ \
|
||||
generator is not in solidcore yet (solidcore#2250, port in progress as #2260)";
|
||||
|
||||
/// Why a variable round whose radius changes is refused ([`fillet_variable`]). The capi builds it
|
||||
/// as the union of its balls (fork 6b346f6, 5db8feb, fb5c060); solidcore's variable round takes
|
||||
/// the balls' great circles, a body every check passes but not that round, so it is not built in
|
||||
/// its place. The refusal comes off when the solidcore pin carries #2262 (on #2260) and its
|
||||
/// `fillet_gn_section_generator` flag is on.
|
||||
pub(crate) const VARIABLE_ROUND: &str = "a variable-radius fillet on solidcore: a round whose radius changes is the union of its \
|
||||
balls, which solidcore does not build yet (solidcore#2250, port in progress as #2262)";
|
||||
|
||||
/// [`blend_holes`] without its holes.
|
||||
#[allow(clippy::too_many_arguments)] // the capi's blend_call
|
||||
pub(crate) fn blend(
|
||||
@@ -1216,24 +1208,31 @@ pub(crate) fn fillet_variable(
|
||||
if law_pinches(degree, knots, radii) {
|
||||
return Err(Error::Unsupported);
|
||||
}
|
||||
on_copy("smlib_fillet_variable", body, |k, w| {
|
||||
on_copy("smlib_fillet_variable", body, |k, mut w| {
|
||||
let picked = match pick_edges(k, &w, &[point], tolerance)? {
|
||||
Ok((p, _)) => p,
|
||||
Err(m) => return Ok(Err(edge_miss(m))),
|
||||
};
|
||||
let [e] = picked[..] else { return Ok(Err(Error::NoMatch { point: 0 })) };
|
||||
if law_along(k, &w, e, start, degree, knots, radii)?.is_none() {
|
||||
let Some(law) = law_along(k, &w, e, start, degree, knots, radii)? else {
|
||||
return Ok(Err(Error::Status(INVALID_INPUT)));
|
||||
}
|
||||
};
|
||||
// A law with a corner splits the round's face there (`SplitFilletFacesAtRadiusCorners`,
|
||||
// fork 6eecf04), which solidcore does not have.
|
||||
if law_has_corner(degree, knots, radii) {
|
||||
return Ok(Err(Error::Unsupported));
|
||||
}
|
||||
// The round is the union of its balls (smlib#78): each ball touches it along the circle
|
||||
// shifted -r r'/|c'| from its great circle, which solidcore's variable round does not
|
||||
// take yet: the great-circle round is not built in its place.
|
||||
Ok(Err(Error::Unported(VARIABLE_ROUND)))
|
||||
// The round is the union of its balls (smlib#78, solidcore#2262 with
|
||||
// `fillet_gn_section_generator`): each ball touches it along the circle shifted
|
||||
// -r r'/|c'| from its great circle. A law whose balls fold over one another is refused
|
||||
// (`NoRoomReason::BallsFold`), [`Error::NoRoom`].
|
||||
let solid_in = brep_p2::is_manifold_solid(&w)?;
|
||||
let (r0, r1) = (radii.first().copied().unwrap_or(0.0), radii.last().copied().unwrap_or(0.0));
|
||||
Ok(match blend_edges(k, &mut w, &[e], &[r0.max(r1)], Section::Round, Some(law), &[], None)? {
|
||||
Ok(()) => finish(w, solid_in)?,
|
||||
Err(Ok(_)) => Err(Error::Unsupported),
|
||||
Err(Err(e)) => Err(e),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -678,26 +678,31 @@ fn a_curvature_round_is_refused_until_solidcore_has_the_equal_area_section() {
|
||||
assert_eq!(blend(&b, Blend::Curvature, &[[90.0; 3]], &[2.0], TOL, &[], &[], Corners::Filled).err(), Some(Error::NoMatch { point: 0 }));
|
||||
}
|
||||
|
||||
/// A variable round whose radius changes is refused on solidcore: the round is the union of its
|
||||
/// balls, and solidcore's takes their great circles (1 to 3 along 30 mm: 27.8982 off, not
|
||||
/// 27.8574), until its pin has that round (solidcore#2250). A constant round is untouched.
|
||||
/// A variable round is the union of its balls (solidcore#2262, `fillet_gn_section_generator`).
|
||||
/// Along the 30 mm top front edge: a law of one radius 2 takes the constant round's
|
||||
/// (1 - π/4) 4 · 30 = 25.752220; 1 to 3, the balls' union's 27.857434 (the great circles would take
|
||||
/// 27.898239), either way along the edge. Both come out 5.3e-5 of that over, as on the C++
|
||||
/// kernel: the solver's surface fit. A law whose radius climbs faster than a rolling ball can
|
||||
/// follow (1 to 4 within a mm) folds its balls over one another: refused, not built.
|
||||
#[test]
|
||||
fn a_variable_round_is_refused_until_solidcore_has_the_balls_union() {
|
||||
fn a_variable_round_is_the_union_of_its_balls() {
|
||||
let b = BLOCK.core();
|
||||
let linear = fillet_variable(&b, TOP_FRONT, TOL, [0.0, 0.0, 10.0], 1, &[0.0, 0.0, 1.0, 1.0], &[1.0, 3.0]).err();
|
||||
assert_eq!(linear, Some(Error::Unported(super::blend::VARIABLE_ROUND)));
|
||||
assert!(linear.is_some_and(|e| e.to_string().contains("solidcore#2250")));
|
||||
let cubic = [0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0];
|
||||
let bulge = fillet_variable(&b, TOP_FRONT, TOL, [0.0, 0.0, 10.0], 3, &cubic, &[1.0, 2.0, 2.0, 1.0]).err();
|
||||
assert_eq!(bulge, Some(Error::Unported(super::blend::VARIABLE_ROUND)));
|
||||
// A law of one radius too: the variable round's own surface, not the constant round's.
|
||||
let constant = fillet_variable(&b, TOP_FRONT, TOL, [0.0, 0.0, 10.0], 1, &[0.0, 0.0, 1.0, 1.0], &[2.0, 2.0]).err();
|
||||
assert_eq!(constant, Some(Error::Unported(super::blend::VARIABLE_ROUND)));
|
||||
let removed = |radii: &[f64], start: [f64; 3]| {
|
||||
let r = fillet_variable(&b, TOP_FRONT, TOL, start, 1, &[0.0, 0.0, 1.0, 1.0], radii).unwrap();
|
||||
let (rec, _) = native(&to_arrays(&r).unwrap()).unwrap();
|
||||
rec.validate().unwrap();
|
||||
ABC - volume(&r, 1e-8).unwrap()
|
||||
};
|
||||
let constant = removed(&[2.0, 2.0], [0.0, 0.0, 10.0]);
|
||||
assert!(rel(constant, (1.0 - PI / 4.0) * 4.0 * 30.0) < 1e-4, "{constant}");
|
||||
let linear = removed(&[1.0, 3.0], [0.0, 0.0, 10.0]);
|
||||
assert!(rel(linear, 27.857434) < 1e-4, "{linear}");
|
||||
assert!(rel(removed(&[1.0, 3.0], [30.0, 0.0, 10.0]), linear) < 1e-8);
|
||||
let steep = [0.0, 0.0, 0.0, 0.0, 0.02, 0.04, 1.0, 1.0, 1.0, 1.0];
|
||||
let folds = fillet_variable(&b, TOP_FRONT, TOL, [0.0, 0.0, 10.0], 3, &steep, &[1.0, 1.0, 4.0, 4.0, 4.0, 4.0]).err();
|
||||
assert_eq!(folds, Some(Error::NoRoom));
|
||||
// A pick that misses is still named first.
|
||||
assert_eq!(fillet_variable(&b, [90.0; 3], TOL, [0.0; 3], 1, &[0.0, 0.0, 1.0, 1.0], &[1.0, 3.0]).err(), Some(Error::NoMatch { point: 0 }));
|
||||
// A plain round is untouched.
|
||||
let round = blend(&b, Blend::Round, &[TOP_FRONT], &[2.0], TOL, &[], &[], Corners::Filled).unwrap();
|
||||
assert!((ABC - volume(&round, 1e-8).unwrap() - (1.0 - PI / 4.0) * 4.0 * 30.0).abs() < 1e-6);
|
||||
}
|
||||
|
||||
/// The capi's argument checks, before any kernel call.
|
||||
|
||||
Reference in New Issue
Block a user