mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 18:45:55 +00:00
solidcore: a variable-radius fillet is refused until solidcore builds the union of its balls
SMLib d01d7b0 builds a variable round as the union of its balls (smlib#78 6b346f6, 5db8feb, fb5c060): each ball touches the round along the circle shifted -r r'/|c'| from its great circle. solidcore's variable round still takes the great circles, a body every check passes but not that round (a linear 3 to 1 on a 20 mm edge removes 18.5998 against 18.5378). fillet_variable on solidcore now gives Error::Unported citing solidcore#2250 (port in progress as #2262, on #2260) after the capi's own checks, a pick that misses and a law with a corner (solidcore#430) still answered first. The refusal comes off when the pin carries #2262 with fillet_gn_section_generator on. Constant-radius rounds are untouched. The C++ closed form of the linear 1 to 3 law is the balls' union's, 27.857434 (integrated independently; the method gives develop's cone case 43.77766 against its 43.777573); the curvature case's solidcore twins are no longer ignored, so they are listed while refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SdcgEu4Lr3moCCisdZShKz
This commit is contained in:
@@ -660,6 +660,14 @@ 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(
|
||||
@@ -1208,27 +1216,24 @@ pub(crate) fn fillet_variable(
|
||||
if law_pinches(degree, knots, radii) {
|
||||
return Err(Error::Unsupported);
|
||||
}
|
||||
on_copy("smlib_fillet_variable", body, |k, mut w| {
|
||||
on_copy("smlib_fillet_variable", body, |k, 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 })) };
|
||||
let Some(law) = law_along(k, &w, e, start, degree, knots, radii)? else {
|
||||
if law_along(k, &w, e, start, degree, knots, radii)?.is_none() {
|
||||
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));
|
||||
}
|
||||
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),
|
||||
})
|
||||
// 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)))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -152,12 +152,13 @@ fn case(name: &str) -> Case {
|
||||
// = abc - (1 - π/4) L (r0² + r0 r1 + r1²) / 3. The round is a B-spline surface fitted to
|
||||
// those sections, which both kernels build 2.5e-7 short of it (and within 1e-9 of each
|
||||
// other), within the solver's fitting tolerance.
|
||||
// The union of the balls, r from 1 to 3 along the 30 mm edge: in each plane across the
|
||||
// edge, the area between the corner and where each ray from it first enters a ball
|
||||
// (Gauss-Legendre in the ray's angle, Simpson along the edge, converged to 1e-8 of the
|
||||
// body), 27.857434; the balls' great circles would take (1 - π/4) L (r0² + r0 r1 + r1²) / 3,
|
||||
// 27.898239.
|
||||
"fillet_variable_linear" => {
|
||||
let (r0, r1, l) = (1.0, 3.0, 30.0);
|
||||
c(
|
||||
Op::Variable(BLOCK, TOP_FRONT, [0.0, 0.0, 10.0], 1, vec![0.0, 0.0, 1.0, 1.0], vec![r0, r1]),
|
||||
Expect::Near(ABC - (1.0 - PI / 4.0) * l * (r0 * r0 + r0 * r1 + r1 * r1) / 3.0, 1e-6),
|
||||
)
|
||||
c(Op::Variable(BLOCK, TOP_FRONT, [0.0, 0.0, 10.0], 1, vec![0.0, 0.0, 1.0, 1.0], vec![1.0, 3.0]), Expect::Near(ABC - 27.857434, 1e-6))
|
||||
}
|
||||
// A round of 12 on a 10 mm cube's top front edge is larger than either face beside it:
|
||||
// the fork builds it over the cliffs, on the faces' continuations, and cuts away all of
|
||||
@@ -387,7 +388,7 @@ macro_rules! cases {
|
||||
cases!(
|
||||
fillet_one_edge,
|
||||
fillet_all_edges,
|
||||
fillet_curvature_one_edge: ignore = "solidcore refuses the curvature section until it has the equal-area Gⁿ generator (solidcore#2250)",
|
||||
fillet_curvature_one_edge,
|
||||
chamfer_one_edge,
|
||||
chamfer_two_distances,
|
||||
chamfer_distance_angle,
|
||||
@@ -677,6 +678,28 @@ 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.
|
||||
#[test]
|
||||
fn a_variable_round_is_refused_until_solidcore_has_the_balls_union() {
|
||||
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)));
|
||||
// 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.
|
||||
#[test]
|
||||
fn hostile_arguments_are_refused() {
|
||||
|
||||
Reference in New Issue
Block a user