mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-08 13:01:52 +00:00
Merge pull request #670 from brett19/fix/cone-trim-with-domain
wire: SmCone::TrimWithDomain through the inherited SmSurfOfRevolution body
This commit is contained in:
@@ -240,6 +240,48 @@ pub struct Cone {
|
||||
pub(crate) cylinder: bool,
|
||||
}
|
||||
|
||||
/// The cone's `ConvertUVFromNURBSToSTEP` on its base parts (the virtual the
|
||||
/// inherited `UpdateAnalyticalDomain` calls); see [`Cone::convert_uv_from_nurbs_to_step`].
|
||||
pub(crate) fn cone_convert_uv_from_nurbs_to_step(
|
||||
rev: &SurfOfRevolution,
|
||||
inside_out: bool,
|
||||
k: &Kernel,
|
||||
nurbs_uv: Point2,
|
||||
) -> Result<Point2> {
|
||||
// swap NurbPoint when needed
|
||||
let step_ivl = rev.anal_uv_domain.v_interval();
|
||||
let nurb_domain = rev.base.natural_uv_domain();
|
||||
let (nurb_uv, nurb_ivl) = if rev.swap_uv {
|
||||
(
|
||||
Point2::new(nurbs_uv.y, nurbs_uv.x),
|
||||
nurb_domain.u_interval(),
|
||||
)
|
||||
} else {
|
||||
(nurbs_uv, nurb_domain.v_interval())
|
||||
};
|
||||
|
||||
// convert sweep coordinate
|
||||
let circle = rev
|
||||
.polar_curve
|
||||
.as_ref()
|
||||
.ok_or(SolidError::null_pointer(OP_TO_STEP))?;
|
||||
let (x, _exact) = rev.polar.convert_to_polar_parameter(k, circle, nurb_uv.x)?;
|
||||
|
||||
// convert linear coordinate
|
||||
let y = if rev.is_bounded()? {
|
||||
if inside_out {
|
||||
step_ivl.min() + ((nurb_uv.y - nurb_ivl.max()) / -nurb_ivl.length() * step_ivl.length())
|
||||
} else {
|
||||
step_ivl.min() + ((nurb_uv.y - nurb_ivl.min()) / nurb_ivl.length() * step_ivl.length())
|
||||
}
|
||||
} else if inside_out {
|
||||
-nurb_uv.y
|
||||
} else {
|
||||
nurb_uv.y
|
||||
};
|
||||
Ok(Point2::new(x, y))
|
||||
}
|
||||
|
||||
/// The cone's `CreateIsoParametricCurve` on its base parts (the virtual the
|
||||
/// inherited NURB rebuild calls).
|
||||
pub(crate) fn cone_iso_curve(
|
||||
@@ -1343,44 +1385,7 @@ impl Cone {
|
||||
///
|
||||
/// `SM_ERR_NULL_POINTER` without polar curve; those of the polar conversion.
|
||||
pub fn convert_uv_from_nurbs_to_step(&self, k: &Kernel, nurbs_uv: Point2) -> Result<Point2> {
|
||||
// swap NurbPoint when needed
|
||||
let step_ivl = self.rev.anal_uv_domain.v_interval();
|
||||
let nurb_domain = self.rev.base.natural_uv_domain();
|
||||
let (nurb_uv, nurb_ivl) = if self.rev.swap_uv {
|
||||
(
|
||||
Point2::new(nurbs_uv.y, nurbs_uv.x),
|
||||
nurb_domain.u_interval(),
|
||||
)
|
||||
} else {
|
||||
(nurbs_uv, nurb_domain.v_interval())
|
||||
};
|
||||
|
||||
// convert sweep coordinate
|
||||
let circle = self
|
||||
.rev
|
||||
.polar_curve
|
||||
.as_ref()
|
||||
.ok_or(SolidError::null_pointer(OP_TO_STEP))?;
|
||||
let (x, _exact) = self
|
||||
.rev
|
||||
.polar
|
||||
.convert_to_polar_parameter(k, circle, nurb_uv.x)?;
|
||||
|
||||
// convert linear coordinate
|
||||
let y = if self.rev.is_bounded()? {
|
||||
if self.inside_out {
|
||||
step_ivl.min()
|
||||
+ ((nurb_uv.y - nurb_ivl.max()) / -nurb_ivl.length() * step_ivl.length())
|
||||
} else {
|
||||
step_ivl.min()
|
||||
+ ((nurb_uv.y - nurb_ivl.min()) / nurb_ivl.length() * step_ivl.length())
|
||||
}
|
||||
} else if self.inside_out {
|
||||
-nurb_uv.y
|
||||
} else {
|
||||
nurb_uv.y
|
||||
};
|
||||
Ok(Point2::new(x, y))
|
||||
cone_convert_uv_from_nurbs_to_step(&self.rev, self.inside_out, k, nurbs_uv)
|
||||
}
|
||||
|
||||
// ---- evaluation -----------------------------------------------------------------
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
//! | [`Cone::rebuild_step_from_nurb_parameters`] | `SmCone::RebuildSTEPFromNURBParameters` |
|
||||
//! | [`ellipse_trim_intervals`] | `sm_GetEllipseTrimIntervals` (file static) |
|
||||
//! | [`Cone::reparameterize`] | `SmSurfOfRevolution::Reparameterize` (inherited; its polar converter rebuild calls the cone's iso curves) |
|
||||
//! | [`Cone::trim_with_domain`] | `SmSurfOfRevolution::TrimWithDomain` (inherited; its conversions and NURB rebuilds call the cone's virtuals) |
|
||||
//! | [`Cone::base_mut`] | `GetGwNurbPointer` (non-`const`, inherited) |
|
||||
//!
|
||||
//! Deviations:
|
||||
@@ -22,7 +23,9 @@
|
||||
//! error status through `SER` from a function returning `SmBoolean`
|
||||
//! (`IsNurbSurfaceCone`: a failed iso curve) the error is returned; upstream's caller
|
||||
//! would read that status as `TRUE` with a `NULL` cone.
|
||||
//! - **Uninitialised reads.** `sm_GetEllipseTrimIntervals` ignores the status of
|
||||
//! - **Uninitialised reads.** A failed `ConvertUVFromNURBSToSTEP` in
|
||||
//! [`Cone::trim_with_domain`] (upstream ignores the status and reads the corner it may
|
||||
//! not have written) returns the error. `sm_GetEllipseTrimIntervals` ignores the status of
|
||||
//! `smgu_LinePlaneIntersect` and then reads its parameter: a seam line parallel to
|
||||
//! the ellipse plane (or of zero length) returns the error here. A failed
|
||||
//! `ConvertUVFromNURBSToSTEP` (upstream then uses a default-constructed domain) and
|
||||
@@ -228,6 +231,26 @@ impl Cone {
|
||||
})
|
||||
}
|
||||
|
||||
/// Trims to the NURB domain `new_nurb_uv_domain` (the inherited
|
||||
/// `SmSurfOfRevolution::TrimWithDomain`): its body with the cone's
|
||||
/// `ConvertUVFromNURBSToSTEP` (in `UpdateAnalyticalDomain`) and iso curves (in
|
||||
/// `MakeNurb`, `Reparameterize` and `RebuildPolarConverter`) as its virtual calls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Those of [`crate::surface::surf_of_revolution::SurfOfRevolution::trim_with_domain`]
|
||||
/// and of [`Self::convert_uv_from_nurbs_to_step`] (upstream ignores that status and
|
||||
/// reads the corner it may not have written).
|
||||
pub fn trim_with_domain(&mut self, k: &Kernel, new_nurb_uv_domain: &Extent2d) -> Result<()> {
|
||||
let inside_out = self.inside_out;
|
||||
self.rev.trim_with_domain_using(
|
||||
k,
|
||||
new_nurb_uv_domain,
|
||||
&|s, k, uv| super::p1::cone_convert_uv_from_nurbs_to_step(s, inside_out, k, uv),
|
||||
&|s, k, p, t| Ok(super::p1::cone_iso_curve(s, inside_out, k, p, t, None, false)?.curve),
|
||||
)
|
||||
}
|
||||
|
||||
/// Rebuilds the polar converter with the cone's iso curves
|
||||
/// (`RebuildPolarConverter`, which calls the virtual `CreateIsoParametricCurve`).
|
||||
fn rebuild_cone_polar_converter(&mut self, k: &Kernel) -> Result<()> {
|
||||
@@ -1129,6 +1152,34 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trim_with_domain_keeps_the_shape() {
|
||||
let k = kernel();
|
||||
for (swap, inside) in [(false, false), (true, false), (false, true), (true, true)] {
|
||||
let mut cone = canonical(&k, 2.0, 1.0, 3.0, swap, inside);
|
||||
let d = cone.rev().base().natural_uv_domain();
|
||||
let (lo, hi) = (d.min(), d.max());
|
||||
let trim = Extent2d::from_bounds(
|
||||
lo.x + 0.25 * (hi.x - lo.x),
|
||||
lo.y + 0.125 * (hi.y - lo.y),
|
||||
lo.x + 0.75 * (hi.x - lo.x),
|
||||
lo.y + 0.5 * (hi.y - lo.y),
|
||||
);
|
||||
cone.trim_with_domain(&k, &trim).unwrap();
|
||||
let nd = cone.rev().base().natural_uv_domain();
|
||||
assert!(nd.is_contained_by(&trim, 1e-12) && trim.is_contained_by(&nd, 1e-12));
|
||||
let p = cone.rev().base().evaluate_point(trim.max()).unwrap();
|
||||
let back = cone.convert_uv_from_nurbs_to_step(&k, trim.max()).unwrap();
|
||||
let q = cone.evaluate_step_point(back).unwrap();
|
||||
assert!((p - q).length() < 1e-9);
|
||||
// the cone through the trimmed corner keeps its radius at that height
|
||||
let r = (p.x * p.x + p.y * p.y).sqrt();
|
||||
assert!((r - cone.radius(p.z)).abs() < 1e-9);
|
||||
// a domain outside the current one is rejected
|
||||
assert!(cone.trim_with_domain(&k, &d).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_non_cones() {
|
||||
let k = kernel();
|
||||
|
||||
@@ -2031,13 +2031,7 @@ impl Surface {
|
||||
SurfaceKind::OffsetSurface(s) => s.trim_with_domain(k, trim_domain),
|
||||
SurfaceKind::SrfInVolume(s) => s.trim_with_domain(k, trim_domain),
|
||||
SurfaceKind::Plane(s) => s.trim_with_domain(trim_domain),
|
||||
SurfaceKind::Cone(_) => {
|
||||
// DISPATCH GAP (#76): keep the cone's MakeNurb/UV/iso overrides during trim.
|
||||
Err(SolidError::unported(
|
||||
"SurfaceKind::Cone::TrimWithDomain callbacks",
|
||||
"surface.surface_p2",
|
||||
))
|
||||
}
|
||||
SurfaceKind::Cone(s) => s.trim_with_domain(k, trim_domain),
|
||||
SurfaceKind::Sphere(s) => s.trim_with_domain(k, trim_domain),
|
||||
SurfaceKind::Torus(s) => s.trim_with_domain(k, trim_domain),
|
||||
SurfaceKind::SurfOfRevolution(s) => s.trim_with_domain(k, trim_domain),
|
||||
|
||||
@@ -9,4 +9,4 @@ tests = ["prog_test:my_test_surface_closest_point", "prog_test:my_test_drop_poin
|
||||
status = "partial"
|
||||
claimed_by = ""
|
||||
claimed_at = ""
|
||||
notes = "G10 (PR #637, 2026-10-23): SetSurface's SmFace::Notify(SM_NO_CHANGE_GEOMETRY) wired (set_surface: empty face environment, null data; set_surface_notified: caller's environment with the surfaces' identities); Surface::split_at makes a missing extrusion NURB (GetOrCreateGwNurbPointer). difftest 2312 cases, 2202 pass, 0 failed, 110 deferred (cpp-crash-guard:UB-137 98, surface.surface_p2 12); all 209 topology.face_p3 deferrals pass. PARTIAL (PR #148): difftest 2312 cases (2260 built-in on the 15 pt_TestFiles B-reps (every op, UV trim curves as read and rebuilt; seam ops with 7 face-property variants on the periodic files) + 36 review crash reproducers + 16 generated), 807 pass bit-identical (max_rel_err 0), 0 failed, 0 both-errored (real passes 807), 1505 deferred (topology.face_p3 1100: SmFace::Notify of every edit, CreateUVTrimCurves; topology.face_p4 307: GetUpwardLoopuses/GetBrep/HasSeamProblem; cpp-crash-guard:UB-137 98: seam calls the shim keeps from upstream's SplitAtSeam crash, never comparable; the guard skips ~99.5% valid MoveSeam+split calls and must be replaced when face_p4 lands); --perturb SENSITIVE 807/807. NormalizedEvaluate/GetOrCreateUVTrimCurve wired to topology.edgeuse_p3 in PR #117 (288 deferred cases now pass). No passing case for UvDomain (with surface), Flip, SetSurface, LoopRoundTrip, RemoveUv, SwapUv, Shrink (all behind face_p3/face_p4); passes are early returns, error paths and guards. The shim normalises analytic geometry to B-splines, so MoveSeam's CopyAndAddAnalytics branch and SwapUV's conic mirrors are not compared. Seam ops take HasSeamProblem's classifications from the C++ shim (Probe op); MergeIntervals wired to intersect.curve_class_p5 (#150); IsCrossedByEdge/HasNearMisses wired to intersect.curve_class_p7 (#65). Back-edges wired: trimming_tools (FlipFaceOrientation, SplitAtSeam), edge, face_p2 points_in_face, mass.integrator (CreateOrientedTrimBoundary); not wirable yet: surface.surface_p3 RemoveUVTrimCurves of owner face (no B-rep there), proto_topology_p2 (ProtoBrep handle). Was: 14 fns; largest: SmFace::MoveSeam, SmFace::SplitAtSeam, SmFace::CreateOrientedTrimBoundary, SmFace::CalculateUVDomainFromUVTrimCurves; cycle back-edges (call into later units; stub or co-port): heal.gap(1), topology.brep_p2(1); unit-SCC #22 (220 units); 317 upstream test entry points reach this unit CalculateUVDomainFromUVTrimCurves wired into boolean.topology_intersector_p1 (PR #146). [topology.face_p3 wired (PR #139): 2312 cases, 990 pass, 0 failed, 1322 deferred (topology.face_p4 1044, topology.face_p3 180 at the justified SetSurface CHANGE_GEOMETRY Notify stub, cpp-crash-guard UB-137 98)] UPDATE after topology.brep_p2 (PR #149): the UNPORTED(topology.brep_p2) SmBrep::ReplaceSurface stub of MoveSeam is wired to topology::brep::p2::replace_surface as SmFace.cpp:17602 (ReplaceSurface(this, pJoinedSurface, FALSE, NULL, FALSE): OldSurface::Delete, no modified list, no UV trim curves); still unreached (the preceding SmFace::GetBrep is topology.face_p4): re-run 2312 cases, 519 pass, 0 fail, 1793 deferred, unchanged. Re-run after merging #145 5b1b28ae (main through e1ae2640) into #149: 2312 cases, 807 pass, 0 fail, 1505 deferred (topology.face_p3 1100, topology.face_p4 307, cpp-crash-guard:UB-137 98); ReplaceSurface still unreached. Owner zone-tol cache (surface/owner-zone-tol): 2312 cases, 1967 pass, 0 fail, 345 deferred."
|
||||
notes = "Cone TrimWithDomain (PR #670, 2026-10-23): the Cone arm of Surface::trim_with_domain runs the inherited SmSurfOfRevolution::TrimWithDomain with the cone ConvertUVFromNURBSToSTEP and iso curves (Cone::trim_with_domain); difftest 2312 cases, 2214 pass, 0 failed, 98 deferred (cpp-crash-guard:UB-137 98); the 12 surface.surface_p2 deferrals pass. G10 (PR #637, 2026-10-23): SetSurface's SmFace::Notify(SM_NO_CHANGE_GEOMETRY) wired (set_surface: empty face environment, null data; set_surface_notified: caller's environment with the surfaces' identities); Surface::split_at makes a missing extrusion NURB (GetOrCreateGwNurbPointer). difftest 2312 cases, 2202 pass, 0 failed, 110 deferred (cpp-crash-guard:UB-137 98, surface.surface_p2 12); all 209 topology.face_p3 deferrals pass. PARTIAL (PR #148): difftest 2312 cases (2260 built-in on the 15 pt_TestFiles B-reps (every op, UV trim curves as read and rebuilt; seam ops with 7 face-property variants on the periodic files) + 36 review crash reproducers + 16 generated), 807 pass bit-identical (max_rel_err 0), 0 failed, 0 both-errored (real passes 807), 1505 deferred (topology.face_p3 1100: SmFace::Notify of every edit, CreateUVTrimCurves; topology.face_p4 307: GetUpwardLoopuses/GetBrep/HasSeamProblem; cpp-crash-guard:UB-137 98: seam calls the shim keeps from upstream's SplitAtSeam crash, never comparable; the guard skips ~99.5% valid MoveSeam+split calls and must be replaced when face_p4 lands); --perturb SENSITIVE 807/807. NormalizedEvaluate/GetOrCreateUVTrimCurve wired to topology.edgeuse_p3 in PR #117 (288 deferred cases now pass). No passing case for UvDomain (with surface), Flip, SetSurface, LoopRoundTrip, RemoveUv, SwapUv, Shrink (all behind face_p3/face_p4); passes are early returns, error paths and guards. The shim normalises analytic geometry to B-splines, so MoveSeam's CopyAndAddAnalytics branch and SwapUV's conic mirrors are not compared. Seam ops take HasSeamProblem's classifications from the C++ shim (Probe op); MergeIntervals wired to intersect.curve_class_p5 (#150); IsCrossedByEdge/HasNearMisses wired to intersect.curve_class_p7 (#65). Back-edges wired: trimming_tools (FlipFaceOrientation, SplitAtSeam), edge, face_p2 points_in_face, mass.integrator (CreateOrientedTrimBoundary); not wirable yet: surface.surface_p3 RemoveUVTrimCurves of owner face (no B-rep there), proto_topology_p2 (ProtoBrep handle). Was: 14 fns; largest: SmFace::MoveSeam, SmFace::SplitAtSeam, SmFace::CreateOrientedTrimBoundary, SmFace::CalculateUVDomainFromUVTrimCurves; cycle back-edges (call into later units; stub or co-port): heal.gap(1), topology.brep_p2(1); unit-SCC #22 (220 units); 317 upstream test entry points reach this unit CalculateUVDomainFromUVTrimCurves wired into boolean.topology_intersector_p1 (PR #146). [topology.face_p3 wired (PR #139): 2312 cases, 990 pass, 0 failed, 1322 deferred (topology.face_p4 1044, topology.face_p3 180 at the justified SetSurface CHANGE_GEOMETRY Notify stub, cpp-crash-guard UB-137 98)] UPDATE after topology.brep_p2 (PR #149): the UNPORTED(topology.brep_p2) SmBrep::ReplaceSurface stub of MoveSeam is wired to topology::brep::p2::replace_surface as SmFace.cpp:17602 (ReplaceSurface(this, pJoinedSurface, FALSE, NULL, FALSE): OldSurface::Delete, no modified list, no UV trim curves); still unreached (the preceding SmFace::GetBrep is topology.face_p4): re-run 2312 cases, 519 pass, 0 fail, 1793 deferred, unchanged. Re-run after merging #145 5b1b28ae (main through e1ae2640) into #149: 2312 cases, 807 pass, 0 fail, 1505 deferred (topology.face_p3 1100, topology.face_p4 307, cpp-crash-guard:UB-137 98); ReplaceSurface still unreached. Owner zone-tol cache (surface/owner-zone-tol): 2312 cases, 1967 pass, 0 fail, 345 deferred."
|
||||
|
||||
Reference in New Issue
Block a user