Merge pull request #1612 from tangency-labs/fix/bspline-surface-cast-subclasses

fix: ports of the SmBSplineSurface cast accept every subclass (cone, sphere, torus, revolution, extrusion)
This commit is contained in:
bherbruck
2026-10-29 23:14:31 -04:00
committed by GitHub
6 changed files with 53 additions and 37 deletions
+6 -15
View File
@@ -105,7 +105,6 @@ use crate::kernel::Kernel;
use crate::math::{Point3, Vec3, BIG_DOUBLE, EFF_ZERO, PI};
use crate::nurbs::b_spline_curve::BSplineCurve;
use crate::nurbs::b_spline_curve_n_lib::TrimMode;
use crate::nurbs::b_spline_surface::BSplineSurface;
use crate::nurbs::b_spline_surface_n_lib::p2::{EvaluateFlags, OrientType};
use crate::nurbs::knots::Side;
use crate::surface::plane::Plane;
@@ -560,18 +559,6 @@ const fn is_b_spline_kind(c: &Curve) -> bool {
)
}
/// `SM_CAST_PTR(SmBSplineSurface, ..)`: the B-spline net of a B-spline surface, a
/// plane or a surface of revolution.
fn b_spline_net(s: &Surface) -> Option<&BSplineSurface> {
match s.kind() {
SurfaceKind::BSpline(b) => Some(b),
SurfaceKind::Plane(p) => Some(p.base()),
SurfaceKind::SurfOfRevolution(r) => Some(r.base()),
SurfaceKind::SurfOfExtrusion(r) => Some(r.base()),
_ => None,
}
}
/// `pSurface->DropCurve(crContext, crDomain, cr3dCurve, crInterval, dApproxTol,
/// rdMaxDist, rdDeviation, rUVCurves)` with the defaults (`bKeepAllDropCurves =
/// TRUE`, no fail record, `bCreatingUVTrimCurve = TRUE`): the UV curves
@@ -2430,14 +2417,18 @@ impl FilletBrep {
.ok_or_else(|| SolidError::null_pointer(OP))?
.clone();
let face_srf = &face_srf;
let surf_net = b_spline_net(face_srf).ok_or_else(|| SolidError::null_pointer(OP))?;
// SM_CAST_PTR(SmBSplineSurface, ..) and NER: every kind derived from
// SmBSplineSurface (planes, cones and cylinders, spheres, tori, surfaces of
// revolution and extrusion), with the NURB its GetDegree reads
let surf_net = crate::topology::brep::p3::with_nurb(k, face_srf)?
.ok_or_else(|| SolidError::null_pointer(OP))?;
let uv_domain = face_srf.natural_uv_domain_with_nurb(k)?;
let tol = face2::tolerance(target, face)?;
// Determine if we want to work on 2D or 3D
let mut project_and_lift = false;
let mut create_crv_on_3d = false;
let is_plane = match Plane::is_nurb_surface_plane(k, surf_net, 1.0) {
let is_plane = match Plane::is_nurb_surface_plane(k, &surf_net, 1.0) {
Ok(p) => p.is_some(),
Err(err) if err.is_unported() => return Err(err),
// an SER status inside is a TRUE return upstream
@@ -76,7 +76,7 @@ use crate::nurbs::b_spline_curve_n_lib::TrimMode;
use crate::nurbs::b_spline_surface::SurfParamType;
use crate::nurbs::b_spline_surface_n_lib::p2::EvaluateFlags;
use crate::nurbs::knots::Side;
use crate::surface::surface::p2::{drop_vectors, TsectCurveType};
use crate::surface::surface::p2::{drop_vectors, TsectCurveType, B_SPLINE_SURFACE_TYPE};
use crate::surface::surface::{Surface, SurfaceKind};
use crate::surface::surface_cache::PatchBoundaryBoundingPlanes;
@@ -268,11 +268,11 @@ impl SurfaceIntersector<'_> {
let surf2 = self.surface(SrfIndex::Second);
// Control polygons are not available for general surfaces: bail out if not
// a BSplineSurface (SM_CAST_PTR(SmBSplineSurface, ..): B-splines and planes;
// curve-bounded and STEP surfaces derive from SmSurface only)
let is_bss =
|s: &Surface| matches!(s.kind(), SurfaceKind::BSpline(_) | SurfaceKind::Plane(_));
if !is_bss(surf1) || !is_bss(surf2) {
// a BSplineSurface (SM_CAST_PTR(SmBSplineSurface, ..): every kind derived from
// it, planes, cones, spheres, tori and surfaces of revolution and extrusion;
// curve-bounded, STEP, offset and volume surfaces derive from SmSurface only)
let is_bss = |s: &Surface| s.is_kind_of(B_SPLINE_SURFACE_TYPE);
if !is_bss(surf1)? || !is_bss(surf2)? {
return Ok(BoundaryPlaneTestResult::Unknown);
}
@@ -103,7 +103,7 @@ use crate::math::{Point3, Vec3, BIG_DOUBLE, EFF_ZERO, EFF_ZERO_SQRT, PI};
use crate::query::face::p2::{
point_classify_prepared, point_classify_requested, PointClassifyOptions,
};
use crate::surface::surface::p2::{surface_cache, Surface, SurfaceKind};
use crate::surface::surface::p2::{surface_cache, Surface, B_SPLINE_SURFACE_TYPE};
use crate::surface::surface_cache::p1::{AltCacheKey, AltSrfCacheType};
use crate::surface::trim_srf_cache::POINT_CLASS_UNKNOWN;
use crate::topology::face::p2::create_curves_by_trimming_snapshot;
@@ -1970,9 +1970,9 @@ pub(super) fn face_silhouettes<O: SolutionObject>(
}
// No cached trimmed silhouette curves in existance -- create them
// (SM_CAST_PTR(SmBSplineSurface, pSurface): upstream dereferences NULL for any
// other kind)
if !matches!(srf.kind(), SurfaceKind::BSpline(_) | SurfaceKind::Plane(_)) {
// (SM_CAST_PTR(SmBSplineSurface, pSurface): every kind derived from it; upstream
// dereferences NULL for any other kind)
if !srf.is_kind_of(B_SPLINE_SURFACE_TYPE)? {
return Err(null_deref(
OP_FACE_SIL,
"face surface is not a B-spline surface",
@@ -1731,6 +1731,16 @@ impl OffsetExecutive {
SurfaceKind::SurfOfExtrusion(surface) if surface.base().gw_nurb().is_none() => {
ignore_status(surface.make_nurb(kernel))?;
}
// (SmCone/SmCylinder, SmSphere and SmTorus are SmBSplineSurfaces too)
SurfaceKind::Cone(surface) if surface.rev().base().gw_nurb().is_none() => {
ignore_status(surface.make_nurb(kernel))?;
}
SurfaceKind::Sphere(surface) if surface.base().base().gw_nurb().is_none() => {
ignore_status(surface.make_nurb(kernel))?;
}
SurfaceKind::Torus(surface) if surface.base().base().gw_nurb().is_none() => {
ignore_status(surface.make_nurb(kernel))?;
}
_ => {}
}
let original_faceuse = faceuse;
+6 -6
View File
@@ -90,7 +90,7 @@ use crate::nurbs::knots::Side;
use super::p1::{
evaluate_1st_derivatives, get_surface_cache, FindPsNormalEnfo, SurfaceSolveContext,
};
use super::p2::{LsiGlobalSolver, SurfaceEval};
use super::p2::{LsiGlobalSolver, SurfaceEval, B_SPLINE_SURFACE_TYPE};
use super::p4::is_point_on_normal;
use super::p4::NoObject;
use super::p7::{good_drop_result, load_guesses, SpGlobalSolver};
@@ -1771,9 +1771,9 @@ impl Surface {
};
let is_plane = |s: &Self| matches!(s.kind(), SurfaceKind::Plane(_));
// SM_CAST_PTR(SmBSplineSurface, ..): a plane is one too (tried before)
let is_bspline =
|s: &Self| matches!(s.kind(), SurfaceKind::BSpline(_) | SurfaceKind::Plane(_));
// SM_CAST_PTR(SmBSplineSurface, ..): every kind derived from it (a plane, tried
// before, a cone, sphere, torus or surface of revolution or extrusion)
let is_bspline = |s: &Self| s.is_kind_of(B_SPLINE_SURFACE_TYPE);
let mut tried0 = false;
let mut tried1 = false;
@@ -1842,7 +1842,7 @@ impl Surface {
}
// Next, favor SmBSplineSurfaces.
if !tried0 && is_bspline(self) {
if !tried0 && is_bspline(self)? {
tried0 = true;
if try_cover(
k,
@@ -1857,7 +1857,7 @@ impl Surface {
return Ok(done(&mut st, false));
}
}
if !tried1 && is_bspline(other) {
if !tried1 && is_bspline(other)? {
tried1 = true;
if try_cover(
k,
@@ -211,7 +211,12 @@ pub(super) fn invalid(operation: &'static str, reason: &'static str) -> SolidErr
/// `SmBSplineSurface` members: an `SmPlane` gives its B-spline part. Callers that
/// reach a virtual member deal with planes first.
pub(super) fn bspline_of(surface: &Surface) -> Option<&BSplineSurface> {
match surface.kind() {
bspline_of_kind(surface.kind())
}
/// [`bspline_of`] of a surface kind (a made kind with its NURB, say).
fn bspline_of_kind(kind: &SurfaceKind) -> Option<&BSplineSurface> {
match kind {
SurfaceKind::BSpline(b) => Some(b),
SurfaceKind::Plane(p) => Some(p.base()),
SurfaceKind::Cone(s) => Some(s.rev().base()),
@@ -775,12 +780,22 @@ impl PatchBoundaryBoundingPlanes {
let tolerance = 0.0001;
// local
let bs = bspline_of(surface).ok_or(SolidError::null_pointer(OP_PLANES))?;
// (the canonical form asks for the NURB through the virtual GetOrCreateGwNurbPointer,
// which makes a plane's)
let bs = match surface.kind() {
bspline_of(surface).ok_or(SolidError::null_pointer(OP_PLANES))?;
// (the control point count and the canonical form read the NURB, made through
// the virtual GetOrCreateGwNurbPointer when missing: a plane's, a cone's,
// sphere's, torus's or surface of revolution's or extrusion's)
let bs = match surface.kind_with_nurb(k)? {
SurfaceKind::Plane(p) => p.base_for_query(),
_ => std::borrow::Cow::Borrowed(bs),
SurfaceKind::SurfOfExtrusion(_) => match surface.extrusion_base_with_nurb(k) {
Ok(base) => base,
Err(e) if e.is_unported() => return Err(e),
Err(_) => std::borrow::Cow::Borrowed(
bspline_of(surface).ok_or(SolidError::null_pointer(OP_PLANES))?,
),
},
made => std::borrow::Cow::Borrowed(
bspline_of_kind(made).ok_or(SolidError::null_pointer(OP_PLANES))?,
),
};
// for every boundary