mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-08 13:01:52 +00:00
port: surface.plane_p1 (SmPlane part 1, partial)
Plane kind of Surface with constructors, evaluation, projection, MakeNurb, DropPointFast, GlobalLineIntersect, TrimPlaneLineToPlaneDomain, CreateIsoParametricCurve, DropCurve, CoincidenceCheck, Reparameterize, ToggleSwapUVBit, RebuildSTEPFromNURBParameters and the re-dispatched SmBSplineSurface methods; C++ shim, ffi binding, difftest runner and fixtures. Wires the UNPORTED(surface.plane_p1) stubs in nurbs.b_spline_surface_p1/p2. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8ohWGLCzFPoZriCeSSero
This commit is contained in:
@@ -778,7 +778,9 @@ fn st<T>(r: solidcore::Result<T>) -> solidcore::Result<f64> {
|
||||
fn the_cbs(s: &Surface) -> solidcore::Result<&CurveBoundedSurface> {
|
||||
match s.kind() {
|
||||
SurfaceKind::CurveBounded(c) => Ok(c),
|
||||
SurfaceKind::BSpline(_) => Err(SolidError::sm_err("difftest: not a curve bounded surface")),
|
||||
SurfaceKind::BSpline(_) | SurfaceKind::Plane(_) => {
|
||||
Err(SolidError::sm_err("difftest: not a curve bounded surface"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1112,7 +1112,7 @@ fn rust_op(i: &Inputs) -> Result<Run, HarnessError> {
|
||||
ProgIn::FirstPatch { node } => {
|
||||
let own = match cache.surface().kind() {
|
||||
SurfaceKind::BSpline(b) => b.gw_nurb(),
|
||||
SurfaceKind::CurveBounded(_) => None,
|
||||
SurfaceKind::CurveBounded(_) | SurfaceKind::Plane(_) => None,
|
||||
};
|
||||
match cache.first_bezier_patch(NodeId(*node)) {
|
||||
Ok(None) => o.push(0.0),
|
||||
|
||||
@@ -1077,7 +1077,7 @@ fn dump_state(o: &mut Vec<f64>, s: Option<&Surface>) {
|
||||
match s.map(Surface::kind) {
|
||||
Some(sp2::SurfaceKind::BSpline(b)) => dump_bss(o, b),
|
||||
// `put_state` of a surface that is not a B-spline
|
||||
Some(sp2::SurfaceKind::CurveBounded(_)) | None => o.push(0.0),
|
||||
Some(sp2::SurfaceKind::CurveBounded(_) | sp2::SurfaceKind::Plane(_)) | None => o.push(0.0),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,691 @@
|
||||
//! FFI wrappers for unit `surface.plane_p1`.
|
||||
//!
|
||||
//! Shim: `harness/cpp/shim/surface_plane_p1.cpp` (`sc_surface_plane_p1_run`). Upstream:
|
||||
//! `source/SMLib/src/SmPlane.cpp`, `source/SMLib/inc/SmPlane.h` (`SmPlane`).
|
||||
//!
|
||||
//! [`run`] builds plane A from a [`Make`], runs the [`Op`]s on it in order and returns
|
||||
//! the record `make_status`, then per op `op status [values]` (values only for status
|
||||
//! 0), then the state of A (nothing after a failed construction). Statuses are `0`
|
||||
//! (`SM_SUCCESS`) or the `SmStatus`.
|
||||
//!
|
||||
//! A plane state is `type O[3] X[3] Y[3] Z[3] domain[4] scale[2] oob knot_type form`
|
||||
//! then the NURB `has [n m p q r s cpts(x y z w) u v]` (the plane's, or the one a copy
|
||||
//! makes when it has none). A curve is `type dim [P[3] V[3] dom[2] scale inside_out]`
|
||||
//! (the bracket for lines) then `knot_type form has [n p m cpts(x y z w) knots]` and the
|
||||
//! natural interval. Solutions are `count` then per solution `type nvars nobjs obj0
|
||||
//! start_value start[3] end_value end[3]` (objects: `0` = A, `1` = another, `-1` =
|
||||
//! none; unused parameters and the end of a single value are `0`).
|
||||
//!
|
||||
//! | [`Op`] | upstream | values |
|
||||
//! |-----------------|-----------------------------------------------|--------|
|
||||
//! | `State` | getters | state |
|
||||
//! | `Evaluate` | `Evaluate(uv, hu, hv, TRUE, TRUE, upper)` | `(hu+1)(hv+1)` vectors, `D[i][j]` row by row |
|
||||
//! | `EvalPoint`, `EvalFast` | `EvaluatePoint`, `EvaluatePointFast` | point |
|
||||
//! | `Normal` | `EvaluateNormal(uv, TRUE, TRUE)` | vector |
|
||||
//! | `Project` | `ProjectPointToUVDomain` | uv |
|
||||
//! | `Queries` | `IsBounded`, `IsPlanar`, `IsOnSeam`, `IsClosed`, both `IsSingularity`, `GetType`, `GetTypeString`, `GetClassString` | values, type chars, `-1`, class chars |
|
||||
//! | `KindOf` | `IsKindOf` | flag |
|
||||
//! | `Adjust`, `MakeNurb`, `Reparam`, `Toggle`, `Rebuild`, `Reverse`, `Swap` | `AdjustSTEPUVDomain`, `MakeNurb`, `Reparameterize`, `ToggleSwapUVBit`, `RebuildSTEPFromNURBParameters`, `Reverse`, `SwapUV` | |
|
||||
//! | `Drop` | `DropPointFast(dom, op, p, NULL, tol, target, SM_SR_ALL)` | solutions |
|
||||
//! | `Line` | `GlobalLineIntersect` | solutions |
|
||||
//! | `TrimLine` | `TrimPlaneLineToPlaneDomain` | `found [start end]` |
|
||||
//! | `Iso` | `CreateIsoParametricCurve` | curve, gap, `has [uv curve]` |
|
||||
//! | `DropCurve` | `DropCurve` | `max_drop max_dev count curves` |
|
||||
//! | `Coincidence` | `CoincidenceCheck` | `flag max_dist` |
|
||||
//! | `Eq` | `operator==` | flag |
|
||||
//! | `Copy` | `Copy` | state |
|
||||
//! | `Grazing` | `sm_IsGrazingIntersection` (file static, copied into the shim) | flag |
|
||||
//! | `Inherited` | of a copy: `GetNaturalUVDomain`, `GetMaxAnalyticDomain`, `IsBilinear`, `IsParallelToVector(v)`, `GetDegree` and `GetNumberControlPoints` (u, v) | values |
|
||||
//!
|
||||
//! [`run`] accepts only finite inputs of magnitude at most [`MAX_COORD`], well-formed
|
||||
//! clamped B-splines (at most [`MAX_INDEX`] + 1 control points per direction, degree
|
||||
//! at most [`MAX_DEGREE`], positive weights or none) and at most [`MAX_OPS`] ops.
|
||||
|
||||
use crate::{call_with_buffer, FfiError};
|
||||
|
||||
mod sys {
|
||||
extern "C" {
|
||||
pub fn sc_surface_plane_p1_run(
|
||||
input: *const f64,
|
||||
n_in: i64,
|
||||
out: *mut f64,
|
||||
cap: i64,
|
||||
out_len: *mut i64,
|
||||
) -> i32;
|
||||
}
|
||||
}
|
||||
|
||||
/// Largest control point index `n` (curves: 32, surfaces: 16 per direction).
|
||||
pub const MAX_INDEX: usize = 16;
|
||||
/// Largest degree.
|
||||
pub const MAX_DEGREE: usize = 8;
|
||||
/// Largest input magnitude.
|
||||
pub const MAX_COORD: f64 = 1e9;
|
||||
/// Most ops per program.
|
||||
pub const MAX_OPS: usize = 64;
|
||||
/// `NL_NOW`: the weight of a non-rational control point.
|
||||
pub const NOW: f64 = -f64::MAX;
|
||||
|
||||
/// How plane A (or another plane) is built.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Make {
|
||||
/// `SmPlane(o, x, y, SmVector2d(scale), SmExtent2d(domain), ctx)`.
|
||||
Bounded {
|
||||
/// Origin.
|
||||
o: [f64; 3],
|
||||
/// X axis.
|
||||
x: [f64; 3],
|
||||
/// Y axis.
|
||||
y: [f64; 3],
|
||||
/// u and v scales.
|
||||
scale: [f64; 2],
|
||||
/// `umin vmin umax vmax`, stored as given.
|
||||
domain: [f64; 4],
|
||||
},
|
||||
/// `SmPlane(o, normal, ctx)`.
|
||||
Normal {
|
||||
/// Origin.
|
||||
o: [f64; 3],
|
||||
/// Normal.
|
||||
n: [f64; 3],
|
||||
},
|
||||
/// `SmPlane::CreateCanonical(ctx, SmAxis2Placement(o, x, y), p)`.
|
||||
Canonical {
|
||||
/// Origin.
|
||||
o: [f64; 3],
|
||||
/// X axis.
|
||||
x: [f64; 3],
|
||||
/// Y axis.
|
||||
y: [f64; 3],
|
||||
},
|
||||
}
|
||||
|
||||
/// A domain operand.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Dom {
|
||||
/// `GetSTEPUVDomain()` at op time.
|
||||
Step,
|
||||
/// `SmExtent2d(umin, vmin, umax, vmax)`.
|
||||
Explicit([f64; 4]),
|
||||
/// `GetNaturalUVDomain()` of a copy at op time.
|
||||
Natural,
|
||||
}
|
||||
|
||||
/// A clamped B-spline curve: `SmBSplineCurve(dim, n, m, p, Pw, U)`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct CurveData {
|
||||
/// Image dimension, 2 or 3.
|
||||
pub dim: u32,
|
||||
/// Degree.
|
||||
pub p: usize,
|
||||
/// Control points as stored (`x y z w`, `w` = [`NOW`] when non-rational).
|
||||
pub cpts: Vec<[f64; 4]>,
|
||||
/// Knots (`n + p + 2` of them).
|
||||
pub knots: Vec<f64>,
|
||||
}
|
||||
|
||||
/// A curve operand.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum CurveIn {
|
||||
/// `SmLine::CreateLineSegment(ctx, 3, a, b, l)`.
|
||||
Segment([f64; 3], [f64; 3]),
|
||||
/// A B-spline curve.
|
||||
BSpline(CurveData),
|
||||
}
|
||||
|
||||
/// A clamped B-spline surface: `SmBSplineSurface(gw_SURFACE*, FALSE, ctx)`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct SurfaceData {
|
||||
/// u degree.
|
||||
pub p: usize,
|
||||
/// v degree.
|
||||
pub q: usize,
|
||||
/// Control net, row `i` (u index) of `x y z w` points.
|
||||
pub net: Vec<Vec<[f64; 4]>>,
|
||||
/// u knots.
|
||||
pub u: Vec<f64>,
|
||||
/// v knots.
|
||||
pub v: Vec<f64>,
|
||||
}
|
||||
|
||||
/// The other surface of a coincidence test.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum SurfaceIn {
|
||||
/// A plane.
|
||||
Plane(Box<Make>),
|
||||
/// A B-spline surface.
|
||||
BSpline(SurfaceData),
|
||||
}
|
||||
|
||||
/// One operation on plane A.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Op {
|
||||
/// The state of A.
|
||||
State,
|
||||
/// `Evaluate(uv, hu, hv, TRUE, TRUE, upper)`.
|
||||
Evaluate {
|
||||
/// Parameter.
|
||||
uv: [f64; 2],
|
||||
/// Highest u derivative (at most 4).
|
||||
hu: usize,
|
||||
/// Highest v derivative (at most 4).
|
||||
hv: usize,
|
||||
/// Upper half only (requires `hu <= hv`).
|
||||
upper: bool,
|
||||
},
|
||||
/// `EvaluatePoint(uv)`.
|
||||
EvalPoint([f64; 2]),
|
||||
/// `EvaluatePointFast(uv)`.
|
||||
EvalFast([f64; 2]),
|
||||
/// `EvaluateNormal(uv, TRUE, TRUE)`.
|
||||
Normal([f64; 2]),
|
||||
/// `ProjectPointToUVDomain(p)`.
|
||||
Project([f64; 3]),
|
||||
/// The simple queries at `uv`.
|
||||
Queries([f64; 2]),
|
||||
/// `IsKindOf(t)`.
|
||||
KindOf(u32),
|
||||
/// `AdjustSTEPUVDomain(dom)`.
|
||||
Adjust(Dom),
|
||||
/// `MakeNurb()`.
|
||||
MakeNurb,
|
||||
/// `Reparameterize(dom)`.
|
||||
Reparam(Dom),
|
||||
/// `ToggleSwapUVBit()`.
|
||||
Toggle,
|
||||
/// `RebuildSTEPFromNURBParameters()`.
|
||||
Rebuild,
|
||||
/// `DropPointFast(dom, op, p, NULL, tol, target, SM_SR_ALL)`.
|
||||
Drop {
|
||||
/// Domain.
|
||||
dom: Dom,
|
||||
/// `SmSolverOperationType` value (0 to 5).
|
||||
op: u8,
|
||||
/// Test point.
|
||||
p: [f64; 3],
|
||||
/// Distance tolerance.
|
||||
tol: f64,
|
||||
/// Optional target distance.
|
||||
target: Option<f64>,
|
||||
},
|
||||
/// `GlobalLineIntersect(dom, p, v, ivl, ray, tol)`.
|
||||
Line {
|
||||
/// Domain.
|
||||
dom: Dom,
|
||||
/// Line point.
|
||||
p: [f64; 3],
|
||||
/// Line vector.
|
||||
v: [f64; 3],
|
||||
/// Optional `SmExtent1d(a, b)`.
|
||||
ivl: Option<[f64; 2]>,
|
||||
/// Fire a ray.
|
||||
ray: bool,
|
||||
/// Distance tolerance.
|
||||
tol: f64,
|
||||
},
|
||||
/// `TrimPlaneLineToPlaneDomain(dom, a, b, tol)`.
|
||||
TrimLine {
|
||||
/// Domain.
|
||||
dom: Dom,
|
||||
/// Segment start.
|
||||
a: [f64; 3],
|
||||
/// Segment end.
|
||||
b: [f64; 3],
|
||||
/// 3D tolerance.
|
||||
tol: f64,
|
||||
},
|
||||
/// `CreateIsoParametricCurve(ctx, U or V, iso, 0, .., domain, &gap, want_uv)`.
|
||||
Iso {
|
||||
/// `false`: `SM_SP_U`, `true`: `SM_SP_V`.
|
||||
v_dir: bool,
|
||||
/// Iso parameter.
|
||||
iso: f64,
|
||||
/// Optional domain `SmExtent2d(umin, vmin, umax, vmax)`.
|
||||
domain: Option<[f64; 4]>,
|
||||
/// Also build the UV curve.
|
||||
want_uv: bool,
|
||||
},
|
||||
/// `DropCurve(ctx, dom, curve, ivl, tol, .., keep_all, NULL, creating)`.
|
||||
DropCurve {
|
||||
/// Domain.
|
||||
dom: Dom,
|
||||
/// The curve.
|
||||
curve: CurveIn,
|
||||
/// `SmExtent1d(a, b)`, or the curve's natural interval.
|
||||
ivl: Option<[f64; 2]>,
|
||||
/// Approximation tolerance.
|
||||
tol: f64,
|
||||
/// Keep all drop curves.
|
||||
keep_all: bool,
|
||||
/// Creating a UV trim curve.
|
||||
creating: bool,
|
||||
},
|
||||
/// `CoincidenceCheck(other, tol, .., whole, ..)`.
|
||||
Coincidence {
|
||||
/// Other surface.
|
||||
other: SurfaceIn,
|
||||
/// 3D tolerance.
|
||||
tol: f64,
|
||||
/// Whole surfaces.
|
||||
whole: bool,
|
||||
},
|
||||
/// `*A == *B`.
|
||||
Eq(Box<Make>),
|
||||
/// `Copy(ctx, p)`.
|
||||
Copy,
|
||||
/// `sm_IsGrazingIntersection(curve, t, CreateLineSegment(a, b), s)`.
|
||||
Grazing {
|
||||
/// The curve.
|
||||
curve: CurveIn,
|
||||
/// Curve parameter.
|
||||
t: f64,
|
||||
/// Line start and end.
|
||||
line: [[f64; 3]; 2],
|
||||
/// Line parameter.
|
||||
s: f64,
|
||||
},
|
||||
/// The inherited queries of a copy (`IsParallelToVector(v)`).
|
||||
Inherited([f64; 3]),
|
||||
/// `Reverse(U or V)` (`true`: `SM_SP_V`).
|
||||
Reverse(bool),
|
||||
/// `SwapUV()`.
|
||||
Swap,
|
||||
}
|
||||
|
||||
fn bad(msg: &str) -> FfiError {
|
||||
FfiError::InvalidArgument(msg.into())
|
||||
}
|
||||
|
||||
fn finite(x: f64) -> Result<f64, FfiError> {
|
||||
if x.is_finite() && x.abs() <= MAX_COORD {
|
||||
Ok(x)
|
||||
} else {
|
||||
Err(bad("non-finite or huge value"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks a clamped knot vector for `n1` control points of degree `p`.
|
||||
fn check_knots(knots: &[f64], n1: usize, p: usize) -> Result<(), FfiError> {
|
||||
if knots.len() != n1 + p + 1 {
|
||||
return Err(bad("knot count"));
|
||||
}
|
||||
if knots.windows(2).any(|w| w.first() > w.last()) {
|
||||
return Err(bad("knots decreasing"));
|
||||
}
|
||||
let (Some(&k0), Some(&km)) = (knots.first(), knots.last()) else {
|
||||
return Err(bad("knot count"));
|
||||
};
|
||||
if km <= k0
|
||||
|| knots.iter().take(p + 1).any(|&k| k != k0)
|
||||
|| knots.iter().rev().take(p + 1).any(|&k| k != km)
|
||||
{
|
||||
return Err(bad("knots not clamped"));
|
||||
}
|
||||
let mut run = 0usize;
|
||||
let mut last = f64::NAN;
|
||||
for &k in knots.iter().filter(|&&k| k != k0 && k != km) {
|
||||
run = if k == last { run + 1 } else { 1 };
|
||||
last = k;
|
||||
if run > p {
|
||||
return Err(bad("interior knot multiplicity"));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks the weights: all [`NOW`] or all in `(0, MAX_COORD]`.
|
||||
fn check_weights<'a>(mut ws: impl Iterator<Item = &'a [f64; 4]>) -> Result<(), FfiError> {
|
||||
let Some(first) = ws.next() else {
|
||||
return Err(bad("no control points"));
|
||||
};
|
||||
let rational = first[3] != NOW;
|
||||
let ok = |w: f64| if rational { w > 0.0 && w <= MAX_COORD } else { w == NOW };
|
||||
if !ok(first[3]) || ws.any(|c| !ok(c[3])) {
|
||||
return Err(bad("weights"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct Enc(Vec<f64>);
|
||||
|
||||
impl Enc {
|
||||
fn num(&mut self, x: f64) -> Result<(), FfiError> {
|
||||
self.0.push(finite(x)?);
|
||||
Ok(())
|
||||
}
|
||||
fn int(&mut self, x: usize) {
|
||||
self.0.push(x as f64);
|
||||
}
|
||||
fn flag(&mut self, b: bool) {
|
||||
self.0.push(if b { 1.0 } else { 0.0 });
|
||||
}
|
||||
fn nums(&mut self, v: &[f64]) -> Result<(), FfiError> {
|
||||
v.iter().try_for_each(|&x| self.num(x))
|
||||
}
|
||||
fn cpt(&mut self, c: &[f64; 4]) -> Result<(), FfiError> {
|
||||
self.nums(&c[..3])?;
|
||||
// the weight was checked (NL_NOW or positive)
|
||||
self.0.push(c[3]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn make(&mut self, m: &Make) -> Result<(), FfiError> {
|
||||
match m {
|
||||
Make::Bounded {
|
||||
o,
|
||||
x,
|
||||
y,
|
||||
scale,
|
||||
domain,
|
||||
} => {
|
||||
self.int(0);
|
||||
self.nums(o)?;
|
||||
self.nums(x)?;
|
||||
self.nums(y)?;
|
||||
self.nums(scale)?;
|
||||
self.nums(domain)
|
||||
}
|
||||
Make::Normal { o, n } => {
|
||||
self.int(1);
|
||||
self.nums(o)?;
|
||||
self.nums(n)
|
||||
}
|
||||
Make::Canonical { o, x, y } => {
|
||||
self.int(2);
|
||||
self.nums(o)?;
|
||||
self.nums(x)?;
|
||||
self.nums(y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dom(&mut self, d: &Dom) -> Result<(), FfiError> {
|
||||
match d {
|
||||
Dom::Step => {
|
||||
self.int(0);
|
||||
self.nums(&[0.0; 4])
|
||||
}
|
||||
Dom::Explicit(v) => {
|
||||
self.int(1);
|
||||
self.nums(v)
|
||||
}
|
||||
Dom::Natural => {
|
||||
self.int(2);
|
||||
self.nums(&[0.0; 4])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn curve(&mut self, c: &CurveIn) -> Result<(), FfiError> {
|
||||
match c {
|
||||
CurveIn::Segment(a, b) => {
|
||||
if a == b {
|
||||
return Err(bad("degenerate segment"));
|
||||
}
|
||||
self.int(0);
|
||||
self.nums(a)?;
|
||||
self.nums(b)
|
||||
}
|
||||
CurveIn::BSpline(d) => {
|
||||
let n1 = d.cpts.len();
|
||||
if !(2..=2 * MAX_INDEX + 1).contains(&n1)
|
||||
|| !(1..=MAX_DEGREE).contains(&d.p)
|
||||
|| d.p >= n1
|
||||
|| (d.dim != 2 && d.dim != 3)
|
||||
{
|
||||
return Err(bad("B-spline curve size, degree or dimension"));
|
||||
}
|
||||
check_knots(&d.knots, n1, d.p)?;
|
||||
check_weights(d.cpts.iter())?;
|
||||
self.int(1);
|
||||
self.int(d.dim as usize);
|
||||
self.int(n1 - 1);
|
||||
self.int(d.p);
|
||||
self.int(d.knots.len() - 1);
|
||||
for c in &d.cpts {
|
||||
self.cpt(c)?;
|
||||
}
|
||||
self.nums(&d.knots)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn surface(&mut self, s: &SurfaceIn) -> Result<(), FfiError> {
|
||||
match s {
|
||||
SurfaceIn::Plane(m) => {
|
||||
self.int(0);
|
||||
self.make(m)
|
||||
}
|
||||
SurfaceIn::BSpline(d) => {
|
||||
let n1 = d.net.len();
|
||||
let m1 = d.net.first().map_or(0, Vec::len);
|
||||
if !(2..=MAX_INDEX + 1).contains(&n1)
|
||||
|| !(2..=MAX_INDEX + 1).contains(&m1)
|
||||
|| d.net.iter().any(|r| r.len() != m1)
|
||||
|| !(1..=MAX_DEGREE).contains(&d.p)
|
||||
|| !(1..=MAX_DEGREE).contains(&d.q)
|
||||
|| d.p >= n1
|
||||
|| d.q >= m1
|
||||
{
|
||||
return Err(bad("B-spline surface size or degree"));
|
||||
}
|
||||
check_knots(&d.u, n1, d.p)?;
|
||||
check_knots(&d.v, m1, d.q)?;
|
||||
check_weights(d.net.iter().flatten())?;
|
||||
self.int(1);
|
||||
self.int(n1 - 1);
|
||||
self.int(m1 - 1);
|
||||
self.int(d.p);
|
||||
self.int(d.q);
|
||||
self.int(d.u.len() - 1);
|
||||
self.int(d.v.len() - 1);
|
||||
for c in d.net.iter().flatten() {
|
||||
self.cpt(c)?;
|
||||
}
|
||||
self.nums(&d.u)?;
|
||||
self.nums(&d.v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn op(&mut self, op: &Op) -> Result<(), FfiError> {
|
||||
match op {
|
||||
Op::State => self.int(1),
|
||||
Op::Evaluate { uv, hu, hv, upper } => {
|
||||
if *hu > 4 || *hv > 4 || (*upper && hu > hv) {
|
||||
return Err(bad("derivative orders"));
|
||||
}
|
||||
self.int(2);
|
||||
self.nums(uv)?;
|
||||
self.int(*hu);
|
||||
self.int(*hv);
|
||||
self.flag(*upper);
|
||||
}
|
||||
Op::EvalPoint(uv) => {
|
||||
self.int(3);
|
||||
self.nums(uv)?;
|
||||
}
|
||||
Op::EvalFast(uv) => {
|
||||
self.int(4);
|
||||
self.nums(uv)?;
|
||||
}
|
||||
Op::Normal(uv) => {
|
||||
self.int(5);
|
||||
self.nums(uv)?;
|
||||
}
|
||||
Op::Project(p) => {
|
||||
self.int(6);
|
||||
self.nums(p)?;
|
||||
}
|
||||
Op::Queries(uv) => {
|
||||
self.int(7);
|
||||
self.nums(uv)?;
|
||||
}
|
||||
Op::KindOf(t) => {
|
||||
if *t > 100_000 {
|
||||
return Err(bad("type"));
|
||||
}
|
||||
self.int(8);
|
||||
self.int(*t as usize);
|
||||
}
|
||||
Op::Adjust(d) => {
|
||||
self.int(9);
|
||||
self.dom(d)?;
|
||||
}
|
||||
Op::MakeNurb => self.int(10),
|
||||
Op::Reparam(d) => {
|
||||
self.int(11);
|
||||
self.dom(d)?;
|
||||
}
|
||||
Op::Toggle => self.int(12),
|
||||
Op::Rebuild => self.int(13),
|
||||
Op::Drop {
|
||||
dom,
|
||||
op,
|
||||
p,
|
||||
tol,
|
||||
target,
|
||||
} => {
|
||||
if *op > 5 {
|
||||
return Err(bad("solver operation"));
|
||||
}
|
||||
self.int(14);
|
||||
self.dom(dom)?;
|
||||
self.int(usize::from(*op));
|
||||
self.nums(p)?;
|
||||
self.num(*tol)?;
|
||||
self.flag(target.is_some());
|
||||
self.num(target.unwrap_or(0.0))?;
|
||||
}
|
||||
Op::Line {
|
||||
dom,
|
||||
p,
|
||||
v,
|
||||
ivl,
|
||||
ray,
|
||||
tol,
|
||||
} => {
|
||||
self.int(15);
|
||||
self.dom(dom)?;
|
||||
self.nums(p)?;
|
||||
self.nums(v)?;
|
||||
self.flag(ivl.is_some());
|
||||
self.nums(&ivl.unwrap_or([0.0, 0.0]))?;
|
||||
self.flag(*ray);
|
||||
self.num(*tol)?;
|
||||
}
|
||||
Op::TrimLine { dom, a, b, tol } => {
|
||||
self.int(16);
|
||||
self.dom(dom)?;
|
||||
self.nums(a)?;
|
||||
self.nums(b)?;
|
||||
self.num(*tol)?;
|
||||
}
|
||||
Op::Iso {
|
||||
v_dir,
|
||||
iso,
|
||||
domain,
|
||||
want_uv,
|
||||
} => {
|
||||
self.int(17);
|
||||
self.flag(*v_dir);
|
||||
self.num(*iso)?;
|
||||
self.flag(domain.is_some());
|
||||
self.nums(&domain.unwrap_or([0.0; 4]))?;
|
||||
self.flag(*want_uv);
|
||||
}
|
||||
Op::DropCurve {
|
||||
dom,
|
||||
curve,
|
||||
ivl,
|
||||
tol,
|
||||
keep_all,
|
||||
creating,
|
||||
} => {
|
||||
self.int(18);
|
||||
self.dom(dom)?;
|
||||
self.curve(curve)?;
|
||||
self.flag(ivl.is_some());
|
||||
self.nums(&ivl.unwrap_or([0.0, 0.0]))?;
|
||||
self.num(*tol)?;
|
||||
self.flag(*keep_all);
|
||||
self.flag(*creating);
|
||||
}
|
||||
Op::Coincidence { other, tol, whole } => {
|
||||
self.int(19);
|
||||
self.surface(other)?;
|
||||
self.num(*tol)?;
|
||||
self.flag(*whole);
|
||||
}
|
||||
Op::Eq(m) => {
|
||||
self.int(20);
|
||||
self.make(m)?;
|
||||
}
|
||||
Op::Copy => self.int(21),
|
||||
Op::Grazing { curve, t, line, s } => {
|
||||
if line[0] == line[1] {
|
||||
return Err(bad("degenerate segment"));
|
||||
}
|
||||
self.int(22);
|
||||
self.curve(curve)?;
|
||||
self.num(*t)?;
|
||||
self.nums(&line[0])?;
|
||||
self.nums(&line[1])?;
|
||||
self.num(*s)?;
|
||||
}
|
||||
Op::Inherited(v) => {
|
||||
self.int(23);
|
||||
self.nums(v)?;
|
||||
}
|
||||
Op::Reverse(v_dir) => {
|
||||
self.int(24);
|
||||
self.flag(*v_dir);
|
||||
}
|
||||
Op::Swap => self.int(25),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The flat program [`run`] hands the shim.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// [`FfiError::InvalidArgument`] for inputs [`run`] rejects.
|
||||
pub fn encode(make: &Make, ops: &[Op]) -> Result<Vec<f64>, FfiError> {
|
||||
if ops.len() > MAX_OPS {
|
||||
return Err(bad("too many ops"));
|
||||
}
|
||||
let mut e = Enc(Vec::new());
|
||||
e.make(make)?;
|
||||
e.int(ops.len());
|
||||
for op in ops {
|
||||
e.op(op)?;
|
||||
}
|
||||
Ok(e.0)
|
||||
}
|
||||
|
||||
/// Builds plane A from `make`, runs `ops` on it and returns the record (see the module
|
||||
/// docs).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// [`FfiError::InvalidArgument`] for rejected inputs; [`FfiError::Status`] for a shim
|
||||
/// failure.
|
||||
pub fn run(make: &Make, ops: &[Op]) -> Result<Vec<f64>, FfiError> {
|
||||
let prog = encode(make, ops)?;
|
||||
let n_in = i64::try_from(prog.len()).map_err(|_| bad("program too long"))?;
|
||||
call_with_buffer(256, |buf: &mut [f64], len| {
|
||||
// SAFETY: `prog` is valid for `n_in` reads; `buf` for `buf.len()` writes; `len`
|
||||
// is a valid i64; nothing is retained.
|
||||
unsafe {
|
||||
sys::sc_surface_plane_p1_run(
|
||||
prog.as_ptr(),
|
||||
n_in,
|
||||
buf.as_mut_ptr(),
|
||||
buf.len() as i64,
|
||||
&raw mut *len,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -186,6 +186,11 @@ fn surface_read_from_db(
|
||||
"SmSurface virtuals of an SmCurveBoundedSurface",
|
||||
"surface.surface_p1",
|
||||
)),
|
||||
// UNPORTED(surface.surface_p1): SmSurface dispatch for an SmPlane
|
||||
SurfaceKind::Plane(_) => Err(SolidError::unported(
|
||||
"SmSurface virtuals of an SmPlane",
|
||||
"surface.surface_p1",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -410,6 +410,11 @@ fn surface_read_from_db(
|
||||
"SmSurface virtuals of an SmCurveBoundedSurface",
|
||||
"surface.surface_p1",
|
||||
)),
|
||||
// UNPORTED(surface.surface_p1): SmSurface dispatch for an SmPlane
|
||||
SurfaceKind::Plane(_) => Err(SolidError::unported(
|
||||
"SmSurface virtuals of an SmPlane",
|
||||
"surface.surface_p1",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1034,23 +1034,20 @@ impl BSplineSurface {
|
||||
/// (or of every v column) lie on a line parallel to it (`IsParallelToVector`).
|
||||
/// `false` without NURB; `true` for a zero vector (see the module docs).
|
||||
///
|
||||
/// The `SmPlane` branch is
|
||||
/// [`crate::surface::plane::Plane::is_parallel_to_vector`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// [`SolidError::Unported`] for an analytic plane (`SmPlane::GetPosition`).
|
||||
/// None (the `Result` is kept for the derived surfaces).
|
||||
pub fn is_parallel_to_vector(&self, vector: Vec3) -> Result<bool> {
|
||||
let Ok(test_vec) = vector.unitize() else {
|
||||
// SER(sTestVec.Unitize()): SM_ERR_INVALID_INPUT returned as the boolean
|
||||
return Ok(true);
|
||||
};
|
||||
|
||||
if self.is_kind_of(PLANE_TYPE)? {
|
||||
// the SmPlane branch: |Z . v| < SM_EFF_ZERO * (1 + |origin|max)
|
||||
// UNPORTED(surface.plane_p1): SmPlane::GetPosition
|
||||
return Err(SolidError::Unported {
|
||||
upstream: "SmPlane::GetPosition",
|
||||
unit: "surface.plane_p1",
|
||||
});
|
||||
}
|
||||
// the SmPlane branch (|Z . v| < SM_EFF_ZERO * (1 + |origin|max)) is
|
||||
// Plane::is_parallel_to_vector: a plane is never a plain BSplineSurface here
|
||||
|
||||
let Some(sur) = self.nurb_ref() else {
|
||||
return Ok(false); // FS
|
||||
|
||||
@@ -74,7 +74,7 @@ use crate::geom::axis2_placement::Axis2Placement;
|
||||
use crate::geom::extent1d::Extent1d;
|
||||
use crate::geom::extent2d::Extent2d;
|
||||
use crate::geom::geom_utility::p1::line_closest_point;
|
||||
use crate::geom::vector2d::Point2;
|
||||
use crate::geom::vector2d::{Point2, Vec2};
|
||||
use crate::geom::vector3d::{Point3, Vec3};
|
||||
use crate::kernel::Kernel;
|
||||
use crate::math::{EFF_ZERO, EFF_ZERO_SQRT};
|
||||
@@ -88,6 +88,7 @@ use crate::nurbs::math::{NOW, NOZ};
|
||||
use crate::nurbs::nurbs_srf::{is_nsrf_degenerate, split_srf};
|
||||
use crate::nurbs::srf_basic::Direction;
|
||||
use crate::nurbs::srf_tool::p1::{extract_iso_crv, extract_patch};
|
||||
use crate::surface::plane::Plane;
|
||||
|
||||
use super::p1::{
|
||||
BSplineSurface, BSplineSurfaceForm, BoundingBoxRequest, CanonicalSurfaceData, SurfParamType,
|
||||
@@ -256,18 +257,25 @@ fn evaluate_1st_derivatives(_srf: &BSplineSurface, _uv: Point2) -> Result<(Point
|
||||
struct PlaneData {
|
||||
/// `EvaluateNormal` at the domain centre.
|
||||
normal: Vec3,
|
||||
/// The points at the domain corners `(0, 0)`, `(0, 1)`, `(1, 0)`, `(1, 1)`
|
||||
/// The points at the natural domain corners `(0, 0)`, `(0, 1)`, `(1, 0)`, `(1, 1)`
|
||||
/// (relative).
|
||||
corners: [Point3; 4],
|
||||
}
|
||||
|
||||
/// `new SmPlane(point, x_dir, y_dir, scale, domain)`, its normal and corners.
|
||||
fn new_plane(_point: Point3, _x_dir: Vec3, _y_dir: Vec3, _domain: &Extent2d) -> Result<PlaneData> {
|
||||
// UNPORTED(surface.plane_p1): SmPlane::SmPlane
|
||||
Err(SolidError::Unported {
|
||||
upstream: "SmPlane::SmPlane",
|
||||
unit: "surface.plane_p1",
|
||||
})
|
||||
/// `new SmPlane(point, x_dir, y_dir, (1, 1), domain)`, its normal
|
||||
/// (`EvaluateNormal`) and the points at the corners of its natural domain
|
||||
/// (`EvaluatePoint` at `GetMin`, `(0, 1)`, `(1, 0)`, `GetMax`).
|
||||
fn new_plane(point: Point3, x_dir: Vec3, y_dir: Vec3, domain: &Extent2d) -> PlaneData {
|
||||
let plane = Plane::new(point, x_dir, y_dir, Vec2::new(1.0, 1.0), domain);
|
||||
let normal = plane.evaluate_normal();
|
||||
let plane_domain = plane.natural_uv_domain();
|
||||
let corners = [
|
||||
plane.evaluate_point(plane_domain.min()),
|
||||
plane.evaluate_point(plane_domain.evaluate(0.0, 1.0)),
|
||||
plane.evaluate_point(plane_domain.evaluate(1.0, 0.0)),
|
||||
plane.evaluate_point(plane_domain.max()),
|
||||
];
|
||||
PlaneData { normal, corners }
|
||||
}
|
||||
|
||||
/// `SmSurface::CreateNaturalUVTrimCurves(domain, SM_SP_U, FALSE, ..)`: the 3D trimming
|
||||
@@ -873,7 +881,7 @@ impl BSplineSurface {
|
||||
let size = bbox.size();
|
||||
let leng = size.length();
|
||||
let tmp_domain = Extent2d::from_bounds(-leng, -leng, leng, leng);
|
||||
let plane = new_plane(pnt, dv, du, &tmp_domain)?;
|
||||
let plane = new_plane(pnt, dv, du, &tmp_domain);
|
||||
|
||||
// Make sure both curves are on the plane before continuing.
|
||||
if !curve1.is_on_plane(pnt, plane.normal, tol, None)?.on_plane {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
pub mod b_spline_volume;
|
||||
pub mod curve_bounded_surface;
|
||||
pub mod plane;
|
||||
#[allow(clippy::module_inception)]
|
||||
pub mod surface;
|
||||
pub mod surface_cache;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
//! Planes (`SmPlane`): an analytic plane `O + u * su * X + v * sv * Y` over a STEP
|
||||
//! (analytic) domain, kept next to a bilinear B-spline (its NURB representation).
|
||||
//!
|
||||
//! Upstream: `source/SMLib/src/SmPlane.cpp`, `source/SMLib/inc/SmPlane.h`.
|
||||
//! The file is split over the plan units `surface.plane_p1` and `_p2`; each lives in
|
||||
//! its own submodule and adds methods to [`Plane`].
|
||||
|
||||
pub mod p1;
|
||||
|
||||
pub use p1::{Coincidence, Plane, PlaneDropCurve, PlaneIsoCurve};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -64,9 +64,13 @@
|
||||
//! Deviations:
|
||||
//! - **Object model.** `SmSurface` is not abstract upstream, but every ported surface
|
||||
//! is a derived class: the [`SurfaceKind`] variants. The ported kinds are
|
||||
//! [`BSplineSurface`] and [`CurveBoundedSurface`] (unit
|
||||
//! `surface.curve_bounded_surface`); the analytic and generated surfaces (`SmPlane`,
|
||||
//! `SmCone`, ...) join the enum in their own units. Each virtual method is one `match`: a
|
||||
//! [`BSplineSurface`], [`CurveBoundedSurface`] (unit
|
||||
//! `surface.curve_bounded_surface`) and [`Plane`] (unit `surface.plane_p1`: the
|
||||
//! inherited `SmBSplineSurface` methods run on its B-spline part, re-dispatched to
|
||||
//! the plane's overrides; `Transform` and `TrimWithDomain` belong to
|
||||
//! `surface.plane_p2`, `STEPInversion` needs the point solvers of
|
||||
//! `surface.surface_p1`); the other analytic and generated surfaces (`SmCone`, ...)
|
||||
//! join the enum in their own units. Each virtual method is one `match`: a
|
||||
//! kind that overrides it calls its own method, the others run the base-class body
|
||||
//! ported here. The base bodies are also public as `base_*` functions (they are
|
||||
//! what a kind without an override runs, and what difftest compares with an
|
||||
@@ -165,6 +169,7 @@ use crate::nurbs::b_spline_surface_n_lib::p2::{EvaluateFlags, OrientType, Skinne
|
||||
use crate::nurbs::crv_fit::p1::ParamMethod;
|
||||
use crate::nurbs::knots::Side;
|
||||
use crate::surface::curve_bounded_surface::CurveBoundedSurface;
|
||||
use crate::surface::plane::Plane;
|
||||
use crate::topology::database_io::DatabaseReader;
|
||||
|
||||
// ---- type codes ---------------------------------------------------------------------
|
||||
@@ -232,6 +237,8 @@ pub enum SurfaceKind {
|
||||
BSpline(BSplineSurface),
|
||||
/// `SmCurveBoundedSurface`.
|
||||
CurveBounded(CurveBoundedSurface),
|
||||
/// `SmPlane` (an `SmBSplineSurface`).
|
||||
Plane(Plane),
|
||||
}
|
||||
|
||||
/// A surface (`SmSurface`): its concrete kind and the object owning it (`m_pOwner`:
|
||||
@@ -258,6 +265,12 @@ impl From<BSplineSurface> for Surface {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Plane> for Surface {
|
||||
fn from(s: Plane) -> Self {
|
||||
Self::new(SurfaceKind::Plane(s))
|
||||
}
|
||||
}
|
||||
|
||||
/// The curves of a surface/surface intersection (`GlobalSurfaceIntersect`'s
|
||||
/// outputs): one entry per intersection curve in each array.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@@ -401,6 +414,7 @@ impl Surface {
|
||||
match &self.kind {
|
||||
SurfaceKind::BSpline(_) => BSplineSurface::TYPE,
|
||||
SurfaceKind::CurveBounded(s) => s.type_code(),
|
||||
SurfaceKind::Plane(s) => s.type_code(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,6 +424,7 @@ impl Surface {
|
||||
match &self.kind {
|
||||
SurfaceKind::BSpline(s) => s.type_string(),
|
||||
SurfaceKind::CurveBounded(s) => s.type_string(),
|
||||
SurfaceKind::Plane(s) => s.type_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,6 +434,7 @@ impl Surface {
|
||||
match &self.kind {
|
||||
SurfaceKind::BSpline(s) => s.class_string(),
|
||||
SurfaceKind::CurveBounded(s) => s.class_string(),
|
||||
SurfaceKind::Plane(s) => s.class_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,6 +447,7 @@ impl Surface {
|
||||
match &self.kind {
|
||||
SurfaceKind::BSpline(s) => s.is_kind_of(t),
|
||||
SurfaceKind::CurveBounded(s) => Ok(s.is_kind_of(t)),
|
||||
SurfaceKind::Plane(s) => s.is_kind_of(t),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,6 +462,7 @@ impl Surface {
|
||||
match &self.kind {
|
||||
SurfaceKind::BSpline(s) => Ok(Self::from(s.copy())),
|
||||
SurfaceKind::CurveBounded(_) => base_copy(),
|
||||
SurfaceKind::Plane(s) => Ok(Self::from(s.copy())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,6 +475,8 @@ impl Surface {
|
||||
match &self.kind {
|
||||
SurfaceKind::BSpline(s) => Ok(Self::from(s.copy_and_add_analytics()?)),
|
||||
SurfaceKind::CurveBounded(_) => base_copy_and_add_analytics(),
|
||||
// SmBSplineSurface::CopyAndAddAnalytics: not SmBSplineSurface_TYPE, so Copy
|
||||
SurfaceKind::Plane(s) => Ok(Self::from(s.copy())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,6 +498,9 @@ impl Surface {
|
||||
match &self.kind {
|
||||
SurfaceKind::BSpline(s) => s.evaluate(uv, highest_u_deriv, highest_v_deriv, flags),
|
||||
SurfaceKind::CurveBounded(s) => s.evaluate(uv, highest_u_deriv, highest_v_deriv, flags),
|
||||
SurfaceKind::Plane(s) => {
|
||||
s.evaluate(uv, highest_u_deriv, highest_v_deriv, flags.only_upper_half)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,6 +513,7 @@ impl Surface {
|
||||
match &self.kind {
|
||||
SurfaceKind::BSpline(s) => s.evaluate_point(uv),
|
||||
SurfaceKind::CurveBounded(s) => s.evaluate_point(uv),
|
||||
SurfaceKind::Plane(s) => Ok(s.evaluate_point(uv)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,14 +525,18 @@ impl Surface {
|
||||
match &self.kind {
|
||||
SurfaceKind::BSpline(s) => s.natural_uv_domain(),
|
||||
SurfaceKind::CurveBounded(s) => s.natural_uv_domain(),
|
||||
SurfaceKind::Plane(s) => s.natural_uv_domain(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The STEP parameter domain (`GetSTEPUVDomain`; the base default, the natural
|
||||
/// domain, for every ported kind).
|
||||
/// The STEP parameter domain (`GetSTEPUVDomain`; the plane's analytic domain,
|
||||
/// else the base default, the natural domain).
|
||||
#[must_use]
|
||||
pub fn step_uv_domain(&self) -> Extent2d {
|
||||
base_step_uv_domain(self)
|
||||
match &self.kind {
|
||||
SurfaceKind::Plane(s) => s.step_uv_domain(),
|
||||
SurfaceKind::BSpline(_) | SurfaceKind::CurveBounded(_) => base_step_uv_domain(self),
|
||||
}
|
||||
}
|
||||
|
||||
/// The degree in `param`'s direction (`GetDegree`).
|
||||
@@ -517,6 +545,7 @@ impl Surface {
|
||||
match &self.kind {
|
||||
SurfaceKind::BSpline(s) => s.degree(param),
|
||||
SurfaceKind::CurveBounded(_) => base_degree(),
|
||||
SurfaceKind::Plane(s) => s.base_for_query().degree(param),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,6 +556,7 @@ impl Surface {
|
||||
match &self.kind {
|
||||
SurfaceKind::BSpline(s) => s.number_control_points(param),
|
||||
SurfaceKind::CurveBounded(_) => base_number_control_points(),
|
||||
SurfaceKind::Plane(s) => s.base_for_query().number_control_points(param),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,6 +569,7 @@ impl Surface {
|
||||
match &self.kind {
|
||||
SurfaceKind::BSpline(s) => s.max_analytic_domain(),
|
||||
SurfaceKind::CurveBounded(_) => Ok(base_max_analytic_domain()),
|
||||
SurfaceKind::Plane(s) => Ok(s.max_analytic_domain()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,6 +579,7 @@ impl Surface {
|
||||
match &self.kind {
|
||||
SurfaceKind::BSpline(s) => s.is_bilinear(),
|
||||
SurfaceKind::CurveBounded(_) => base_is_bilinear(),
|
||||
SurfaceKind::Plane(s) => s.base_for_query().is_bilinear(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,6 +592,7 @@ impl Surface {
|
||||
match &self.kind {
|
||||
SurfaceKind::BSpline(s) => s.is_parallel_to_vector(vector),
|
||||
SurfaceKind::CurveBounded(_) => Ok(base_is_parallel_to_vector()),
|
||||
SurfaceKind::Plane(s) => Ok(s.is_parallel_to_vector(vector)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -577,6 +610,7 @@ impl Surface {
|
||||
match &self.kind {
|
||||
SurfaceKind::BSpline(s) => s.knots(param, ivl),
|
||||
SurfaceKind::CurveBounded(_) => base_knots(),
|
||||
SurfaceKind::Plane(s) => s.base_for_query().knots(param, ivl),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,25 +648,43 @@ impl Surface {
|
||||
s.step_inversion(anal_domain, point_on_surf, tol_3d, uv_guess)
|
||||
}
|
||||
SurfaceKind::CurveBounded(_) => base_step_inversion(),
|
||||
// SmBSplineSurface::STEPInversion solves on the plane through the virtual
|
||||
// point solvers
|
||||
// UNPORTED(surface.surface_p1): SmSurface::GlobalPointSolve
|
||||
SurfaceKind::Plane(_) => Err(SolidError::unported(
|
||||
"SmSurface::GlobalPointSolve",
|
||||
"surface.surface_p1",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// `DropPointFast`: no ported kind overrides it; the base default fails.
|
||||
/// `DropPointFast`: the plane override (its solutions name no object); the
|
||||
/// other ported kinds run the base default, which fails.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// `SM_ERR` (the base default).
|
||||
/// Those of [`Plane::drop_point_fast`]; `SM_ERR` (the base default).
|
||||
pub fn drop_point_fast<O: SolutionObject>(
|
||||
&self,
|
||||
_uv_domain: &Extent2d,
|
||||
_operation: SolverOperation,
|
||||
_test_point: Point3,
|
||||
uv_domain: &Extent2d,
|
||||
operation: SolverOperation,
|
||||
test_point: Point3,
|
||||
_in_pointing_vector: Option<Vec3>,
|
||||
_distance_tolerance: f64,
|
||||
_target_distance: Option<f64>,
|
||||
distance_tolerance: f64,
|
||||
target_distance: Option<f64>,
|
||||
_requested: SolutionRequested,
|
||||
) -> Result<SolutionArray<O>> {
|
||||
base_drop_point_fast()
|
||||
match &self.kind {
|
||||
SurfaceKind::Plane(s) => s.drop_point_fast(
|
||||
uv_domain,
|
||||
operation,
|
||||
test_point,
|
||||
distance_tolerance,
|
||||
target_distance,
|
||||
None,
|
||||
),
|
||||
SurfaceKind::BSpline(_) | SurfaceKind::CurveBounded(_) => base_drop_point_fast(),
|
||||
}
|
||||
}
|
||||
|
||||
/// `IntersectWithPlane`: no ported kind overrides it; the base default fails.
|
||||
@@ -665,6 +717,7 @@ impl Surface {
|
||||
match &mut self.kind {
|
||||
SurfaceKind::BSpline(s) => s.adjust_step_uv_domain(new_step_uv_domain),
|
||||
SurfaceKind::CurveBounded(_) => base_adjust_step_uv_domain(),
|
||||
SurfaceKind::Plane(s) => s.adjust_step_uv_domain(new_step_uv_domain),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,6 +730,7 @@ impl Surface {
|
||||
match &mut self.kind {
|
||||
SurfaceKind::BSpline(s) => s.reverse(param),
|
||||
SurfaceKind::CurveBounded(_) => base_edit(),
|
||||
SurfaceKind::Plane(s) => s.reverse(param),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -689,6 +743,7 @@ impl Surface {
|
||||
match &mut self.kind {
|
||||
SurfaceKind::BSpline(s) => s.reparameterize(new_domain),
|
||||
SurfaceKind::CurveBounded(_) => base_edit(),
|
||||
SurfaceKind::Plane(s) => s.reparameterize(new_domain),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -701,6 +756,7 @@ impl Surface {
|
||||
match &mut self.kind {
|
||||
SurfaceKind::BSpline(s) => s.swap_uv(),
|
||||
SurfaceKind::CurveBounded(_) => base_edit(),
|
||||
SurfaceKind::Plane(s) => s.swap_uv(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -713,6 +769,11 @@ impl Surface {
|
||||
match &mut self.kind {
|
||||
SurfaceKind::BSpline(s) => s.transform(rotate_n_move, scale),
|
||||
SurfaceKind::CurveBounded(_) => base_edit(),
|
||||
// UNPORTED(surface.plane_p2): SmPlane::Transform
|
||||
SurfaceKind::Plane(_) => Err(SolidError::unported(
|
||||
"SmPlane::Transform",
|
||||
"surface.plane_p2",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -725,6 +786,11 @@ impl Surface {
|
||||
match &mut self.kind {
|
||||
SurfaceKind::BSpline(s) => s.trim_with_domain(trim_domain),
|
||||
SurfaceKind::CurveBounded(_) => base_edit(),
|
||||
// UNPORTED(surface.plane_p2): SmPlane::TrimWithDomain
|
||||
SurfaceKind::Plane(_) => Err(SolidError::unported(
|
||||
"SmPlane::TrimWithDomain",
|
||||
"surface.plane_p2",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -743,6 +809,10 @@ impl Surface {
|
||||
Ok((Self::from(l), Self::from(r)))
|
||||
}
|
||||
SurfaceKind::CurveBounded(_) => base_split_at(),
|
||||
SurfaceKind::Plane(s) => {
|
||||
let (l, r) = s.base_for_query().split_at(param, surf_param)?;
|
||||
Ok((Self::from(l), Self::from(r)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -761,6 +831,10 @@ impl Surface {
|
||||
s.create_extended_surface(dist, extension_continuity)?,
|
||||
)),
|
||||
SurfaceKind::CurveBounded(_) => base_create_extended_surface(),
|
||||
SurfaceKind::Plane(s) => Ok(Self::from(
|
||||
s.base_for_query()
|
||||
.create_extended_surface(dist, extension_continuity)?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,6 +857,13 @@ impl Surface {
|
||||
extension_continuity,
|
||||
)?)),
|
||||
SurfaceKind::CurveBounded(_) => base_create_extended_surface(),
|
||||
SurfaceKind::Plane(s) => {
|
||||
Ok(Self::from(s.base_for_query().create_extended_surface_dir(
|
||||
ext_direction,
|
||||
dist,
|
||||
extension_continuity,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -174,11 +174,24 @@ fn invalid(operation: &'static str, reason: &'static str) -> SolidError {
|
||||
SolidError::InvalidInput { operation, reason }
|
||||
}
|
||||
|
||||
const OP_PLANE_ISO: &str = "surface::surface_cache::p1::create_iso_parametric_curve";
|
||||
const OP_PLANE_BOX: &str = "surface::surface_cache::p1::calculate_bounding_box";
|
||||
|
||||
/// A surface cache operation on an `SmPlane`, which the cache code does not handle.
|
||||
const fn plane_unsupported(operation: &'static str) -> SolidError {
|
||||
SolidError::Unsupported {
|
||||
operation,
|
||||
reason: "SmPlane surfaces in the surface cache",
|
||||
}
|
||||
}
|
||||
|
||||
/// `SM_CAST_PTR(SmBSplineSurface, surface)`.
|
||||
const fn bspline_of(surface: &Surface) -> Option<&BSplineSurface> {
|
||||
match surface.kind() {
|
||||
SurfaceKind::BSpline(b) => Some(b),
|
||||
SurfaceKind::CurveBounded(_) => None,
|
||||
// an SmPlane is an SmBSplineSurface upstream, but the cache code here runs the
|
||||
// B-spline methods without the plane's overrides: refused
|
||||
SurfaceKind::CurveBounded(_) | SurfaceKind::Plane(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +226,9 @@ fn create_iso_parametric_curve(
|
||||
) -> Result<BSplineCurve> {
|
||||
match surface.kind() {
|
||||
SurfaceKind::BSpline(b) => bspline_iso_curve(k, b, param, iso, domain),
|
||||
// SmPlane::CreateIsoParametricCurve makes an SmLine, which the cache code here
|
||||
// cannot hold as a B-spline
|
||||
SurfaceKind::Plane(_) => Err(plane_unsupported(OP_PLANE_ISO)),
|
||||
SurfaceKind::CurveBounded(_) => {
|
||||
// UNPORTED(surface.surface_p5): SmSurface::CreateIsoParametricCurve
|
||||
Err(SolidError::unported(
|
||||
@@ -247,6 +263,8 @@ fn calculate_bounding_box(
|
||||
) -> Result<BoundingBoxes> {
|
||||
match surface.kind() {
|
||||
SurfaceKind::BSpline(b) => b.calculate_bounding_box(domain, req),
|
||||
// the plane branch of SmBSplineSurface::CalculateBoundingBox is not ported
|
||||
SurfaceKind::Plane(_) => Err(plane_unsupported(OP_PLANE_BOX)),
|
||||
SurfaceKind::CurveBounded(_) => {
|
||||
// UNPORTED(surface.surface_p1): SmSurface::CalculateBoundingBox
|
||||
Err(SolidError::unported(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,838 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// difftest shim for unit `surface.plane_p1`: the SmPlane members owned by the unit
|
||||
// (source/SMLib/src/SmPlane.cpp, source/SMLib/inc/SmPlane.h), called on SmPlane objects (virtual
|
||||
// dispatch is upstream's), plus the inherited SmBSplineSurface members the port re-dispatches for
|
||||
// planes (GetNaturalUVDomain, GetMaxAnalyticDomain, IsBilinear, IsParallelToVector, GetDegree,
|
||||
// GetNumberControlPoints, Reverse, SwapUV).
|
||||
//
|
||||
// One entry point, sc_surface_plane_p1_run(in, n_in, out, cap, out_len). `in` is a program: a
|
||||
// plane block (plane A), the op count, then the ops; layouts are documented in
|
||||
// crates/solidcore-ffi/src/units/surface_plane_p1.rs. Every read is bounds checked here (a bad
|
||||
// program is SC_ERR_ARG). `out` receives `make_status`, then per op `op status [values]` (values
|
||||
// only when the status is 0), then the state of A. A failed plane block ends the record after
|
||||
// its status.
|
||||
//
|
||||
// Const queries that make the NURB of a plane without one (GetNaturalUVDomain and friends) are
|
||||
// asked of a copy here, so that the record does not depend on when upstream makes it lazily; the
|
||||
// state prints the NURB the plane has, or the one a copy makes.
|
||||
#include "sc_shim.h"
|
||||
|
||||
#include <SmAxis2Placement.h>
|
||||
#include <SmBSplineCurve.h>
|
||||
#include <SmBSplineSurface.h>
|
||||
#include <SmContext.h>
|
||||
#include <SmCurve.h>
|
||||
#include <SmExtent1d.h>
|
||||
#include <SmExtent2d.h>
|
||||
#include <SmLine.h>
|
||||
#include <SmNurbsCrv.h>
|
||||
#include <SmNurbsSrf.h>
|
||||
#include <SmPlane.h>
|
||||
#include <SmSolutionArray.h>
|
||||
#include <SmSurface.h>
|
||||
#include <SmTArray.h>
|
||||
#include <SmVector2d.h>
|
||||
#include <SmVector3d.h>
|
||||
#include <nurbs.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
// Plane blocks (keep in sync with crates/solidcore-ffi/src/units/surface_plane_p1.rs).
|
||||
enum : int32_t
|
||||
{
|
||||
MK_BOUNDED = 0,
|
||||
MK_NORMAL = 1,
|
||||
MK_CANONICAL = 2,
|
||||
};
|
||||
|
||||
// Ops (keep in sync with crates/solidcore-ffi/src/units/surface_plane_p1.rs).
|
||||
enum : int32_t
|
||||
{
|
||||
OP_STATE = 1,
|
||||
OP_EVALUATE = 2,
|
||||
OP_EVAL_POINT = 3,
|
||||
OP_EVAL_FAST = 4,
|
||||
OP_NORMAL = 5,
|
||||
OP_PROJECT = 6,
|
||||
OP_QUERIES = 7,
|
||||
OP_KIND_OF = 8,
|
||||
OP_ADJUST = 9,
|
||||
OP_MAKE_NURB = 10,
|
||||
OP_REPARAM = 11,
|
||||
OP_TOGGLE = 12,
|
||||
OP_REBUILD = 13,
|
||||
OP_DROP = 14,
|
||||
OP_LINE = 15,
|
||||
OP_TRIM_LINE = 16,
|
||||
OP_ISO = 17,
|
||||
OP_DROP_CURVE = 18,
|
||||
OP_COINCIDENCE = 19,
|
||||
OP_EQ = 20,
|
||||
OP_COPY = 21,
|
||||
OP_GRAZING = 22,
|
||||
OP_INHERITED = 23,
|
||||
OP_REVERSE = 24,
|
||||
OP_SWAP = 25,
|
||||
OP_LAST = 25,
|
||||
};
|
||||
|
||||
// Reads the protected STEP flags through pointers to members named in a derived class.
|
||||
struct PeekS : SmBSplineSurface
|
||||
{
|
||||
static SmKnotType SmBSplineSurface::*kt() { return &PeekS::m_eKnotType; }
|
||||
static SmBSplineSurfaceForm SmBSplineSurface::*form() { return &PeekS::m_eBSplineSurfaceForm; }
|
||||
};
|
||||
|
||||
struct PeekC : SmBSplineCurve
|
||||
{
|
||||
static SmKnotType SmBSplineCurve::*kt() { return &PeekC::m_eKnotType; }
|
||||
static SmBSplineCurveForm SmBSplineCurve::*form() { return &PeekC::m_eBSplineCurveForm; }
|
||||
};
|
||||
|
||||
struct Bad : std::runtime_error
|
||||
{
|
||||
Bad() : std::runtime_error("bad program") {}
|
||||
};
|
||||
|
||||
struct Rd
|
||||
{
|
||||
const double* in;
|
||||
int64_t n;
|
||||
int64_t at;
|
||||
double num()
|
||||
{
|
||||
if (at >= n)
|
||||
throw Bad();
|
||||
return in[at++];
|
||||
}
|
||||
int64_t idx(int64_t lo, int64_t hi)
|
||||
{
|
||||
double d = num();
|
||||
if (!(d >= (double)lo && d <= (double)hi) || d != (double)(int64_t)d)
|
||||
throw Bad();
|
||||
return (int64_t)d;
|
||||
}
|
||||
bool flag() { return idx(0, 1) != 0; }
|
||||
SmVector3d vec()
|
||||
{
|
||||
double x = num();
|
||||
double y = num();
|
||||
double z = num();
|
||||
return SmVector3d(x, y, z);
|
||||
}
|
||||
SmPoint2d uv()
|
||||
{
|
||||
double x = num();
|
||||
double y = num();
|
||||
return SmPoint2d(x, y);
|
||||
}
|
||||
SmExtent2d dom4()
|
||||
{
|
||||
double a = num();
|
||||
double b = num();
|
||||
double c = num();
|
||||
double d = num();
|
||||
return SmExtent2d(a, b, c, d);
|
||||
}
|
||||
};
|
||||
|
||||
double st(SmStatus s)
|
||||
{
|
||||
return s == SM_SUCCESS ? 0.0 : (double)s;
|
||||
}
|
||||
|
||||
double flg(SmBoolean b)
|
||||
{
|
||||
return b ? 1.0 : 0.0;
|
||||
}
|
||||
|
||||
void put_vec(std::vector<double>& o, const SmVector3d& v)
|
||||
{
|
||||
o.push_back(v.x);
|
||||
o.push_back(v.y);
|
||||
o.push_back(v.z);
|
||||
}
|
||||
|
||||
void put_uv(std::vector<double>& o, const SmPoint2d& v)
|
||||
{
|
||||
o.push_back(v.x);
|
||||
o.push_back(v.y);
|
||||
}
|
||||
|
||||
void put_dom(std::vector<double>& o, const SmExtent2d& e)
|
||||
{
|
||||
o.push_back(e.GetMin().x);
|
||||
o.push_back(e.GetMin().y);
|
||||
o.push_back(e.GetMax().x);
|
||||
o.push_back(e.GetMax().y);
|
||||
}
|
||||
|
||||
void put_ivl(std::vector<double>& o, const SmExtent1d& e)
|
||||
{
|
||||
o.push_back(e.GetMin());
|
||||
o.push_back(e.GetMax());
|
||||
}
|
||||
|
||||
void put_chars(std::vector<double>& o, const TCHAR* s)
|
||||
{
|
||||
for (; *s; s++)
|
||||
o.push_back((double)(unsigned char)*s);
|
||||
}
|
||||
|
||||
// A plane block; NULL with *status set when the construction fails.
|
||||
SmPlane* read_plane(Rd& rd, const SmContext& ctx, SmStatus* status)
|
||||
{
|
||||
*status = SM_SUCCESS;
|
||||
int64_t kind = rd.idx(0, 2);
|
||||
switch (kind)
|
||||
{
|
||||
case MK_BOUNDED:
|
||||
{
|
||||
SmPoint3d o = rd.vec();
|
||||
SmVector3d x = rd.vec();
|
||||
SmVector3d y = rd.vec();
|
||||
SmVector2d s = rd.uv();
|
||||
SmExtent2d d = rd.dom4();
|
||||
return new (ctx) SmPlane(o, x, y, s, d, &ctx);
|
||||
}
|
||||
case MK_NORMAL:
|
||||
{
|
||||
SmPoint3d o = rd.vec();
|
||||
SmVector3d nv = rd.vec();
|
||||
return new (ctx) SmPlane(o, nv, &ctx);
|
||||
}
|
||||
default:
|
||||
{
|
||||
SmPoint3d o = rd.vec();
|
||||
SmVector3d x = rd.vec();
|
||||
SmVector3d y = rd.vec();
|
||||
SmAxis2Placement f(o, x, y);
|
||||
SmPlane* p = NULL;
|
||||
*status = SmPlane::CreateCanonical(ctx, f, p);
|
||||
if (*status != SM_SUCCESS)
|
||||
{
|
||||
delete p;
|
||||
return NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `n m p q r s cpts(x y z w) u v` with sizes checked by the Rust side (1 <= p <= n <= 16).
|
||||
SmBSplineSurface* read_bspline_surface(Rd& rd, const SmContext& ctx)
|
||||
{
|
||||
gw_INDEX n = (gw_INDEX)rd.idx(1, 16);
|
||||
gw_INDEX m = (gw_INDEX)rd.idx(1, 16);
|
||||
gw_DEGREE p = (gw_DEGREE)rd.idx(1, 8);
|
||||
gw_DEGREE q = (gw_DEGREE)rd.idx(1, 8);
|
||||
gw_INDEX r = (gw_INDEX)rd.idx(3, 40);
|
||||
gw_INDEX s = (gw_INDEX)rd.idx(3, 40);
|
||||
if (p > n || q > m || r != n + p + 1 || s != m + q + 1)
|
||||
throw Bad();
|
||||
std::vector<double> v;
|
||||
for (int64_t i = 0; i < 4 * (n + 1) * (m + 1) + r + 1 + s + 1; i++)
|
||||
v.push_back(rd.num());
|
||||
gw_SURFACE* sur = sm_AllocateNurbSurface(n, m, p, q, r, s);
|
||||
size_t k = 0;
|
||||
for (gw_INDEX i = 0; i <= n; i++)
|
||||
for (gw_INDEX j = 0; j <= m; j++)
|
||||
{
|
||||
sur->net->Pw[i][j].x = v[k++];
|
||||
sur->net->Pw[i][j].y = v[k++];
|
||||
sur->net->Pw[i][j].z = v[k++];
|
||||
sur->net->Pw[i][j].w = v[k++];
|
||||
}
|
||||
for (gw_INDEX i = 0; i <= r; i++)
|
||||
sur->knu->U[i] = v[k++];
|
||||
for (gw_INDEX i = 0; i <= s; i++)
|
||||
sur->knv->U[i] = v[k++];
|
||||
return new (ctx) SmBSplineSurface(sur, FALSE, &ctx);
|
||||
}
|
||||
|
||||
// Another surface: `0 plane-block` or `1 bspline-surface`; NULL when the plane block fails.
|
||||
SmSurface* read_surface(Rd& rd, const SmContext& ctx)
|
||||
{
|
||||
int64_t kind = rd.idx(0, 1);
|
||||
if (kind == 0)
|
||||
{
|
||||
SmStatus s = SM_SUCCESS;
|
||||
SmPlane* p = read_plane(rd, ctx, &s);
|
||||
if (p == NULL)
|
||||
throw Bad();
|
||||
return p;
|
||||
}
|
||||
return read_bspline_surface(rd, ctx);
|
||||
}
|
||||
|
||||
// A curve: `0 a b` (CreateLineSegment, dim 3) or `1 dim n p m cpts(x y z w) knots`.
|
||||
SmCurve* read_curve(Rd& rd, const SmContext& ctx)
|
||||
{
|
||||
int64_t kind = rd.idx(0, 1);
|
||||
if (kind == 0)
|
||||
{
|
||||
SmPoint3d a = rd.vec();
|
||||
SmPoint3d b = rd.vec();
|
||||
SmLine* l = NULL;
|
||||
if (SmLine::CreateLineSegment(ctx, 3, a, b, l) != SM_SUCCESS || l == NULL)
|
||||
throw Bad();
|
||||
return l;
|
||||
}
|
||||
ULONG dim = (ULONG)rd.idx(2, 3);
|
||||
int64_t n = rd.idx(1, 32);
|
||||
int64_t p = rd.idx(1, 8);
|
||||
int64_t m = rd.idx(3, 64);
|
||||
if (p > n || m != n + p + 1)
|
||||
throw Bad();
|
||||
std::vector<double> v;
|
||||
for (int64_t i = 0; i < 4 * (n + 1) + m + 1; i++)
|
||||
v.push_back(rd.num());
|
||||
gw_CURVE* cur = sm_AllocateNurbCurve((gw_INDEX)n, (gw_DEGREE)p, (gw_INDEX)m);
|
||||
for (int64_t i = 0; i <= n; i++)
|
||||
{
|
||||
cur->pol->Pw[i].x = v[4 * i];
|
||||
cur->pol->Pw[i].y = v[4 * i + 1];
|
||||
cur->pol->Pw[i].z = v[4 * i + 2];
|
||||
cur->pol->Pw[i].w = v[4 * i + 3];
|
||||
}
|
||||
for (int64_t i = 0; i <= m; i++)
|
||||
cur->knt->U[i] = v[4 * (n + 1) + i];
|
||||
SmBSplineCurve* c = new (ctx) SmBSplineCurve(dim, (ULONG)cur->pol->n, (ULONG)cur->knt->m, (short)cur->p,
|
||||
cur->pol->Pw, cur->knt->U, &ctx);
|
||||
smos_Free(cur);
|
||||
return c;
|
||||
}
|
||||
|
||||
void put_surface_nurb(std::vector<double>& o, const gw_SURFACE* sur)
|
||||
{
|
||||
o.push_back(sur ? 1.0 : 0.0);
|
||||
if (!sur)
|
||||
return;
|
||||
gw_INDEX n = sur->net->n;
|
||||
gw_INDEX m = sur->net->m;
|
||||
gw_INDEX r = sur->knu->m;
|
||||
gw_INDEX s = sur->knv->m;
|
||||
o.push_back((double)n);
|
||||
o.push_back((double)m);
|
||||
o.push_back((double)sur->p);
|
||||
o.push_back((double)sur->q);
|
||||
o.push_back((double)r);
|
||||
o.push_back((double)s);
|
||||
for (gw_INDEX i = 0; i <= n; i++)
|
||||
for (gw_INDEX j = 0; j <= m; j++)
|
||||
{
|
||||
o.push_back(sur->net->Pw[i][j].x);
|
||||
o.push_back(sur->net->Pw[i][j].y);
|
||||
o.push_back(sur->net->Pw[i][j].z);
|
||||
o.push_back(sur->net->Pw[i][j].w);
|
||||
}
|
||||
for (gw_INDEX i = 0; i <= r; i++)
|
||||
o.push_back(sur->knu->U[i]);
|
||||
for (gw_INDEX i = 0; i <= s; i++)
|
||||
o.push_back(sur->knv->U[i]);
|
||||
}
|
||||
|
||||
// A copy of `a` that leaves `a` alone: the copy constructor makes the source's missing NURB
|
||||
// (through a const_cast), so a plane without NURB is rebuilt from its analytic data instead
|
||||
// (SetCanonical keeps unit perpendicular axes as they are).
|
||||
SmPlane* side_free_copy(const SmContext& ctx, const SmPlane* a)
|
||||
{
|
||||
if (a->GetGwNurbPointer() != NULL)
|
||||
return new (ctx) SmPlane(*a);
|
||||
const SmAxis2Placement& pos = a->GetPosition();
|
||||
SmPlane* c = new (ctx) SmPlane(pos.GetOrigin(), pos.GetXAxis(), pos.GetYAxis(), a->GetUVScale(),
|
||||
a->GetSTEPUVDomain(), &ctx);
|
||||
c->GetOutOfBoundsEnabled() = a->IsOutOfBoundsEnabled();
|
||||
return c;
|
||||
}
|
||||
|
||||
// type O X Y Z domain scale oob kt form, then the NURB (made on a copy when missing).
|
||||
void put_state(std::vector<double>& o, const SmContext& ctx, const SmPlane* a)
|
||||
{
|
||||
const SmAxis2Placement& pos = a->GetPosition();
|
||||
o.push_back((double)a->GetType());
|
||||
put_vec(o, pos.GetOrigin());
|
||||
put_vec(o, pos.GetXAxis());
|
||||
put_vec(o, pos.GetYAxis());
|
||||
put_vec(o, pos.GetZAxis());
|
||||
put_dom(o, a->GetSTEPUVDomain());
|
||||
SmVector2d sc = a->GetUVScale();
|
||||
put_uv(o, sc);
|
||||
o.push_back(flg(a->IsOutOfBoundsEnabled()));
|
||||
o.push_back((double)(a->*PeekS::kt()));
|
||||
o.push_back((double)(a->*PeekS::form()));
|
||||
const gw_SURFACE* sur = a->GetGwNurbPointer();
|
||||
if (sur)
|
||||
{
|
||||
put_surface_nurb(o, sur);
|
||||
return;
|
||||
}
|
||||
SmPlane* tmp = side_free_copy(ctx, a);
|
||||
put_surface_nurb(o, tmp->GetOrCreateGwNurbPointer());
|
||||
delete tmp;
|
||||
}
|
||||
|
||||
void put_curve_nurb(std::vector<double>& o, const SmBSplineCurve* c)
|
||||
{
|
||||
o.push_back((double)(c->*PeekC::kt()));
|
||||
o.push_back((double)(c->*PeekC::form()));
|
||||
const gw_CURVE* cur = c->GetGwNurbPointer();
|
||||
o.push_back(cur ? 1.0 : 0.0);
|
||||
if (!cur)
|
||||
return;
|
||||
gw_INDEX n = cur->pol->n;
|
||||
gw_INDEX m = cur->knt->m;
|
||||
o.push_back((double)n);
|
||||
o.push_back((double)cur->p);
|
||||
o.push_back((double)m);
|
||||
for (gw_INDEX i = 0; i <= n; i++)
|
||||
{
|
||||
o.push_back(cur->pol->Pw[i].x);
|
||||
o.push_back(cur->pol->Pw[i].y);
|
||||
o.push_back(cur->pol->Pw[i].z);
|
||||
o.push_back(cur->pol->Pw[i].w);
|
||||
}
|
||||
for (gw_INDEX i = 0; i <= m; i++)
|
||||
o.push_back(cur->knt->U[i]);
|
||||
}
|
||||
|
||||
// type dim [line: P V dom scale inside_out] B-spline part, natural interval.
|
||||
void put_curve(std::vector<double>& o, const SmCurve* c)
|
||||
{
|
||||
o.push_back((double)c->GetType());
|
||||
o.push_back((double)c->GetDim());
|
||||
if (c->IsKindOf(SmLine_TYPE))
|
||||
{
|
||||
const SmLine* l = (const SmLine*)c;
|
||||
put_vec(o, l->GetLinePoint());
|
||||
put_vec(o, l->GetLineVector());
|
||||
put_ivl(o, l->GetSTEPInterval());
|
||||
o.push_back(l->GetLineScale());
|
||||
o.push_back(l->GetInsideOut() ? 1.0 : 0.0);
|
||||
}
|
||||
if (!c->IsKindOf(SmBSplineCurve_TYPE))
|
||||
throw Bad();
|
||||
put_curve_nurb(o, (const SmBSplineCurve*)c);
|
||||
put_ivl(o, c->GetNaturalInterval());
|
||||
}
|
||||
|
||||
double obj_id(const SmObject* p, const SmObject* a)
|
||||
{
|
||||
if (p == NULL)
|
||||
return -1.0;
|
||||
return p == a ? 0.0 : 1.0;
|
||||
}
|
||||
|
||||
void put_sols(std::vector<double>& o, SmSolutionArray& sols, const SmObject* a)
|
||||
{
|
||||
o.push_back((double)sols.GetSize());
|
||||
for (ULONG i = 0; i < sols.GetSize(); i++)
|
||||
{
|
||||
const SmSolution& s = sols[i];
|
||||
o.push_back((double)s.m_eSolutionType);
|
||||
o.push_back((double)s.m_lNumVariables);
|
||||
o.push_back((double)s.m_lNumObjects);
|
||||
o.push_back(s.m_lNumObjects > 0 ? obj_id(s.m_apObjects[0], a) : -1.0);
|
||||
o.push_back(s.m_vStart.m_dSolutionValue);
|
||||
for (ULONG k = 0; k < 3; k++)
|
||||
o.push_back(k < s.m_lNumVariables ? s.m_vStart[k] : 0.0);
|
||||
bool range = s.m_eSolutionType == SM_ST_RANGE_OF_VALUES;
|
||||
o.push_back(range ? s.m_vEnd.m_dSolutionValue : 0.0);
|
||||
for (ULONG k = 0; k < 3; k++)
|
||||
o.push_back(range && k < s.m_lNumVariables ? s.m_vEnd[k] : 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
// Domain `kind a b c d`: 0 STEP domain, 1 SmExtent2d(a, b, c, d), 2 natural domain (of a copy).
|
||||
SmExtent2d read_dom(Rd& rd, const SmContext& ctx, const SmPlane* a)
|
||||
{
|
||||
int64_t kind = rd.idx(0, 2);
|
||||
SmExtent2d d = rd.dom4();
|
||||
if (kind == 0)
|
||||
return a->GetSTEPUVDomain();
|
||||
if (kind == 2)
|
||||
{
|
||||
SmPlane* tmp = side_free_copy(ctx, a);
|
||||
SmExtent2d e = tmp->GetNaturalUVDomain();
|
||||
delete tmp;
|
||||
return e;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
// The file-static sm_IsGrazingIntersection of SmPlane.cpp, copied verbatim (not exported).
|
||||
SmBoolean is_grazing_intersection(const SmCurve* cpCurve, double dCurveParam, const SmLine* cpLine,
|
||||
double dLineParam)
|
||||
{
|
||||
SmVector3d sCurveEval[3];
|
||||
SmVector3d sLineEval[2];
|
||||
cpCurve->Evaluate(dCurveParam, 2, FALSE, sCurveEval);
|
||||
cpLine->Evaluate(dLineParam, 1, FALSE, sLineEval);
|
||||
if (!sCurveEval[1].IsParallelTo(sLineEval[1]))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
SmVector3d sCross(sCurveEval[1] * sCurveEval[2]);
|
||||
if (sCross.LengthSquared() > SM_EFF_ZERO_SQ)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void run_op(int32_t op, Rd& rd, const SmContext& ctx, SmPlane* a, std::vector<double>& o)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case OP_STATE:
|
||||
o.push_back(0.0);
|
||||
put_state(o, ctx, a);
|
||||
break;
|
||||
case OP_EVALUATE:
|
||||
{
|
||||
SmPoint2d uv = rd.uv();
|
||||
ULONG hu = (ULONG)rd.idx(0, 4);
|
||||
ULONG hv = (ULONG)rd.idx(0, 4);
|
||||
bool upper = rd.flag();
|
||||
if (upper && hu > hv)
|
||||
throw Bad();
|
||||
std::vector<SmVector3d> d((hu + 1) * (hv + 1));
|
||||
for (size_t i = 0; i < d.size(); i++)
|
||||
d[i].Set(0.0, 0.0, 0.0);
|
||||
SmStatus s = a->Evaluate(uv, hu, hv, TRUE, TRUE, upper ? TRUE : FALSE, d.data());
|
||||
o.push_back(st(s));
|
||||
if (s == SM_SUCCESS)
|
||||
for (size_t i = 0; i < d.size(); i++)
|
||||
put_vec(o, d[i]);
|
||||
break;
|
||||
}
|
||||
case OP_EVAL_POINT:
|
||||
{
|
||||
SmPoint2d uv = rd.uv();
|
||||
SmPoint3d p;
|
||||
SmStatus s = a->EvaluatePoint(uv, p);
|
||||
o.push_back(st(s));
|
||||
if (s == SM_SUCCESS)
|
||||
put_vec(o, p);
|
||||
break;
|
||||
}
|
||||
case OP_EVAL_FAST:
|
||||
{
|
||||
SmPoint2d uv = rd.uv();
|
||||
SmPoint3d p;
|
||||
SmStatus s = a->EvaluatePointFast(uv, p);
|
||||
o.push_back(st(s));
|
||||
if (s == SM_SUCCESS)
|
||||
put_vec(o, p);
|
||||
break;
|
||||
}
|
||||
case OP_NORMAL:
|
||||
{
|
||||
SmPoint2d uv = rd.uv();
|
||||
SmVector3d nv;
|
||||
SmStatus s = a->EvaluateNormal(uv, TRUE, TRUE, nv);
|
||||
o.push_back(st(s));
|
||||
if (s == SM_SUCCESS)
|
||||
put_vec(o, nv);
|
||||
break;
|
||||
}
|
||||
case OP_PROJECT:
|
||||
{
|
||||
SmPoint3d p = rd.vec();
|
||||
SmPoint2d uv;
|
||||
SmStatus s = a->ProjectPointToUVDomain(p, uv);
|
||||
o.push_back(st(s));
|
||||
if (s == SM_SUCCESS)
|
||||
put_uv(o, uv);
|
||||
break;
|
||||
}
|
||||
case OP_QUERIES:
|
||||
{
|
||||
SmPoint2d uv = rd.uv();
|
||||
o.push_back(0.0);
|
||||
o.push_back(flg(a->IsBounded()));
|
||||
SmPoint3d pp;
|
||||
SmVector3d pn;
|
||||
double dev = -7.0;
|
||||
o.push_back(flg(a->IsPlanar(SM_EFF_ZERO, &pp, &pn, &dev)));
|
||||
put_vec(o, pp);
|
||||
put_vec(o, pn);
|
||||
o.push_back(dev);
|
||||
SmSurfParamType sd = SM_SP_UNKNOWN;
|
||||
SmTArray<SmPoint2d> cross;
|
||||
cross.Add(uv);
|
||||
o.push_back(flg(a->IsOnSeam(uv, NULL, &sd, &cross)));
|
||||
o.push_back((double)sd);
|
||||
o.push_back((double)cross.GetSize());
|
||||
SmContinuityType ct = SM_CT_UNDEFINED;
|
||||
o.push_back(flg(a->IsClosed(a->GetSTEPUVDomain(), SM_SP_U, NULL, &ct)));
|
||||
o.push_back((double)ct);
|
||||
SmSurfParamType d1 = SM_SP_UNKNOWN;
|
||||
o.push_back(flg(a->IsSingularity(uv, d1)));
|
||||
o.push_back((double)d1);
|
||||
SmSurfParamType d2 = SM_SP_UNKNOWN;
|
||||
o.push_back(flg(a->IsSingularity(uv, 0, NULL, d2)));
|
||||
o.push_back((double)d2);
|
||||
o.push_back((double)a->GetType());
|
||||
put_chars(o, a->GetTypeString());
|
||||
o.push_back(-1.0);
|
||||
put_chars(o, a->GetClassString());
|
||||
break;
|
||||
}
|
||||
case OP_KIND_OF:
|
||||
{
|
||||
double t = (double)rd.idx(0, 100000);
|
||||
o.push_back(0.0);
|
||||
o.push_back(flg(a->IsKindOf((SM_TYPE)t)));
|
||||
break;
|
||||
}
|
||||
case OP_ADJUST:
|
||||
{
|
||||
SmExtent2d d = read_dom(rd, ctx, a);
|
||||
o.push_back(st(a->AdjustSTEPUVDomain(d)));
|
||||
break;
|
||||
}
|
||||
case OP_MAKE_NURB: o.push_back(st(a->MakeNurb())); break;
|
||||
case OP_REPARAM:
|
||||
{
|
||||
SmExtent2d d = read_dom(rd, ctx, a);
|
||||
o.push_back(st(a->Reparameterize(d)));
|
||||
break;
|
||||
}
|
||||
case OP_TOGGLE:
|
||||
a->ToggleSwapUVBit();
|
||||
o.push_back(0.0);
|
||||
break;
|
||||
case OP_REBUILD: o.push_back(st(a->RebuildSTEPFromNURBParameters())); break;
|
||||
case OP_DROP:
|
||||
{
|
||||
SmExtent2d d = read_dom(rd, ctx, a);
|
||||
SmSolverOperationType sop = (SmSolverOperationType)rd.idx(0, 5);
|
||||
SmPoint3d p = rd.vec();
|
||||
double tol = rd.num();
|
||||
bool has = rd.flag();
|
||||
double target = rd.num();
|
||||
SmSolutionArray sols;
|
||||
SmStatus s = a->DropPointFast(d, sop, p, NULL, tol, has ? &target : NULL, SM_SR_ALL, sols);
|
||||
o.push_back(st(s));
|
||||
if (s == SM_SUCCESS)
|
||||
put_sols(o, sols, a);
|
||||
break;
|
||||
}
|
||||
case OP_LINE:
|
||||
{
|
||||
SmExtent2d d = read_dom(rd, ctx, a);
|
||||
SmPoint3d p = rd.vec();
|
||||
SmVector3d v = rd.vec();
|
||||
bool has = rd.flag();
|
||||
double i0 = rd.num();
|
||||
double i1 = rd.num();
|
||||
bool ray = rd.flag();
|
||||
double tol = rd.num();
|
||||
SmExtent1d ivl(i0, i1);
|
||||
SmSolutionArray sols;
|
||||
SmStatus s = a->GlobalLineIntersect(d, p, v, has ? &ivl : NULL, ray ? TRUE : FALSE, tol, sols);
|
||||
o.push_back(st(s));
|
||||
if (s == SM_SUCCESS)
|
||||
put_sols(o, sols, a);
|
||||
break;
|
||||
}
|
||||
case OP_TRIM_LINE:
|
||||
{
|
||||
SmExtent2d d = read_dom(rd, ctx, a);
|
||||
SmPoint3d p0 = rd.vec();
|
||||
SmPoint3d p1 = rd.vec();
|
||||
double tol = rd.num();
|
||||
SmPoint3d t0, t1;
|
||||
SmBoolean found = FALSE;
|
||||
SmStatus s = a->TrimPlaneLineToPlaneDomain(d, p0, p1, tol, t0, t1, found);
|
||||
o.push_back(st(s));
|
||||
if (s == SM_SUCCESS)
|
||||
{
|
||||
o.push_back(flg(found));
|
||||
if (found)
|
||||
{
|
||||
put_vec(o, t0);
|
||||
put_vec(o, t1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_ISO:
|
||||
{
|
||||
SmSurfParamType param = rd.idx(0, 1) == 0 ? SM_SP_U : SM_SP_V;
|
||||
double iso = rd.num();
|
||||
bool has = rd.flag();
|
||||
SmExtent2d d = rd.dom4();
|
||||
bool want = rd.flag();
|
||||
SmBSplineCurve* c = NULL;
|
||||
SmCurve* uv = NULL;
|
||||
double gap = -7.0;
|
||||
SmStatus s = a->CreateIsoParametricCurve(ctx, param, iso, 0.0, c, has ? &d : NULL, &gap, want ? &uv : NULL);
|
||||
o.push_back(st(s));
|
||||
if (s == SM_SUCCESS)
|
||||
{
|
||||
if (c == NULL)
|
||||
throw Bad();
|
||||
put_curve(o, c);
|
||||
o.push_back(gap);
|
||||
o.push_back(uv ? 1.0 : 0.0);
|
||||
if (uv)
|
||||
put_curve(o, uv);
|
||||
}
|
||||
delete c;
|
||||
delete uv;
|
||||
break;
|
||||
}
|
||||
case OP_DROP_CURVE:
|
||||
{
|
||||
SmExtent2d d = read_dom(rd, ctx, a);
|
||||
SmCurve* c = read_curve(rd, ctx);
|
||||
bool given = rd.flag();
|
||||
double i0 = rd.num();
|
||||
double i1 = rd.num();
|
||||
double tol = rd.num();
|
||||
bool keep = rd.flag();
|
||||
bool creating = rd.flag();
|
||||
SmExtent1d ivl = given ? SmExtent1d(i0, i1) : c->GetNaturalInterval();
|
||||
double dmax = -7.0;
|
||||
double ddev = -7.0;
|
||||
SmTArray<SmBSplineCurve*> arr;
|
||||
SmStatus s = a->DropCurve(ctx, d, *c, ivl, tol, dmax, ddev, arr, keep ? TRUE : FALSE, NULL,
|
||||
creating ? TRUE : FALSE);
|
||||
o.push_back(st(s));
|
||||
if (s == SM_SUCCESS)
|
||||
{
|
||||
o.push_back(dmax);
|
||||
o.push_back(ddev);
|
||||
o.push_back((double)arr.GetSize());
|
||||
for (ULONG i = 0; i < arr.GetSize(); i++)
|
||||
put_curve(o, arr[i]);
|
||||
}
|
||||
for (ULONG i = 0; i < arr.GetSize(); i++)
|
||||
delete arr[i];
|
||||
delete c;
|
||||
break;
|
||||
}
|
||||
case OP_COINCIDENCE:
|
||||
{
|
||||
SmSurface* other = read_surface(rd, ctx);
|
||||
double tol = rd.num();
|
||||
bool whole = rd.flag();
|
||||
SmBoolean coinc = UNSURE;
|
||||
double dist = -7.0;
|
||||
SmSurface* containing = NULL;
|
||||
SmStatus s = a->CoincidenceCheck(*other, tol, coinc, dist, whole ? TRUE : FALSE, containing);
|
||||
o.push_back(st(s));
|
||||
if (s == SM_SUCCESS)
|
||||
{
|
||||
o.push_back(flg(coinc));
|
||||
o.push_back(dist);
|
||||
}
|
||||
delete other;
|
||||
break;
|
||||
}
|
||||
case OP_EQ:
|
||||
{
|
||||
SmStatus ms = SM_SUCCESS;
|
||||
SmPlane* b = read_plane(rd, ctx, &ms);
|
||||
if (b == NULL)
|
||||
throw Bad();
|
||||
o.push_back(0.0);
|
||||
o.push_back(flg(*a == *b));
|
||||
delete b;
|
||||
break;
|
||||
}
|
||||
case OP_COPY:
|
||||
{
|
||||
SmPlane* c = NULL;
|
||||
SmStatus s = a->Copy(ctx, c);
|
||||
o.push_back(st(s));
|
||||
if (s == SM_SUCCESS)
|
||||
put_state(o, ctx, c);
|
||||
delete c;
|
||||
break;
|
||||
}
|
||||
case OP_GRAZING:
|
||||
{
|
||||
SmCurve* c = read_curve(rd, ctx);
|
||||
double cp = rd.num();
|
||||
SmPoint3d l0 = rd.vec();
|
||||
SmPoint3d l1 = rd.vec();
|
||||
double lp = rd.num();
|
||||
SmLine* l = NULL;
|
||||
if (SmLine::CreateLineSegment(ctx, 3, l0, l1, l) != SM_SUCCESS || l == NULL)
|
||||
throw Bad();
|
||||
o.push_back(0.0);
|
||||
o.push_back(flg(is_grazing_intersection(c, cp, l, lp)));
|
||||
delete l;
|
||||
delete c;
|
||||
break;
|
||||
}
|
||||
case OP_INHERITED:
|
||||
{
|
||||
SmVector3d v = rd.vec();
|
||||
SmPlane* tmp = side_free_copy(ctx, a);
|
||||
o.push_back(0.0);
|
||||
put_dom(o, tmp->GetNaturalUVDomain());
|
||||
put_dom(o, tmp->GetMaxAnalyticDomain());
|
||||
o.push_back(flg(tmp->IsBilinear()));
|
||||
o.push_back(flg(tmp->IsParallelToVector(v)));
|
||||
o.push_back((double)tmp->GetDegree(SM_SP_U));
|
||||
o.push_back((double)tmp->GetDegree(SM_SP_V));
|
||||
o.push_back((double)tmp->GetNumberControlPoints(SM_SP_U));
|
||||
o.push_back((double)tmp->GetNumberControlPoints(SM_SP_V));
|
||||
delete tmp;
|
||||
break;
|
||||
}
|
||||
case OP_REVERSE:
|
||||
{
|
||||
SmSurfParamType param = rd.idx(0, 1) == 0 ? SM_SP_U : SM_SP_V;
|
||||
o.push_back(st(a->Reverse(param)));
|
||||
break;
|
||||
}
|
||||
case OP_SWAP: o.push_back(st(a->SwapUV())); break;
|
||||
default: throw Bad();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
SC_EXPORT int32_t sc_surface_plane_p1_run(const double* in, int64_t n_in, double* out, int64_t cap,
|
||||
int64_t* out_len)
|
||||
{
|
||||
if (in == NULL || n_in < 2)
|
||||
return SC_ERR_ARG;
|
||||
try
|
||||
{
|
||||
SmContext ctx;
|
||||
std::vector<double> o;
|
||||
Rd rd{in, n_in, 0};
|
||||
SmStatus ms = SM_SUCCESS;
|
||||
SmPlane* a = read_plane(rd, ctx, &ms);
|
||||
o.push_back(st(ms));
|
||||
if (a != NULL)
|
||||
{
|
||||
int64_t n_ops = rd.idx(0, 64);
|
||||
for (int64_t i = 0; i < n_ops; i++)
|
||||
{
|
||||
int32_t op = (int32_t)rd.idx(1, OP_LAST);
|
||||
o.push_back((double)op);
|
||||
run_op(op, rd, ctx, a, o);
|
||||
}
|
||||
put_state(o, ctx, a);
|
||||
delete a;
|
||||
}
|
||||
return sc_copy_out(o.data(), (int64_t)o.size(), out, cap, out_len);
|
||||
}
|
||||
catch (const Bad&)
|
||||
{
|
||||
return SC_ERR_ARG;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return SC_ERR_EXCEPTION;
|
||||
}
|
||||
}
|
||||
@@ -2539,3 +2539,38 @@ fixtures use Surface.sms and offset_nurb.sms from the allowed upstream corpus.
|
||||
The C++ shim deliberately excludes classifying a surface without a face: upstream
|
||||
would dereference null. FFI regression tests reject this path, truncated/trailing
|
||||
records, invalid counts and non-finite coordinates before entering C++.
|
||||
|
||||
## surface.plane_p1 (`SmPlane`, part 1)
|
||||
|
||||
All non-deferred cases agree bit for bit with upstream (`max_rel_err = 0`). Upstream
|
||||
behaviour worth knowing:
|
||||
|
||||
- **`SmPlane::Reparameterize` keeps the origin.** The new origin (`sOriginMax`) is only
|
||||
computed inside `SM_DEBUG_CODE`. The release build changes the analytic domain and the
|
||||
scales but keeps `m_vPosition`, so the analytic plane no longer matches its NURB unless
|
||||
the new domain starts where the old one did. The port keeps this (fixture
|
||||
`edge_reparameterize_keeps_origin`).
|
||||
- **Lazy NURB through `const_cast`.** An `SmPlane` built by its constructors has no NURB.
|
||||
`const` methods that need it (`GetNaturalUVDomain`, so `CoincidenceCheck`,
|
||||
`CreateIsoParametricCurve`, `DropPointFast(SM_SO_NORMALIZE)` via `SmTol::MapTo2d`) make
|
||||
it on `this` through `GetOrCreateGwNurbPointer`. So does the `SmBSplineSurface` copy
|
||||
constructor, on the *source*. The port makes it on a copy (`Plane::base_with_nurb`), so
|
||||
the source keeps none. Only `ToggleSwapUVBit` (which leaves the NURB alone) and
|
||||
`operator==` (which compares NURBs) can see the difference. The runner generates those
|
||||
two only while both sides agree. The shim copies planes without a NURB by rebuilding
|
||||
them from their analytic data, because its own copies would otherwise change plane A.
|
||||
- **`SmPlane::Evaluate` with `bOnlyUpperHalf` and more u than v derivatives** writes
|
||||
outside its array (the `ULONG` index `lHighestVDeriv - k` wraps). The port returns
|
||||
`InvalidInput`, and the ffi wrapper rejects that input.
|
||||
- **Deferred paths:** `CoincidenceCheck` with a surface that is not an `SmPlane` needs
|
||||
`SmSurface::IsPlanar` (`surface.surface_p7`), then `CreateNaturalUVTrimCurves`
|
||||
(`surface.surface_p6`) and `DropPoint` (`surface.surface_p5`). `DropCurve` with a curve
|
||||
whose control points must be clamped to the domain, or with a curve that is neither a
|
||||
line nor a B-spline kind, needs the general `SmSurface::DropCurve`
|
||||
(`intersect.surface_drop_lib_p3`). `GlobalLineIntersect` of a line lying in the plane
|
||||
that meets the domain at a single line parameter needs the general
|
||||
`SmSurface::GlobalLineIntersect` through the surface cache (`surface.surface_p8`).
|
||||
- **`SmSurfaceCache` and planes.** The surface cache port treats its surface as a plain
|
||||
B-spline. For an `SmPlane` it returns `SolidError::Unsupported`
|
||||
(`CreateIsoParametricCurve`, which makes an `SmLine`, and the plane branch of
|
||||
`CalculateBoundingBox`), or refuses the B-spline cast.
|
||||
|
||||
+2
-2
@@ -1453,10 +1453,10 @@ depends_on = ["curve.curve_p2", "curve.curve_p4", "curve.curve_p5", "curve.curve
|
||||
est_lines = 1934
|
||||
layer = 23
|
||||
tests = ["prog_test:my_test_extent3d", "prog_test:my_test_vector2d", "prog_test:my_test_surface_closest_point", "prog_test:my_test_drop_point", "prog_test:my_test_matrix", "prog_test:my_test_reversed_line_join"]
|
||||
status = "todo"
|
||||
status = "partial"
|
||||
claimed_by = ""
|
||||
claimed_at = ""
|
||||
notes = "39 fns; largest: SmPlane::CoincidenceCheck, SmPlane::DropCurve, SmPlane::GlobalLineIntersect, SmPlane::DropPointFast; cycle back-edges (call into later units; stub or co-port): surface.surface_p1(2), surface.surface_p7(2), intersect.surface_drop_lib_p3(1), surface.surface_p5(1), surface.surface_p6(1), surface.surface_p8(1); unit-SCC #22 (220 units); 319 upstream test entry points reach this unit"
|
||||
notes = "partial: difftest 1518 cases (1391 passed, 127 deferred: intersect.surface_drop_lib_p3 83, surface.surface_p7 42, surface.surface_p8 2), max_rel_err 0; 39 fns; largest: SmPlane::CoincidenceCheck, SmPlane::DropCurve, SmPlane::GlobalLineIntersect, SmPlane::DropPointFast; cycle back-edges (call into later units; stub or co-port): surface.surface_p1(2), surface.surface_p7(2), intersect.surface_drop_lib_p3(1), surface.surface_p5(1), surface.surface_p6(1), surface.surface_p8(1); unit-SCC #22 (220 units); 319 upstream test entry points reach this unit"
|
||||
|
||||
[[unit]]
|
||||
id = "surface.surf_of_revolution_p1"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user