mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 10:35:58 +00:00
smlib: circle, cylinder and torus NURBS forms come into solidcore (solidcore#1432, #1433)
from_arrays now runs SMLib's own tests where the capi's flat import does (capi/src/smlib_flat.cpp): - MakeEdgeCurve: a circle edge given with its NURBS form must pass SmCircle::IsNurbCurveCircle (Ellipse::is_nurb_curve_circle); the circle kept is the one of its canonical entry's frame and radius, its domain from ConicDomainOfNurbs, holding that NURBS (SameNurbsCurve, ported); any failure is SM_ERR_INVALID_INPUT. - AnalyticOfNurbs: SmCylinder::IsNurbSurfaceCylinder and SmTorus::IsNurbSurfaceTorus, kept only when their NURBS is the face's. The cylinder, frustum and torus now round-trip (same record within 1e-12, volume exact). The cone and sphere records come back the same, but their volumes still miss: the cone's on solidcore#1435 (1.4e-9 at 1e-8), the sphere's on #1437 (0); those stay ignored on those issues. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SdcgExNEwfR9CX2RAzp2Pt
This commit is contained in:
@@ -3,8 +3,7 @@
|
||||
//! faces by surface kind, or the same error (parity only: the expected values are the analytic
|
||||
//! ones).
|
||||
//!
|
||||
//! The bodies are solidcore's own boxes and cylinders: a record's curved surfaces do not come
|
||||
//! into solidcore yet (solidcore#1432, #1433).
|
||||
//! The bodies are solidcore's own boxes and cylinders.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::f64::consts::PI;
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
//! gives the same volume, counts, faces by surface kind and solids (parity only: the expected
|
||||
//! values are the analytic ones).
|
||||
//!
|
||||
//! Curved operands are built on solidcore itself: a record's circles, cylinders and tori do not
|
||||
//! come into solidcore yet (solidcore#1432, #1433).
|
||||
//! Curved operands are built on solidcore itself.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::f64::consts::PI;
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
//! `SmBrep::MakeTopologyFromData`, healing unless the arrays say SMLib wrote them).
|
||||
//!
|
||||
//! Where solidcore has no port of an SMLib call the C++ makes, the conversion fails with
|
||||
//! "not supported yet" naming the call: `SmCylinder::IsNurbSurfaceCylinder`,
|
||||
//! `SmTorus::IsNurbSurfaceTorus` and `SmCircle::IsNurbCurveCircle` (an analytic face or circle
|
||||
//! edge given with its NURBS form), `SmSurface::ApproximateSurface` (a face whose surface is
|
||||
//! "not supported yet" naming the call: `SmSurface::ApproximateSurface` (a face whose surface is
|
||||
//! not a B-spline kind) and `SmShell::IsInnerShellOf` (`PutEnclosingShellsFirst`, a region of
|
||||
//! several shells).
|
||||
|
||||
@@ -1937,9 +1935,9 @@ fn analytic_of_nurbs(k: &Kernel, nurbs: &BSplineSurface, ty: i64, tol: f64) -> R
|
||||
} else if ty == SURFACE_SPHERE_I {
|
||||
answer(Sphere::is_nurb_surface_sphere(k, nurbs, 1.0)).map_err(sc("SmSphere::IsNurbSurfaceSphere"))?.flatten().map(Surface::from)
|
||||
} else if ty == SURFACE_CYLINDER_I {
|
||||
return Err(unported("SmCylinder::IsNurbSurfaceCylinder"));
|
||||
answer(Cone::is_nurb_surface_cylinder(k, nurbs, 1.0)).map_err(sc("SmCylinder::IsNurbSurfaceCylinder"))?.flatten().map(Surface::from)
|
||||
} else if ty == SURFACE_TORUS_I {
|
||||
return Err(unported("SmTorus::IsNurbSurfaceTorus"));
|
||||
answer(Torus::is_nurb_surface_torus(k, nurbs, 1.0)).map_err(sc("SmTorus::IsNurbSurfaceTorus"))?.flatten().map(Surface::from)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -1955,22 +1953,63 @@ fn placement(origin: Point3, z: Vec3, x: Vec3) -> Result<Axis2Placement> {
|
||||
Ok(p)
|
||||
}
|
||||
|
||||
/// `MakeEdgeCurve`: one edge's 3D curve, as its kind says; `d_min`/`d_max` its range in SMLib
|
||||
/// units. A circle or ellipse with its NURBS form (`form`) is that NURBS as the conic of its
|
||||
/// canonical entry, which SMLib's own circle test must accept.
|
||||
/// `SameNurbsCurve`: equal counts and orders, knots and weights within rounding, control points
|
||||
/// within rounding of the curve's size (`smu_AddSmBSplineCurve3d` of each).
|
||||
fn same_nurbs_curve(a: &NurbCurve, b: &NurbCurve) -> bool {
|
||||
let arrays = |n: &NurbCurve| {
|
||||
let (mut count, mut order, mut cvs, mut knots, mut weights) = (Vec::new(), Vec::new(), Vec::new(), Vec::new(), Vec::new());
|
||||
let o = NurbsOut { count: &mut count, order: &mut order, cvs: &mut cvs, knots: &mut knots, weights: &mut weights };
|
||||
add_bspline_curve_3d(n, o).ok().map(|()| (count, order, cvs, knots, weights))
|
||||
};
|
||||
let (Some((ac, ao, ap, ak, aw)), Some((bc, bo, bp, bk, bw))) = (arrays(a), arrays(b)) else { return false };
|
||||
if ac != bc || ao != bo || ap.len() != bp.len() || ak.len() != bk.len() || aw.len() != bw.len() {
|
||||
return false;
|
||||
}
|
||||
let size = ap.iter().fold(1.0_f64, |m, x| m.max(x.abs()));
|
||||
let near = |x: &[f64], y: &[f64]| x.iter().zip(y).all(|(a, b)| (a - b).abs() <= 1e-12 * a.abs().max(1.0));
|
||||
ap.iter().zip(&bp).all(|(a, b)| (a - b).abs() <= 1e-12 * size) && near(&ak, &bk) && near(&aw, &bw)
|
||||
}
|
||||
|
||||
/// `MakeEdgeCurve` (capi/src/smlib_flat.cpp): one edge's 3D curve, as its kind says;
|
||||
/// `d_min`/`d_max` its range in SMLib units. A circle or ellipse with its NURBS form (`form`) is that NURBS as the conic of its
|
||||
/// canonical entry (frame and radii; the domain from `ConicDomainOfNurbs`), keeping that NURBS.
|
||||
/// A circle's NURBS must pass SMLib's own test (`SmCircle::IsNurbCurveCircle`), whose circle is
|
||||
/// not the one kept (its frame has its x axis towards the NURBS's start), and the kept circle
|
||||
/// must hold the same NURBS (`SameNurbsCurve`); either failing is `SM_ERR_INVALID_INPUT`.
|
||||
fn make_edge_curve(k: &Kernel, ty: i64, d_min: f64, d_max: f64, e: &EdgeIn<'_>, c: &mut Cursor, form: bool) -> Result<Curve> {
|
||||
if form {
|
||||
if ty == CURVE_CIRCLE_I {
|
||||
return Err(unported("SmCircle::IsNurbCurveCircle"));
|
||||
}
|
||||
let i = c.ellipse;
|
||||
c.ellipse += 1;
|
||||
let circle = ty == CURVE_CIRCLE_I;
|
||||
let i = if circle {
|
||||
c.circle += 1;
|
||||
c.circle - 1
|
||||
} else {
|
||||
c.ellipse += 1;
|
||||
c.ellipse - 1
|
||||
};
|
||||
let nurbs = bspline_curve_3d(e, c)?;
|
||||
let o = get3(e.ellipse_center, i)?;
|
||||
let (mut z, mut x) = (get3(e.ellipse_axis, i)?, get3(e.ellipse_ref, i)?);
|
||||
let (rx, ry) = (get(e.ellipse_x, i)?, get(e.ellipse_y, i)?);
|
||||
let domain = conic_domain_of_nurbs(&nurbs, o, &mut z, &mut x, rx, ry).ok_or_else(|| bad("an ellipse NURBS off its ellipse"))?;
|
||||
let (o, mut z, mut x, rx, ry) = if circle {
|
||||
let r = get(e.circle_radius, i)?;
|
||||
(get3(e.circle_center, i)?, get3(e.circle_axis, i)?, get3(e.circle_ref, i)?, r, r)
|
||||
} else {
|
||||
(get3(e.ellipse_center, i)?, get3(e.ellipse_axis, i)?, get3(e.ellipse_ref, i)?, get(e.ellipse_x, i)?, get(e.ellipse_y, i)?)
|
||||
};
|
||||
if circle {
|
||||
let test = Curve::from(nurbs.clone_exact());
|
||||
let is_circle = answer(Ellipse::is_nurb_curve_circle(k, &test, None)).map_err(sc("SmCircle::IsNurbCurveCircle"))?.flatten();
|
||||
if is_circle.is_none() {
|
||||
return Err(bad("a circle NURBS SMLib's circle test rejects"));
|
||||
}
|
||||
}
|
||||
let domain = conic_domain_of_nurbs(&nurbs, o, &mut z, &mut x, rx, ry).ok_or_else(|| bad("a conic NURBS off its conic"))?;
|
||||
let opts = EllipseOptions { dim: 3, nurb: Some(&nurbs), inside_out: false };
|
||||
if circle {
|
||||
let cir = Ellipse::new_circle(k, o, x, z.cross(x), domain, rx, &opts).map_err(sc("SmCircle::SmCircle"))?;
|
||||
let kept = nurb_of(cir.base())?;
|
||||
if !nurbs.gw_nurb().is_some_and(|want| same_nurbs_curve(want, kept)) {
|
||||
return Err(bad("a circle that does not keep its NURBS"));
|
||||
}
|
||||
return Ok(Curve::from(cir));
|
||||
}
|
||||
let el = Ellipse::new(k, o, x, z.cross(x), domain, rx, ry, &opts).map_err(sc("SmEllipse::SmEllipse"))?;
|
||||
return Ok(Curve::from(el));
|
||||
}
|
||||
|
||||
@@ -158,13 +158,14 @@ macro_rules! primitive {
|
||||
}
|
||||
|
||||
primitive!(box_, "box");
|
||||
primitive!(cylinder, "cylinder", round_trip: ignore = "solidcore#1433, #1432: no SmCylinder::IsNurbSurfaceCylinder (nor SmCircle::IsNurbCurveCircle)");
|
||||
primitive!(cylinder, "cylinder");
|
||||
primitive!(sphere, "sphere",
|
||||
volume: ignore = "solidcore#1437: trim_boundary_seam_wrap zeroes a sphere's volume",
|
||||
round_trip: ignore = "solidcore#1432: no SmCircle::IsNurbCurveCircle");
|
||||
primitive!(cone, "cone", round_trip: ignore = "solidcore#1432: no SmCircle::IsNurbCurveCircle");
|
||||
primitive!(frustum, "frustum", round_trip: ignore = "solidcore#1432: no SmCircle::IsNurbCurveCircle");
|
||||
primitive!(torus, "torus", round_trip: ignore = "solidcore#1433, #1432: no SmTorus::IsNurbSurfaceTorus (nor SmCircle::IsNurbCurveCircle)");
|
||||
round_trip: ignore = "solidcore#1437: the record comes back the same, but trim_boundary_seam_wrap zeroes its volume");
|
||||
primitive!(cone, "cone",
|
||||
round_trip: ignore = "solidcore#1435: the record comes back the same, but its volume at 1e-8 is 1.4e-9 off (the C++ kernel 1.1e-11 on the same arrays); the side face's precise properties do not split at the surface's knots, fork 08d9a14 (solidcore PR #1456)");
|
||||
primitive!(frustum, "frustum");
|
||||
primitive!(torus, "torus");
|
||||
|
||||
#[test]
|
||||
fn hostile_arrays_are_refused() {
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
//! kernel gives the same volume (or, for a sheet, area), counts and faces by surface kind, or the
|
||||
//! same error (parity only: the expected values are the analytic ones).
|
||||
//!
|
||||
//! The bodies are solidcore's own boxes, cylinders and linear sweeps (and booleans of them): a
|
||||
//! record's curved surfaces do not come into solidcore yet (solidcore#1432, #1433).
|
||||
//! The bodies are solidcore's own boxes, cylinders and linear sweeps (and booleans of them).
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::f64::consts::PI;
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
//! kernel gives the same volume (or, for a sheet, area), counts and faces by surface kind, or the
|
||||
//! same error (parity only: the expected values are the analytic ones).
|
||||
//!
|
||||
//! The profiles are built straight into solidcore curves from the curve lists (a record's curved
|
||||
//! surfaces do not come into solidcore yet, solidcore#1432, #1433).
|
||||
//! The profiles are built straight into solidcore curves from the curve lists.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::f64::consts::{FRAC_PI_2, PI, TAU};
|
||||
|
||||
Reference in New Issue
Block a user