diff --git a/crates/solidcore/src/curve/curve/p1.rs b/crates/solidcore/src/curve/curve/p1.rs index 5af4cfd982..6fe2767337 100644 --- a/crates/solidcore/src/curve/curve/p1.rs +++ b/crates/solidcore/src/curve/curve/p1.rs @@ -78,16 +78,16 @@ //! are wired; [`SolidError::Unported`] is propagated from what they reach. //! - **Statuses passed on (WR-663).** With the opt-in //! `Kernel::fixes.query_statuses_pass_on` the statuses SMLib drops are passed on, as the -//! fork (tangency-labs/smlib 8311f8e): `sm_RefineCCSolutions` (its local intersections -//! and `Inversion`s), the degenerate `ApproximateCurve`'s `EditParameterization`, +//! fork (tangency-labs/smlib 8311f8e): `sm_RefineCCSolutions` (its local intersections; +//! its `Inversion`s not, tangency-labs/smlib 8ea6a20), the degenerate `ApproximateCurve`'s `EditParameterization`, //! `ClassifyIntersectingRangeSolution`, `SmCPGlobalSolver::LocalSolve` and //! `GlobalPointSolve`'s `SolveIt`. use crate::curve::curve::knots_ignoring_status; use crate::curve::curve::p2::{ - add_to_curve, evaluate_continuity, find_test_params, flush_crv, inversion_passed_on, output, - pop_stack, proj_signed_angle, push_point, stack_look, test_step, CpGlobalSolver, Curve, - CurveKind, CurvePropertyType, TessStepResult, BSPLINE_CURVE_TYPE, CRV_ON_SURF_TYPE, + add_to_curve, evaluate_continuity, find_test_params, flush_crv, output, pop_stack, + proj_signed_angle, push_point, stack_look, test_step, CpGlobalSolver, Curve, CurveKind, + CurvePropertyType, TessStepResult, BSPLINE_CURVE_TYPE, CRV_ON_SURF_TYPE, }; use crate::curve::curve::p3::{eval_n, local_curve_intersect, PointSolveOptions}; use crate::curve::curve::p4::proj_dist; @@ -249,9 +249,13 @@ where if x.deviation < end.solution_value { // Do not allow big shifts. [140915b] let inv1 = sol_ivl1.inversion(x.my_param); - inversion_passed_on(&inv1, pass_on)?; let inv2 = sol_ivl2.inversion(x.other_param); - inversion_passed_on(&inv2, pass_on)?; + // The fork (tangency-labs/smlib 8ea6a20): a refined parameter outside the + // range is an answer, which the shift limit judges. Only a zero-length + // range gives no fraction, and then the end stays where it is. + if pass_on && (inv1.parameter.is_none() || inv2.parameter.is_none()) { + continue; + } let mut frac1 = inv1.parameter.unwrap_or(UNDEF_DOUBLE); let mut frac2 = inv2.parameter.unwrap_or(UNDEF_DOUBLE); if reverse1 { diff --git a/crates/solidcore/src/curve/curve/p2.rs b/crates/solidcore/src/curve/curve/p2.rs index 059e420bfd..f33fbfdef0 100644 --- a/crates/solidcore/src/curve/curve/p2.rs +++ b/crates/solidcore/src/curve/curve/p2.rs @@ -201,12 +201,12 @@ use crate::curve::offset_curve::OffsetCurve; use crate::curve::parabola::Parabola; use crate::curve::projected_curve::ProjectedCurve; use crate::curve::tangent_field::TangentFieldCurve; -use crate::error::{ignore_or_pass_on, ignored, ignored_or_pass_on, Result, SolidError, SM_ERR}; +use crate::error::{ignore_or_pass_on, ignored, ignored_or_pass_on, Result, SolidError}; use crate::geom::a_object::{self, AObject, CacheStore}; use crate::geom::attribute::{AttributeStore, ObjectKey}; use crate::geom::axis2_placement::Axis2Placement; use crate::geom::context::{Context, IdentitySlot}; -use crate::geom::extent1d::{Extent1d, Inversion}; +use crate::geom::extent1d::Extent1d; use crate::geom::extent2d::Extent2d; use crate::geom::extent3d::Extent3d; use crate::geom::geom_utility::p1::{ @@ -2281,22 +2281,6 @@ pub fn base_ends(k: &Kernel, curve: &C) -> Result Result<()> { - if pass_on && !inv.is_success() { - return Err(SolidError::Upstream { - operation: "geom::extent1d::Extent1d::inversion", - code: SM_ERR, - entity: None, - }); - } - Ok(()) -} - /// The outcome of [`ignored_or_soft`]. #[derive(Debug)] pub(crate) enum Soft { @@ -4477,19 +4461,6 @@ mod tests { b } - #[test] - fn issue_2009_inversion_status_is_passed_on() { - // tangency-labs/smlib 8311f8e (sm_RefineCCSolutions `SER(Inversion)`): a value - // outside the interval (or one that a zero-length interval cannot map) is - // SM_ERR with the opt-in fix and dropped without it. - let ivl = Extent1d::from_min_max(0.0, 1.0); - assert!(inversion_passed_on(&ivl.inversion(0.5), true).is_ok()); - assert!(inversion_passed_on(&ivl.inversion(2.0), true).is_err()); - assert!(inversion_passed_on(&ivl.inversion(2.0), false).is_ok()); - let flat = Extent1d::from_min_max(1.0, 1.0); - assert!(inversion_passed_on(&flat.inversion(2.0), true).is_err()); - } - #[test] fn evaluation_copy_keeps_nested_knots_flags_and_owners() { use crate::curve::projected_curve::OwnsCurve; diff --git a/crates/solidcore/tests/issue_948_ruled_skin_difference.rs b/crates/solidcore/tests/issue_948_ruled_skin_difference.rs index aeba64a5e0..a03247f6bb 100644 --- a/crates/solidcore/tests/issue_948_ruled_skin_difference.rs +++ b/crates/solidcore/tests/issue_948_ruled_skin_difference.rs @@ -105,3 +105,27 @@ fn issue_948_difference_of_two_ruled_skins_fails_as_upstream() { ), } } + +/// WR-663 with the fork's tangency-labs/smlib@8ea6a20: a refined end past a curve/curve range +/// no longer aborts the intersection. The difference still fails, as the pinned C++ and the +/// fork do, but where the default path does, not in `Extent1d::inversion`. +#[test] +fn a_refined_parameter_outside_the_range_does_not_abort_the_intersection() { + let data = sections(MIN7); + let mut k = Kernel::default(); + k.fixes.query_statuses_pass_on = true; + let outer = ruled_skin(&mut k, &data[0].1); + let inner = ruled_skin(&mut k, &data[1].1); + match boolean_difference(&mut k, outer, inner) { + Err(SolidError::Upstream { + operation, code, .. + }) => { + assert_eq!(code, SM_ERR); + assert_eq!(operation, "topology::brep::p6"); + } + other => panic!( + "expected the default path's SM_ERR, got {:?}", + other.map(|_| "a body") + ), + } +} diff --git a/plan/upstream-issues/WR-663--addendum-2026-11-01-refined-parameter-outside-the-range.md b/plan/upstream-issues/WR-663--addendum-2026-11-01-refined-parameter-outside-the-range.md new file mode 100644 index 0000000000..9909e4cd41 --- /dev/null +++ b/plan/upstream-issues/WR-663--addendum-2026-11-01-refined-parameter-outside-the-range.md @@ -0,0 +1,9 @@ +Updates: WR-663 + +#### WR-663 (update, 2026-11-01). A refined parameter outside the range is an answer, not a failed status +- **What changes:** with `Kernel::fixes.query_statuses_pass_on`, `sm_RefineCCSolutions` no longer returns the status of `Inversion`. A refined end just past the solution's range keeps its fraction, and the shift limit judges it. Only a zero-length range gives no fraction, and then the end stays where it was. With the flag off nothing changes. +- **How found:** this site took 8311f8e's `SER(Inversion)` from the fork, but not the fork's later tangency-labs/smlib@8ea6a20 (9b93b88 before cherry-picking), which takes it back out. The fork at d01d7b0 has `(void)sSolIvl1.Inversion(..)` and `bHaveFracs`. The port's `SM_ERR` aborted the whole curve/curve intersection as `geom::extent1d::Extent1d::inversion`. +- **Confirmed:** in the Rust port. + - #948's `min7.txt`: with this flag alone the difference failed in `Extent1d::inversion`, earlier than the default path. It now fails where the default path does, in `topology::brep::p6`. The fork fails that difference with 1001 too, so this does not fix #948. + - The order-2 round of the pipe on a plate (#2252's `pipe_on_plate_g2`), on #2260's head 9f3cdcfbc with every fix on except `geometric_g2_continuity`: `Extent1d::inversion` 1001 before, built after. Volume 40258.543608517 against the fork case's 40258.5435962825 (3.0e-10 relative), seams G2. That case needs #2260's generator, so it is not a test here. + - The fork's C++ was run only as far as #948's status (d01d7b0 built from `source/SMLib` and `source/SM_API`).