mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-08 04:52:10 +00:00
surface: the fitted extension path is deleted; the exact extension is the only one
Deleted: Surface/Plane/OffsetSurface/BSplineSurface create_extended_surface(_dir), with create_extended_surface_dispatched, extend_side and MAX_EXTEND_HALVINGS/DOUBLINGS (CreateExtendedSurface on every kind), srf_adv::p1::extend_by_dist (N_SrfExtendByDist) and srf_adv::p2's ST_surxsdr (extend_by_dist_at_depth/_in_place, SrfContinuity, MAX_EXTEND_DEPTH). Their only callers were each other: every caller extends through surface::extend since the switch. remove_knot_conditional and extend_by_param_dist stay. difftest: their ops are removed with their FFI ops, C++ shim cases, fixtures and fuzz corpus entries (nlib.srf_adv_p1/p2, surface.surface_p2, surface.plane_p1, nurbs.b_spline_surface_n_lib_p1, surface.surf_of_extrusion_p2, surface.offset_surface; each unit passes in full; removed shim op numbers are left unused). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NvoxKMbMFSQU8tzRtoJhtT
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
//! Runner for unit `nlib.srf_adv_p1`: NLib surface extension by a distance, offset
|
||||
//! second-derivative bounds, unbounded evaluation, skinning along a spine and
|
||||
//! cross-boundary derivative strips from a vector field.
|
||||
//! Runner for unit `nlib.srf_adv_p1`: NLib offset second-derivative bounds, unbounded
|
||||
//! evaluation, skinning along a spine and cross-boundary derivative strips from a
|
||||
//! vector field.
|
||||
//!
|
||||
//! C++: `solidcore_ffi::units::nlib_srf_adv_p1` (upstream
|
||||
//! `source/SMLib/src/NL_SrfAdv.cpp`). Rust: `solidcore::nurbs::srf_adv::p1`.
|
||||
@@ -10,7 +10,7 @@
|
||||
//! number, `1000` for upstream's bare error flag; `-1` on the Rust side for any other
|
||||
//! error, which never matches), then on success the results: the surface
|
||||
//! (`dims = [n, m, p, q, r, s]`, `net` x y z w row-major, `u`, `v`) for
|
||||
//! `extend_by_dist`, `skin_spine` (followed by every profile after the call,
|
||||
//! `skin_spine` (followed by every profile after the call,
|
||||
//! `c<i>dims = [n, p, m]`, `c<i>net`, `c<i>u`, since upstream modifies them in place)
|
||||
//! and `vector_field`; `max = [Muu, Muv, Mvv]` for `max_deriv`. `unbounded` runs a
|
||||
//! sequence of evaluations sharing the extension (`esur` / `gflg` upstream): per
|
||||
@@ -27,11 +27,10 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use solidcore::math::Vec3;
|
||||
use solidcore::nurbs::crv_adv::p1::{CurveEnd, BARE_FLAG_REASON};
|
||||
use solidcore::nurbs::crv_adv::p1::BARE_FLAG_REASON;
|
||||
use solidcore::nurbs::crv_basic::{Boundary, Curve};
|
||||
use solidcore::nurbs::math::{CPoint, NOW, NOZ};
|
||||
use solidcore::nurbs::srf_adv::p1 as adv;
|
||||
use solidcore::nurbs::srf_adv::p2::SrfContinuity;
|
||||
use solidcore::nurbs::srf_adv::p3::ExtendContinuity;
|
||||
use solidcore::nurbs::srf_basic::{Direction, Surface};
|
||||
use solidcore::{Result as SolidResult, SolidError};
|
||||
@@ -93,15 +92,6 @@ enum CrvIn {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "op", rename_all = "snake_case")]
|
||||
enum Inputs {
|
||||
ExtendByDist {
|
||||
sur: SrfIn,
|
||||
dist: f64,
|
||||
dir: i32,
|
||||
end: i32,
|
||||
cont: i32,
|
||||
#[serde(default)]
|
||||
inplace: bool,
|
||||
},
|
||||
MaxDeriv {
|
||||
sur: SrfIn,
|
||||
d: f64,
|
||||
@@ -323,24 +313,6 @@ fn to_output(i: &Inputs, flat: &[f64]) -> Result<Output, HarnessError> {
|
||||
fn cpp_input(i: &Inputs) -> Result<(Op, Vec<f64>), HarnessError> {
|
||||
let mut o = Vec::new();
|
||||
let op = match i {
|
||||
Inputs::ExtendByDist {
|
||||
sur,
|
||||
dist,
|
||||
dir,
|
||||
end,
|
||||
cont,
|
||||
inplace,
|
||||
} => {
|
||||
encode(&resolve(sur)?, &mut o)?;
|
||||
o.extend([
|
||||
*dist,
|
||||
f64::from(*dir),
|
||||
f64::from(*end),
|
||||
f64::from(*cont),
|
||||
flag(*inplace),
|
||||
]);
|
||||
Op::ExtendByDist
|
||||
}
|
||||
Inputs::MaxDeriv { sur, d } => {
|
||||
encode(&resolve(sur)?, &mut o)?;
|
||||
o.push(*d);
|
||||
@@ -400,21 +372,7 @@ const BARE_FLAG: f64 = 1000.0;
|
||||
fn cpp(i: &Inputs) -> Result<Output, HarnessError> {
|
||||
let (op, input) = cpp_input(i)?;
|
||||
match ffi::eval(op, &input) {
|
||||
Ok(mut flat) => {
|
||||
// Past its recursion limit `ST_surxsdr` returns a bare error flag and
|
||||
// `NL_ERROR` still holds whatever an earlier, recovered error left there
|
||||
// (plan/findings.md, nlib.srf_adv_p2). When the port reports the recursion
|
||||
// limit, an upstream failure is compared as the bare flag.
|
||||
if matches!(i, Inputs::ExtendByDist { .. })
|
||||
&& flat.first().is_some_and(|&st| st != 0.0)
|
||||
&& rust_flat(i)?.first() == Some(&BARE_FLAG)
|
||||
{
|
||||
if let Some(st) = flat.first_mut() {
|
||||
*st = BARE_FLAG;
|
||||
}
|
||||
}
|
||||
to_output(i, &flat)
|
||||
}
|
||||
Ok(flat) => to_output(i, &flat),
|
||||
Err(FfiError::InvalidArgument(m)) => Err(harness(format!("ffi rejected: {m}"))),
|
||||
Err(e) => Err(harness(format!("shim failure: {e}"))),
|
||||
}
|
||||
@@ -429,11 +387,6 @@ fn status<T>(r: &SolidResult<T>) -> f64 {
|
||||
Ok(_) => 0.0,
|
||||
Err(SolidError::Nlib { code, .. }) => f64::from(code.code()),
|
||||
Err(SolidError::InvalidInput { reason, .. } | SolidError::PortInvariant { reason, .. }) if *reason == BARE_FLAG_REASON => BARE_FLAG,
|
||||
Err(SolidError::NotConverged { operation, .. })
|
||||
if operation.ends_with("srf_adv::p2::extend_by_dist_at_depth") =>
|
||||
{
|
||||
BARE_FLAG
|
||||
}
|
||||
Err(_) => -1.0,
|
||||
}
|
||||
}
|
||||
@@ -441,23 +394,6 @@ fn status<T>(r: &SolidResult<T>) -> f64 {
|
||||
fn rust_flat(i: &Inputs) -> Result<Vec<f64>, HarnessError> {
|
||||
let mut o = Vec::new();
|
||||
match i {
|
||||
Inputs::ExtendByDist {
|
||||
sur,
|
||||
dist,
|
||||
dir,
|
||||
end,
|
||||
cont,
|
||||
..
|
||||
} => {
|
||||
let s = build(&resolve(sur)?)?;
|
||||
let end = CurveEnd::from_flag(*end).ok_or_else(|| harness("bad end flag"))?;
|
||||
let cont = SrfContinuity::from_flag(*cont).ok_or_else(|| harness("bad cont flag"))?;
|
||||
let r = adv::extend_by_dist(&s, *dist, direction(*dir)?, end, cont);
|
||||
o.push(status(&r));
|
||||
if let Ok(q) = r {
|
||||
dump(&mut o, &q);
|
||||
}
|
||||
}
|
||||
Inputs::MaxDeriv { sur, d } => {
|
||||
let s = build(&resolve(sur)?)?;
|
||||
let r = adv::offset_get_max_deriv(&s, *d);
|
||||
@@ -828,32 +764,6 @@ fn cont_flag(rng: &mut Rng) -> i32 {
|
||||
*rng.choose(&[2, 3, 4, 6]).unwrap_or(&2)
|
||||
}
|
||||
|
||||
fn gen_extend(rng: &mut Rng) -> Inputs {
|
||||
let s = gen_srf(rng);
|
||||
let dist = match rng.below(10) {
|
||||
0 => 1e-6,
|
||||
1 => rng.uniform(3.0, 20.0),
|
||||
_ => rng.uniform(0.01, 3.0),
|
||||
};
|
||||
let dir = 1 + rng.below(2) as i32;
|
||||
let mut cont = cont_flag(rng);
|
||||
// A degree-1 extension direction makes upstream's reflection-plane fallback read
|
||||
// past its scratch row (plan/findings.md, nlib.srf_adv_p2); the port reports
|
||||
// `InvalidInput` there. Keep degree-1 extensions to C-max.
|
||||
let ext_deg = if dir == 1 { s.p } else { s.q };
|
||||
if ext_deg == 1 && cont != 4 {
|
||||
cont = 4;
|
||||
}
|
||||
Inputs::ExtendByDist {
|
||||
sur: SrfIn::Inline(s),
|
||||
dist,
|
||||
dir,
|
||||
end: 1 + rng.below(2) as i32,
|
||||
cont,
|
||||
inplace: rng.chance(0.3),
|
||||
}
|
||||
}
|
||||
|
||||
fn unbounded_param(rng: &mut Rng, k: &[f64]) -> f64 {
|
||||
let a = k.first().copied().unwrap_or(0.0);
|
||||
let b = k.last().copied().unwrap_or(1.0);
|
||||
@@ -1071,17 +981,16 @@ fn gen_vector_field(rng: &mut Rng) -> Inputs {
|
||||
}
|
||||
|
||||
fn gen_case(rng: &mut Rng) -> Inputs {
|
||||
match rng.below(10) {
|
||||
0 | 1 => gen_extend(rng),
|
||||
2 => Inputs::MaxDeriv {
|
||||
match rng.below(8) {
|
||||
0 => Inputs::MaxDeriv {
|
||||
sur: SrfIn::Inline(gen_srf(rng)),
|
||||
d: match rng.below(4) {
|
||||
0 => 0.0,
|
||||
_ => rng.uniform(-3.0, 3.0),
|
||||
},
|
||||
},
|
||||
3..=5 => gen_unbounded(rng),
|
||||
6..=8 => gen_skin_spine(rng),
|
||||
1..=3 => gen_unbounded(rng),
|
||||
4..=6 => gen_skin_spine(rng),
|
||||
_ => gen_vector_field(rng),
|
||||
}
|
||||
}
|
||||
@@ -1121,25 +1030,23 @@ mod tests {
|
||||
/// closed spine skins with end and all derivatives.
|
||||
#[test]
|
||||
fn generated_cases_cover_outcomes() {
|
||||
// [extend ok, extend err, maxd ok, unb ok, unb err, unb wrapped, unb ext,
|
||||
// spine none ok, spine ends ok, spine all ok, spine err, spine rational ok,
|
||||
// vfield ok, vfield err]
|
||||
let mut stats = [0usize; 14];
|
||||
// [maxd ok, unb ok, unb err, unb wrapped, unb ext, spine none ok, spine ends ok,
|
||||
// spine all ok, spine err, spine rational ok, vfield ok, vfield err]
|
||||
let mut stats = [0usize; 12];
|
||||
for seed in 0..3000u64 {
|
||||
let mut rng = Rng::new(seed);
|
||||
let i = gen_case(&mut rng);
|
||||
let flat = rust_flat(&i).unwrap();
|
||||
let ok = flat.first() == Some(&0.0);
|
||||
match &i {
|
||||
Inputs::ExtendByDist { .. } => stats[usize::from(!ok)] += 1,
|
||||
Inputs::MaxDeriv { .. } => stats[2] += usize::from(ok),
|
||||
Inputs::MaxDeriv { .. } => stats[0] += usize::from(ok),
|
||||
Inputs::Unbounded { .. } => {
|
||||
let out = to_output(&i, &flat).unwrap();
|
||||
let all_ok = out.values.contains_key("has_ext");
|
||||
stats[3 + usize::from(!all_ok)] += 1;
|
||||
stats[1 + usize::from(!all_ok)] += 1;
|
||||
match out.values.get("gflg").and_then(|g| g.first()) {
|
||||
Some(&g) if g > 0.0 => stats[5] += 1,
|
||||
Some(_) => stats[6] += 1,
|
||||
Some(&g) if g > 0.0 => stats[3] += 1,
|
||||
Some(_) => stats[4] += 1,
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
@@ -1148,19 +1055,19 @@ mod tests {
|
||||
} => {
|
||||
if ok {
|
||||
stats[match derivs {
|
||||
1 => 7,
|
||||
2 => 8,
|
||||
_ => 9,
|
||||
1 => 5,
|
||||
2 => 6,
|
||||
_ => 7,
|
||||
}] += 1;
|
||||
let c0 = resolve_crv(&curves[0]).unwrap();
|
||||
if c0.cpts[0][3] != NOW && *derivs != 1 {
|
||||
stats[11] += 1;
|
||||
stats[9] += 1;
|
||||
}
|
||||
} else {
|
||||
stats[10] += 1;
|
||||
stats[8] += 1;
|
||||
}
|
||||
}
|
||||
Inputs::VectorField { .. } => stats[12 + usize::from(!ok)] += 1,
|
||||
Inputs::VectorField { .. } => stats[10 + usize::from(!ok)] += 1,
|
||||
}
|
||||
}
|
||||
eprintln!("{stats:?}");
|
||||
|
||||
@@ -1,41 +1,30 @@
|
||||
//! Runner for unit `nlib.srf_adv_p2`: NLib surface extension by a distance
|
||||
//! (`ST_surxsdr`, the recursive core of `N_SrfExtendByDist`) and the derivatives of
|
||||
//! the unit surface normal (`N_SrfEvalPtNormalDeriv`).
|
||||
//! Runner for unit `nlib.srf_adv_p2`: the derivatives of the unit surface normal
|
||||
//! (`N_SrfEvalPtNormalDeriv`).
|
||||
//!
|
||||
//! C++: `solidcore_ffi::units::nlib_srf_adv_p2` (upstream
|
||||
//! `source/SMLib/src/NL_SrfAdv.cpp`). Rust: `solidcore::nurbs::srf_adv::p2`.
|
||||
//!
|
||||
//! Each case runs one *op* (`inputs.op`). Both sides produce the same flat record of
|
||||
//! doubles, split into keys by [`to_output`]: `status` (`0` ok, else the `NL_*_ERR`
|
||||
//! number; `1000` for upstream's bare error flag past the recursion limit, the Rust
|
||||
//! `NotConverged` of the extension; `-1` on the Rust side for any other non-NLib
|
||||
//! error, which never matches), then on success the results: the extended
|
||||
//! surface for `extend` (`dims = [n, m, p, q, r, s]`, the control points `net`, x y z
|
||||
//! w row-major, the knots `u`, `v`), and `nd` (`N_u, N_v, N_uu, N_uv, N_vv`, xyz each)
|
||||
//! for `normal_deriv`. Upstream's in-place mode (`inplace`, the way every upstream
|
||||
//! caller uses `N_SrfExtendByDist`) is compared against the same Rust call.
|
||||
//! number; `-1` on the Rust side for any non-NLib error, which never matches), then
|
||||
//! on success `nd` (`N_u, N_v, N_uu, N_uv, N_vv`, xyz each) for `normal_deriv`.
|
||||
//!
|
||||
//! Past its recursion limit upstream returns a bare error flag with a stale
|
||||
//! `NL_ERROR` number (left by an earlier recovered error or cleared by a callee; see
|
||||
//! `plan/findings.md`). When the Rust side reports the recursion limit, a failing C++
|
||||
//! side is therefore reported as the bare flag (`1000`) instead of that number; a
|
||||
//! C++ success still fails the case.
|
||||
//! The surface extension by a distance (`extend`, `ST_surxsdr`) was removed with the
|
||||
//! port's fitted extension.
|
||||
//!
|
||||
//! A surface input is inline (`net` rows of `[x, y, z, w]`, degrees, knots) or an
|
||||
//! upstream `.sms` file (`{"sms": {"$upstream_file": "Surface.sms"}}`). Inputs must be
|
||||
//! valid clamped surfaces (the FFI wrapper rejects anything else). Generated surfaces
|
||||
//! are random nets or smooth grid-like nets with noise; extension distances are drawn
|
||||
//! relative to the net size (small to several times the size, which exercises the
|
||||
//! recursion). Every run has at least [`MIN_GENERATED`] generated cases.
|
||||
//! are random nets or smooth grid-like nets with noise. Every run has at least
|
||||
//! [`MIN_GENERATED`] generated cases.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use solidcore::nurbs::crv_adv::p1::CurveEnd;
|
||||
use solidcore::nurbs::knots::Side;
|
||||
use solidcore::nurbs::math::{CPoint, NOW, NOZ};
|
||||
use solidcore::nurbs::srf_adv::p2::{self as adv, SrfContinuity};
|
||||
use solidcore::nurbs::srf_basic::{Direction, Surface};
|
||||
use solidcore::nurbs::srf_adv::p2 as adv;
|
||||
use solidcore::nurbs::srf_basic::Surface;
|
||||
use solidcore::{Result as SolidResult, SolidError};
|
||||
use solidcore_ffi::units::nlib_srf_adv_p2::{self as ffi, Op};
|
||||
use solidcore_ffi::FfiError;
|
||||
@@ -76,17 +65,6 @@ enum SrfIn {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "op", rename_all = "snake_case")]
|
||||
enum Inputs {
|
||||
Extend {
|
||||
sur: SrfIn,
|
||||
dist: f64,
|
||||
dir: i32,
|
||||
end: i32,
|
||||
cont: i32,
|
||||
#[serde(default)]
|
||||
depth: usize,
|
||||
#[serde(default)]
|
||||
inplace: bool,
|
||||
},
|
||||
NormalDeriv {
|
||||
sur: SrfIn,
|
||||
u: f64,
|
||||
@@ -99,7 +77,7 @@ enum Inputs {
|
||||
impl Inputs {
|
||||
fn sur(&self) -> &SrfIn {
|
||||
match self {
|
||||
Inputs::Extend { sur, .. } | Inputs::NormalDeriv { sur, .. } => sur,
|
||||
Inputs::NormalDeriv { sur, .. } => sur,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,33 +160,6 @@ fn take<'a>(flat: &'a [f64], at: &mut usize, k: usize) -> Result<&'a [f64], Harn
|
||||
Ok(part)
|
||||
}
|
||||
|
||||
fn count(x: f64) -> Result<usize, HarnessError> {
|
||||
if x.fract() != 0.0 || !(0.0..=1e7).contains(&x) {
|
||||
return Err(harness(format!("bad count {x} in record")));
|
||||
}
|
||||
Ok(x as usize)
|
||||
}
|
||||
|
||||
/// Splits a surface dump starting at `at` into `<pfx>dims`, `<pfx>net`, `<pfx>u`,
|
||||
/// `<pfx>v`.
|
||||
fn split_srf(
|
||||
flat: &[f64],
|
||||
at: &mut usize,
|
||||
pfx: &str,
|
||||
mut out: Output,
|
||||
) -> Result<Output, HarnessError> {
|
||||
let dims = take(flat, at, 6)?;
|
||||
let dim = |k: usize| count(dims.get(k).copied().unwrap_or(f64::NAN));
|
||||
let (n, m, r, s) = (dim(0)?, dim(1)?, dim(4)?, dim(5)?);
|
||||
out = out.values(&format!("{pfx}dims"), dims.to_vec());
|
||||
let net = take(flat, at, 4 * (n + 1) * (m + 1))?;
|
||||
out = out.values(&format!("{pfx}net"), net.to_vec());
|
||||
let u = take(flat, at, r + 1)?;
|
||||
out = out.values(&format!("{pfx}u"), u.to_vec());
|
||||
let v = take(flat, at, s + 1)?;
|
||||
Ok(out.values(&format!("{pfx}v"), v.to_vec()))
|
||||
}
|
||||
|
||||
fn to_output(i: &Inputs, flat: &[f64]) -> Result<Output, HarnessError> {
|
||||
let mut out = Output::new();
|
||||
let mut at = 0usize;
|
||||
@@ -217,7 +168,6 @@ fn to_output(i: &Inputs, flat: &[f64]) -> Result<Output, HarnessError> {
|
||||
out = out.values("status", status);
|
||||
if ok {
|
||||
match i {
|
||||
Inputs::Extend { .. } => out = split_srf(flat, &mut at, "", out)?,
|
||||
Inputs::NormalDeriv { .. } => out = out.values("nd", take(flat, &mut at, 15)?.to_vec()),
|
||||
}
|
||||
}
|
||||
@@ -236,25 +186,6 @@ fn cpp_input(i: &Inputs) -> Result<(Op, Vec<f64>), HarnessError> {
|
||||
let mut o = Vec::new();
|
||||
encode(&resolve(i.sur())?, &mut o)?;
|
||||
let op = match i {
|
||||
Inputs::Extend {
|
||||
dist,
|
||||
dir,
|
||||
end,
|
||||
cont,
|
||||
depth,
|
||||
inplace,
|
||||
..
|
||||
} => {
|
||||
o.extend([
|
||||
*dist,
|
||||
f64::from(*dir),
|
||||
f64::from(*end),
|
||||
f64::from(*cont),
|
||||
*depth as f64,
|
||||
if *inplace { 1.0 } else { 0.0 },
|
||||
]);
|
||||
Op::ExtendByDist
|
||||
}
|
||||
Inputs::NormalDeriv { u, v, ufl, vfl, .. } => {
|
||||
o.extend([*u, *v, f64::from(*ufl), f64::from(*vfl)]);
|
||||
Op::NormalDeriv
|
||||
@@ -263,28 +194,10 @@ fn cpp_input(i: &Inputs) -> Result<(Op, Vec<f64>), HarnessError> {
|
||||
Ok((op, o))
|
||||
}
|
||||
|
||||
/// Status of the upstream bare error flag (no `NL_ERROR` number of its own).
|
||||
const BARE_FLAG: f64 = 1000.0;
|
||||
|
||||
fn cpp(i: &Inputs) -> Result<Output, HarnessError> {
|
||||
let (op, input) = cpp_input(i)?;
|
||||
match ffi::eval(op, &input) {
|
||||
Ok(mut flat) => {
|
||||
// Past the recursion limit `ST_surxsdr` returns a bare error flag and
|
||||
// `NL_ERROR` still holds whatever an earlier, recovered error (or a
|
||||
// callee's `N_ErrClear`) left there: that number is not a result of the
|
||||
// routine. When the port reports the recursion limit, an upstream failure
|
||||
// is compared as the bare flag.
|
||||
if matches!(i, Inputs::Extend { .. })
|
||||
&& flat.first().is_some_and(|&st| st != 0.0)
|
||||
&& rust_flat(i)?.first() == Some(&BARE_FLAG)
|
||||
{
|
||||
if let Some(st) = flat.first_mut() {
|
||||
*st = BARE_FLAG;
|
||||
}
|
||||
}
|
||||
to_output(i, &flat)
|
||||
}
|
||||
Ok(flat) => to_output(i, &flat),
|
||||
Err(FfiError::InvalidArgument(m)) => Err(harness(format!("ffi rejected: {m}"))),
|
||||
Err(e) => Err(harness(format!("shim failure: {e}"))),
|
||||
}
|
||||
@@ -292,34 +205,15 @@ fn cpp(i: &Inputs) -> Result<Output, HarnessError> {
|
||||
|
||||
// ---- Rust side --------------------------------------------------------------------
|
||||
|
||||
/// `0` for `Ok`, the `NL_*_ERR` number for an NLib error, `1000` for the recursion
|
||||
/// limit of the extension (upstream's bare error flag), `-1` otherwise.
|
||||
/// `0` for `Ok`, the `NL_*_ERR` number for an NLib error, `-1` otherwise.
|
||||
fn status<T>(r: &SolidResult<T>) -> f64 {
|
||||
match r {
|
||||
Ok(_) => 0.0,
|
||||
Err(SolidError::Nlib { code, .. }) => f64::from(code.code()),
|
||||
Err(SolidError::NotConverged { operation, .. })
|
||||
if operation.ends_with("srf_adv::p2::extend_by_dist_at_depth") =>
|
||||
{
|
||||
BARE_FLAG
|
||||
}
|
||||
Err(_) => -1.0,
|
||||
}
|
||||
}
|
||||
|
||||
fn dump(o: &mut Vec<f64>, s: &Surface) {
|
||||
let (n, m, r, ss) = s.array_sizes();
|
||||
let (p, q) = s.degrees();
|
||||
o.extend([n, m, p, q, r, ss].map(|k| k as f64));
|
||||
for row in s.rows() {
|
||||
for c in row {
|
||||
o.extend_from_slice(&c.to_array());
|
||||
}
|
||||
}
|
||||
o.extend_from_slice(s.u_knots());
|
||||
o.extend_from_slice(s.v_knots());
|
||||
}
|
||||
|
||||
fn side(f: i32) -> Result<Side, HarnessError> {
|
||||
match f {
|
||||
1 => Ok(Side::Left),
|
||||
@@ -332,24 +226,6 @@ fn rust_flat(i: &Inputs) -> Result<Vec<f64>, HarnessError> {
|
||||
let s = build(&resolve(i.sur())?)?;
|
||||
let mut o = Vec::new();
|
||||
match i {
|
||||
Inputs::Extend {
|
||||
dist,
|
||||
dir,
|
||||
end,
|
||||
cont,
|
||||
depth,
|
||||
..
|
||||
} => {
|
||||
let dir = Direction::from_flag(*dir).ok_or_else(|| harness("bad direction flag"))?;
|
||||
let end = CurveEnd::from_flag(*end).ok_or_else(|| harness("bad end flag"))?;
|
||||
let cont =
|
||||
SrfContinuity::from_flag(*cont).ok_or_else(|| harness("bad continuity flag"))?;
|
||||
let r = adv::extend_by_dist_at_depth(&s, *dist, dir, end, cont, *depth);
|
||||
o.push(status(&r));
|
||||
if let Ok(q) = r {
|
||||
dump(&mut o, &q);
|
||||
}
|
||||
}
|
||||
Inputs::NormalDeriv { u, v, ufl, vfl, .. } => {
|
||||
let r = adv::eval_pt_normal_deriv(&s, *u, *v, side(*ufl)?, side(*vfl)?);
|
||||
o.push(status(&r));
|
||||
@@ -584,89 +460,22 @@ fn gen_smooth(rng: &mut Rng) -> InlineSrf {
|
||||
InlineSrf { net, p, q, u, v }
|
||||
}
|
||||
|
||||
/// Size of a net: the largest coordinate range of its Euclidean points.
|
||||
fn net_size(s: &InlineSrf) -> f64 {
|
||||
let pts: Vec<[f64; 3]> = s
|
||||
.net
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(|c| {
|
||||
let w = if c[3] == NOW { 1.0 } else { c[3] };
|
||||
let z = if c[2] == NOZ { 0.0 } else { c[2] / w };
|
||||
[c[0] / w, c[1] / w, z]
|
||||
})
|
||||
.collect();
|
||||
(0..3)
|
||||
.map(|k| {
|
||||
let lo = pts
|
||||
.iter()
|
||||
.filter_map(|p| p.get(k).copied())
|
||||
.fold(f64::INFINITY, f64::min);
|
||||
let hi = pts
|
||||
.iter()
|
||||
.filter_map(|p| p.get(k).copied())
|
||||
.fold(f64::NEG_INFINITY, f64::max);
|
||||
hi - lo
|
||||
})
|
||||
.fold(0.0, f64::max)
|
||||
}
|
||||
|
||||
fn gen_cont(rng: &mut Rng) -> i32 {
|
||||
match rng.below(4) {
|
||||
0 => 2,
|
||||
1 => 3,
|
||||
2 => 4,
|
||||
_ => 6,
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_case(rng: &mut Rng) -> Inputs {
|
||||
let s = if rng.chance(0.7) {
|
||||
gen_smooth(rng)
|
||||
} else {
|
||||
gen_srf(rng)
|
||||
};
|
||||
if rng.chance(0.25) {
|
||||
let (ufl, vfl) = (1 + rng.below(2) as i32, 1 + rng.below(2) as i32);
|
||||
let (ou, ov) = (rng.chance(0.1), rng.chance(0.1));
|
||||
let u = param(rng, &s.u, ou);
|
||||
let v = param(rng, &s.v, ov);
|
||||
return Inputs::NormalDeriv {
|
||||
sur: SrfIn::Inline(s),
|
||||
u,
|
||||
v,
|
||||
ufl,
|
||||
vfl,
|
||||
};
|
||||
}
|
||||
let size = net_size(&s).max(1e-6);
|
||||
let dist = size
|
||||
* match rng.below(20) {
|
||||
0 => 0.0,
|
||||
1 => -0.1,
|
||||
2 | 3 => rng.uniform(1.0, 4.0),
|
||||
4 => rng.uniform(1e-4, 1e-2),
|
||||
_ => rng.uniform(0.02, 0.8),
|
||||
};
|
||||
let depth = if rng.chance(0.05) {
|
||||
rng.below(12) as usize
|
||||
} else {
|
||||
0
|
||||
};
|
||||
// A zero distance makes upstream's G2 iso-curve extension (used for G1 and G2)
|
||||
// loop forever; keep it to Cmax / G1R.
|
||||
let mut cont = gen_cont(rng);
|
||||
if dist == 0.0 && cont < 4 {
|
||||
cont = 4;
|
||||
}
|
||||
Inputs::Extend {
|
||||
let (ufl, vfl) = (1 + rng.below(2) as i32, 1 + rng.below(2) as i32);
|
||||
let (ou, ov) = (rng.chance(0.1), rng.chance(0.1));
|
||||
let u = param(rng, &s.u, ou);
|
||||
let v = param(rng, &s.v, ov);
|
||||
Inputs::NormalDeriv {
|
||||
sur: SrfIn::Inline(s),
|
||||
dist,
|
||||
dir: 1 + rng.below(2) as i32,
|
||||
end: 1 + rng.below(2) as i32,
|
||||
cont,
|
||||
depth,
|
||||
inplace: rng.chance(0.3),
|
||||
u,
|
||||
v,
|
||||
ufl,
|
||||
vfl,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,46 +509,17 @@ impl UnitDiff for Runner {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// The generator reaches successful and failing extensions of every continuity
|
||||
/// and both normal-derivative outcomes.
|
||||
///
|
||||
/// Deep-but-bounded extension recursion (`ST_curxcdr` fails only past 50 levels,
|
||||
/// nested in `ST_surxsdr`'s 10) must fit the thread stack: generated cases reach
|
||||
/// the recursion limits, and a debug build's frames are much larger than
|
||||
/// upstream's, so the loop runs on a dedicated thread with a raised stack (same
|
||||
/// pattern as the reader fuzz `run_guarded`); the release difftest profile needs
|
||||
/// the default stack.
|
||||
/// The generator reaches successful normal derivatives.
|
||||
#[test]
|
||||
fn generated_cases_cover_outcomes() {
|
||||
const STACK: usize = 64 * 1024 * 1024;
|
||||
let h = std::thread::Builder::new()
|
||||
.stack_size(STACK)
|
||||
.spawn(|| {
|
||||
let mut stats = [0usize; 6];
|
||||
for seed in 0..1500u64 {
|
||||
let mut rng = Rng::new(seed);
|
||||
let i = gen_case(&mut rng);
|
||||
let flat = rust_flat(&i).unwrap();
|
||||
let ok = flat.first() == Some(&0.0);
|
||||
match (&i, ok) {
|
||||
(Inputs::Extend { cont, .. }, true) => {
|
||||
stats[match cont {
|
||||
2 => 0,
|
||||
3 => 1,
|
||||
4 => 2,
|
||||
_ => 3,
|
||||
}] += 1;
|
||||
}
|
||||
(Inputs::Extend { .. }, false) => stats[4] += 1,
|
||||
(Inputs::NormalDeriv { .. }, true) => stats[5] += 1,
|
||||
(Inputs::NormalDeriv { .. }, false) => {}
|
||||
}
|
||||
}
|
||||
stats
|
||||
})
|
||||
.expect("spawn");
|
||||
let stats = h.join().unwrap();
|
||||
eprintln!("{stats:?}");
|
||||
assert!(stats.iter().all(|&k| k >= 10), "{stats:?}");
|
||||
let mut ok = 0usize;
|
||||
for seed in 0..1500u64 {
|
||||
let mut rng = Rng::new(seed);
|
||||
let i = gen_case(&mut rng);
|
||||
let flat = rust_flat(&i).unwrap();
|
||||
ok += usize::from(flat.first() == Some(&0.0));
|
||||
}
|
||||
eprintln!("{ok}");
|
||||
assert!(ok >= 10, "{ok}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,15 +19,14 @@
|
||||
//! derivatives, knots.
|
||||
//!
|
||||
//! Cases whose Rust side reaches an unported `SmSurface` method are deferred (none
|
||||
//! now: `CreateExtendedSurface` and `sm_CreateNonRationalCurve`'s `DropCurve`, unit
|
||||
//! `intersect.surface_drop_lib_p3`, are ported). Every run has at least
|
||||
//! now: `sm_CreateNonRationalCurve`'s `DropCurve`, unit
|
||||
//! `intersect.surface_drop_lib_p3`, is ported). Every run has at least
|
||||
//! [`MIN_GENERATED`] generated cases.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use solidcore::geom::extent2d::Extent2d;
|
||||
use solidcore::geom::geom_utility::p2::ContinuityType;
|
||||
use solidcore::geom::vector2d::Point2;
|
||||
use solidcore::geom::vector3d::Vec3;
|
||||
use solidcore::nurbs::b_spline_curve::BSplineCurve;
|
||||
@@ -123,12 +122,6 @@ enum OpIn {
|
||||
#[serde(default)]
|
||||
param: u8,
|
||||
},
|
||||
Extend {
|
||||
dir_overload: bool,
|
||||
dir: u8,
|
||||
dist: f64,
|
||||
cont: u8,
|
||||
},
|
||||
Join {
|
||||
other: SrfIn,
|
||||
dir: u8,
|
||||
@@ -229,7 +222,6 @@ impl OpIn {
|
||||
fn ffi(&self) -> Op {
|
||||
match self {
|
||||
OpIn::Offset { .. } => Op::Offset,
|
||||
OpIn::Extend { .. } => Op::Extend,
|
||||
OpIn::Join { .. } => Op::Join,
|
||||
OpIn::ExtEval { .. } => Op::ExtEval,
|
||||
OpIn::Reparam { .. } => Op::Reparam,
|
||||
@@ -254,7 +246,6 @@ impl OpIn {
|
||||
matches!(
|
||||
self,
|
||||
OpIn::Offset { .. }
|
||||
| OpIn::Extend { .. }
|
||||
| OpIn::Join { .. }
|
||||
| OpIn::Coons { .. }
|
||||
| OpIn::Ruled { .. }
|
||||
@@ -388,17 +379,6 @@ fn cpp_input(i: &Inputs) -> Result<Vec<f64>, HarnessError> {
|
||||
}
|
||||
match &i.op {
|
||||
OpIn::Offset { dist, tol, param } => o.extend([*dist, *tol, f64::from(*param)]),
|
||||
OpIn::Extend {
|
||||
dir_overload,
|
||||
dir,
|
||||
dist,
|
||||
cont,
|
||||
} => o.extend([
|
||||
flag(*dir_overload),
|
||||
f64::from(*dir),
|
||||
*dist,
|
||||
f64::from(*cont),
|
||||
]),
|
||||
OpIn::Join { other, dir, tol } => {
|
||||
push_srf(&mut o, &srf(other)?)?;
|
||||
o.extend([f64::from(*dir), *tol]);
|
||||
@@ -764,26 +744,6 @@ fn param_of(v: u8) -> Result<SurfParamType, HarnessError> {
|
||||
})
|
||||
}
|
||||
|
||||
fn continuity_of(v: u8) -> Result<ContinuityType, HarnessError> {
|
||||
Ok(match v {
|
||||
0 => ContinuityType::Undefined,
|
||||
1 => ContinuityType::Discontinuous,
|
||||
2 => ContinuityType::C0,
|
||||
3 => ContinuityType::G1,
|
||||
4 => ContinuityType::G1R,
|
||||
5 => ContinuityType::G1G2,
|
||||
6 => ContinuityType::G1G2G3,
|
||||
7 => ContinuityType::C1,
|
||||
8 => ContinuityType::C1G2,
|
||||
9 => ContinuityType::C1G2G3,
|
||||
10 => ContinuityType::C1C2,
|
||||
11 => ContinuityType::C1C2G3,
|
||||
12 => ContinuityType::C1C2C3,
|
||||
13 => ContinuityType::CInfinity,
|
||||
_ => return Err(harness(format!("bad SmContinuityType {v}"))),
|
||||
})
|
||||
}
|
||||
|
||||
fn offset_param_of(v: u8) -> Result<OffsetParamType, HarnessError> {
|
||||
Ok(match v {
|
||||
0 => OffsetParamType::Inherited,
|
||||
@@ -860,7 +820,7 @@ fn fail(e: &SolidError) -> Fail {
|
||||
}
|
||||
|
||||
/// Runs a member op; returns the flat record.
|
||||
fn rust_member(k: &solidcore::kernel::Kernel, this: &mut BSplineSurface, op: &OpIn) -> Result<Result<Vec<f64>, Fail>, HarnessError> {
|
||||
fn rust_member(this: &mut BSplineSurface, op: &OpIn) -> Result<Result<Vec<f64>, Fail>, HarnessError> {
|
||||
let mut o = vec![0.0];
|
||||
let new = |o: &mut Vec<f64>, r: solidcore::Result<BSplineSurface>| match r {
|
||||
Ok(s) => {
|
||||
@@ -875,20 +835,6 @@ fn rust_member(k: &solidcore::kernel::Kernel, this: &mut BSplineSurface, op: &Op
|
||||
&mut o,
|
||||
this.approximate_offset_surface(*dist, *tol, offset_param_of(*param)?),
|
||||
),
|
||||
OpIn::Extend {
|
||||
dir_overload,
|
||||
dir,
|
||||
dist,
|
||||
cont,
|
||||
} => {
|
||||
let cont = continuity_of(*cont)?;
|
||||
let r = if *dir_overload {
|
||||
this.create_extended_surface_dir(k, param_of(*dir)?, *dist, cont)
|
||||
} else {
|
||||
this.create_extended_surface(k, *dist, cont)
|
||||
};
|
||||
new(&mut o, r)
|
||||
}
|
||||
OpIn::Join { other, dir, tol } => {
|
||||
let mut other = BSplineSurface::from_nurb(surface_of(&srf(other)?)?, false);
|
||||
new(&mut o, this.join_surface(&mut other, param_of(*dir)?, *tol))
|
||||
@@ -1179,11 +1125,11 @@ fn rust_static(op: &OpIn) -> Result<Result<Vec<f64>, Fail>, HarnessError> {
|
||||
Ok(res.map(|()| o))
|
||||
}
|
||||
|
||||
fn rust(k: &solidcore::kernel::Kernel, i: &Inputs) -> Result<Output, HarnessError> {
|
||||
fn rust(i: &Inputs) -> Result<Output, HarnessError> {
|
||||
let res = match &i.this {
|
||||
Some(t) => {
|
||||
let mut this = this_of(t)?;
|
||||
rust_member(k, &mut this, &i.op)?
|
||||
rust_member(&mut this, &i.op)?
|
||||
}
|
||||
None => rust_static(&i.op)?,
|
||||
};
|
||||
@@ -1711,12 +1657,6 @@ fn gen_member(rng: &mut Rng) -> Inputs {
|
||||
tol: *rng.choose(&[1e-2, 3e-2]).unwrap_or(&1e-2),
|
||||
param: rng.below(4) as u8,
|
||||
},
|
||||
1 => OpIn::Extend {
|
||||
dir_overload: rng.chance(0.7),
|
||||
dir: rng.below(9) as u8,
|
||||
dist: rng.uniform(0.05, 1.0),
|
||||
cont: *rng.choose(&[3, 4, 5, 13, 2]).unwrap_or(&3),
|
||||
},
|
||||
2 => {
|
||||
let along_v = rng.chance(0.5);
|
||||
let s_nr = InlineSrf {
|
||||
@@ -1843,7 +1783,6 @@ impl UnitDiff for Runner {
|
||||
}
|
||||
|
||||
fn run_rust(&self, case: &Case) -> Result<Output, HarnessError> {
|
||||
let k = &solidcore::Kernel::default();
|
||||
rust(k, &case.parse()?)
|
||||
rust(&case.parse()?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ use solidcore::geom::assert_array::{AssertArray, ObjectKey};
|
||||
use solidcore::geom::axis2_placement::Axis2Placement;
|
||||
use solidcore::geom::extent1d::Extent1d;
|
||||
use solidcore::geom::extent2d::Extent2d;
|
||||
use solidcore::geom::geom_utility::p2::ContinuityType;
|
||||
use solidcore::geom::vector2d::Point2;
|
||||
use solidcore::geom::vector3d::Vec3;
|
||||
use solidcore::kernel::Kernel;
|
||||
@@ -206,15 +205,6 @@ enum StepIn {
|
||||
CreateOffset {
|
||||
d: f64,
|
||||
},
|
||||
Extend {
|
||||
dist: f64,
|
||||
cont: u8,
|
||||
},
|
||||
ExtendDir {
|
||||
param: u8,
|
||||
dist: f64,
|
||||
cont: u8,
|
||||
},
|
||||
Copy,
|
||||
Eq {
|
||||
other: OffIn,
|
||||
@@ -394,15 +384,6 @@ fn ffi_step(s: &StepIn) -> Result<ffi::Step, HarnessError> {
|
||||
param: *param,
|
||||
},
|
||||
StepIn::CreateOffset { d } => F::CreateOffset { d: *d },
|
||||
StepIn::Extend { dist, cont } => F::Extend {
|
||||
dist: *dist,
|
||||
cont: *cont,
|
||||
},
|
||||
StepIn::ExtendDir { param, dist, cont } => F::ExtendDir {
|
||||
param: *param,
|
||||
dist: *dist,
|
||||
cont: *cont,
|
||||
},
|
||||
StepIn::Copy => F::Copy,
|
||||
StepIn::Eq { other } => F::Eq {
|
||||
other: Box::new(ffi_off(other)?),
|
||||
@@ -467,27 +448,6 @@ fn param_of(v: u8) -> Result<SurfParamType, HarnessError> {
|
||||
SurfParamType::from_code(u64::from(v)).ok_or_else(|| harness(format!("bad param {v}")))
|
||||
}
|
||||
|
||||
fn continuity_of(v: u8) -> Result<ContinuityType, HarnessError> {
|
||||
use ContinuityType as C;
|
||||
Ok(match v {
|
||||
0 => C::Undefined,
|
||||
1 => C::Discontinuous,
|
||||
2 => C::C0,
|
||||
3 => C::G1,
|
||||
4 => C::G1R,
|
||||
5 => C::G1G2,
|
||||
6 => C::G1G2G3,
|
||||
7 => C::C1,
|
||||
8 => C::C1G2,
|
||||
9 => C::C1G2G3,
|
||||
10 => C::C1C2,
|
||||
11 => C::C1C2G3,
|
||||
12 => C::C1C2C3,
|
||||
13 => C::CInfinity,
|
||||
_ => return Err(harness(format!("bad continuity {v}"))),
|
||||
})
|
||||
}
|
||||
|
||||
/// The `SmStatus` of a Rust error.
|
||||
fn status(e: &SolidError) -> f64 {
|
||||
match e {
|
||||
@@ -834,18 +794,6 @@ fn run_step(
|
||||
StepIn::CreateOffset { d } => {
|
||||
push_res(o, s.create_offset_surface(k, *d), |o, r| put_result(k, o, &r))
|
||||
}
|
||||
StepIn::Extend { dist, cont } => {
|
||||
let c = continuity_of(*cont)?;
|
||||
push_res(o, s.create_extended_surface(k, *dist, c), |o, r| {
|
||||
put_result(k, o, &r)
|
||||
})
|
||||
}
|
||||
StepIn::ExtendDir { param, dist, cont } => {
|
||||
let (p, c) = (param_of(*param)?, continuity_of(*cont)?);
|
||||
push_res(o, s.create_extended_surface_dir(k, p, *dist, c), |o, r| {
|
||||
put_result(k, o, &r)
|
||||
})
|
||||
}
|
||||
StepIn::Copy => push_res(o, s.copy(k), |o, c| put_result(k, o, &Surface::from(c))),
|
||||
StepIn::Eq { other } => match rust_off(k, other)? {
|
||||
Ok(other) => {
|
||||
@@ -1511,17 +1459,6 @@ fn gen_step(rng: &mut Rng, d: [f64; 4], scale: f64, size: f64, can_via: bool) ->
|
||||
rng.uniform(-1.0, 1.0) * scale
|
||||
},
|
||||
},
|
||||
// (`ffi::check` refuses `CreateExtendedSurface` in process, UB-107/HANG-6/HANG-22,
|
||||
// so `gen_case` drops a script with one of these)
|
||||
86..=87 => StepIn::Extend {
|
||||
dist: rng.uniform(0.05, 0.4) * size,
|
||||
cont: *rng.choose(&[3, 4, 5, 13]).unwrap_or(&3),
|
||||
},
|
||||
88 => StepIn::ExtendDir {
|
||||
param: *rng.choose(&[0, 1, 3, 5, 6, 7, 8]).unwrap_or(&0),
|
||||
dist: rng.uniform(0.05, 0.4) * size,
|
||||
cont: *rng.choose(&[3, 4, 5, 13]).unwrap_or(&3),
|
||||
},
|
||||
89..=91 => StepIn::Copy,
|
||||
92..=93 => StepIn::Eq {
|
||||
other: gen_off(rng, scale, 2),
|
||||
@@ -1591,10 +1528,7 @@ fn gen_case(rng: &mut Rng) -> Option<Inputs> {
|
||||
bytes: Some(bytes),
|
||||
});
|
||||
}
|
||||
let mut steps = gen_steps(rng, &off, d, scale, size);
|
||||
if let Some(degree_1) = degree_1_directions(&k, &off) {
|
||||
avoid_reflection_extensions(&mut steps, degree_1);
|
||||
}
|
||||
let steps = gen_steps(rng, &off, d, scale, size);
|
||||
let fsteps = ffi_steps(&steps).ok()?;
|
||||
ffi::check(&foff, &fsteps).ok()?;
|
||||
// Domain/zero-offset guards depend on the actual C++ state after preceding
|
||||
@@ -1693,74 +1627,6 @@ fn gen_assert_valid_case(rng: &mut Rng) -> Option<Inputs> {
|
||||
Some(Inputs::Script { off, steps })
|
||||
}
|
||||
|
||||
/// Which directions (`[u, v]`) of the B-spline NLib extends for this offset (its base,
|
||||
/// looking through nested offsets, curve-bounded and STEP wrappers) have degree 1;
|
||||
/// `None` when neither does.
|
||||
fn degree_1_directions(k: &Kernel, off: &OffIn) -> Option<[bool; 2]> {
|
||||
let Ok(Ok(base)) = rust_srf(k, &off.base) else {
|
||||
return None;
|
||||
};
|
||||
let mut s = &base;
|
||||
for _ in 0..=solidcore::surface::offset_surface::MAX_SURFACE_NESTING {
|
||||
s = match s.kind() {
|
||||
SurfaceKind::OffsetSurface(o) => o.base_surface(),
|
||||
SurfaceKind::CurveBounded(c) => c.basis(),
|
||||
SurfaceKind::Step(st) => st.surface()?,
|
||||
SurfaceKind::BSpline(_)
|
||||
| SurfaceKind::ProceduralBlend(_)
|
||||
| SurfaceKind::BallPatch(_)
|
||||
| SurfaceKind::Plane(_)
|
||||
| SurfaceKind::Cone(_)
|
||||
| SurfaceKind::Sphere(_)
|
||||
| SurfaceKind::Torus(_)
|
||||
| SurfaceKind::SurfOfRevolution(_)
|
||||
| SurfaceKind::SurfOfExtrusion(_) => {
|
||||
let d = [
|
||||
s.degree(SurfParamType::U) == 1,
|
||||
s.degree(SurfParamType::V) == 1,
|
||||
];
|
||||
return (d[0] || d[1]).then_some(d);
|
||||
}
|
||||
SurfaceKind::SrfInVolume(_) => return None,
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// UB-32 (`plan/upstream-issues.md`): a reflection extension (`G1`, `G1G2`) in a
|
||||
/// degree-1 direction can make `ST_surxsdr`'s reflection-plane fallback read past its
|
||||
/// scratch row; the port reports `InvalidInput` there. As in `nlib.srf_adv_p1`, keep
|
||||
/// such extensions to C-max, only for the extensions that reach a degree-1 direction
|
||||
/// (`degree_1` is `[u, v]` of the base; `SwapUV` exchanges them as the script runs).
|
||||
/// (Applied after generation, so the random stream and the other cases are unchanged;
|
||||
/// the evaluation-triggered extension is ruled and safe.)
|
||||
fn avoid_reflection_extensions(steps: &mut [StepIn], degree_1: [bool; 2]) {
|
||||
use SurfParamType as P;
|
||||
let [mut du, mut dv] = degree_1;
|
||||
for step in steps {
|
||||
let (cont, reaches) = match step {
|
||||
StepIn::Swap => {
|
||||
core::mem::swap(&mut du, &mut dv);
|
||||
continue;
|
||||
}
|
||||
StepIn::Extend { cont, .. } => (cont, du || dv),
|
||||
StepIn::ExtendDir { param, cont, .. } => {
|
||||
let reaches = match param_of(*param) {
|
||||
Ok(P::U | P::UMin | P::UMax) => du,
|
||||
Ok(P::V | P::VMin | P::VMax) => dv,
|
||||
Ok(P::Neither) => false,
|
||||
_ => du || dv,
|
||||
};
|
||||
(cont, reaches)
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
if reaches && matches!(*cont, 3 | 5) {
|
||||
*cont = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fills a fixture's placeholders from the base built on the Rust side: a `dom` of
|
||||
/// `"NAT"` (the base's natural domain), `"SUB"` (its middle half) or `"OUT"` (a quarter
|
||||
/// larger on every side); a `uv` of `"MID"`, `"VMIN"` or `"VMAX"` (the middle of the
|
||||
|
||||
@@ -24,7 +24,6 @@ use solidcore::curve::line::Line;
|
||||
use solidcore::geom::axis2_placement::Axis2Placement;
|
||||
use solidcore::geom::extent1d::Extent1d;
|
||||
use solidcore::geom::extent2d::Extent2d;
|
||||
use solidcore::geom::geom_utility::p2::ContinuityType;
|
||||
use solidcore::geom::global_solver::SolverOperation;
|
||||
use solidcore::geom::solution_array::{ObjectKind, SolutionArray, SolutionObject, SolutionType};
|
||||
use solidcore::geom::vector2d::{Point2, Vec2};
|
||||
@@ -224,12 +223,6 @@ enum OpIn {
|
||||
v_dir: bool,
|
||||
},
|
||||
Swap,
|
||||
Extend {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
dir: Option<u8>,
|
||||
dist: f64,
|
||||
cont: u8,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -423,11 +416,6 @@ fn ffi_op(o: &OpIn) -> ffi::Op {
|
||||
OpIn::Inherited { v } => ffi::Op::Inherited(*v),
|
||||
OpIn::Reverse { v_dir } => ffi::Op::Reverse(*v_dir),
|
||||
OpIn::Swap => ffi::Op::Swap,
|
||||
OpIn::Extend { dir, dist, cont } => ffi::Op::Extend {
|
||||
dir: *dir,
|
||||
dist: *dist,
|
||||
cont: *cont,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -972,45 +960,10 @@ fn rust_op(k: &Kernel, op: &OpIn, a: &mut Plane, o: &mut Vec<f64>) -> Result<(),
|
||||
OpIn::Swap => {
|
||||
st(o, a.swap_uv())?;
|
||||
}
|
||||
OpIn::Extend { dir, dist, cont } => {
|
||||
let cont = continuity_of(*cont)?;
|
||||
let r = match dir {
|
||||
Some(d) => {
|
||||
let d = SurfParamType::from_code(u64::from(*d))
|
||||
.ok_or_else(|| harness(format!("bad direction {d}")))?;
|
||||
a.create_extended_surface_dir(d, *dist, cont)
|
||||
}
|
||||
None => a.create_extended_surface(*dist, cont),
|
||||
};
|
||||
if let Some(e) = st(o, r)? {
|
||||
put_surface_nurb(o, e.gw_nurb());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn continuity_of(v: u8) -> Result<ContinuityType, HarnessError> {
|
||||
use ContinuityType as C;
|
||||
Ok(match v {
|
||||
0 => C::Undefined,
|
||||
1 => C::Discontinuous,
|
||||
2 => C::C0,
|
||||
3 => C::G1,
|
||||
4 => C::G1R,
|
||||
5 => C::G1G2,
|
||||
6 => C::G1G2G3,
|
||||
7 => C::C1,
|
||||
8 => C::C1G2,
|
||||
9 => C::C1G2G3,
|
||||
10 => C::C1C2,
|
||||
11 => C::C1C2G3,
|
||||
12 => C::C1C2C3,
|
||||
13 => C::CInfinity,
|
||||
_ => return Err(harness(format!("bad continuity {v}"))),
|
||||
})
|
||||
}
|
||||
|
||||
fn op_code(op: &OpIn) -> Result<f64, HarnessError> {
|
||||
let prog = ffi::encode(
|
||||
&ffi::Make::Normal {
|
||||
@@ -1534,14 +1487,6 @@ fn gen_op(rng: &mut Rng, m: &MakeIn, g: &Geo) -> OpIn {
|
||||
line: [l0, l1],
|
||||
s: rng.uniform(0.0, 1.0),
|
||||
}
|
||||
} else if rng.chance(0.25) {
|
||||
OpIn::Extend {
|
||||
dir: rng
|
||||
.chance(0.6)
|
||||
.then(|| pick(rng, &[0u8, 1, 2, 3, 4, 5, 6, 7, 8])),
|
||||
dist: pick(rng, &[0.01, 0.1, 0.5, 2.0]),
|
||||
cont: pick(rng, &[2u8, 3, 4, 5, 13]),
|
||||
}
|
||||
} else if rng.chance(0.5) {
|
||||
OpIn::Reverse {
|
||||
v_dir: rng.chance(0.5),
|
||||
|
||||
@@ -31,7 +31,6 @@ use serde_json::Value;
|
||||
|
||||
use solidcore::geom::axis2_placement::Axis2Placement;
|
||||
use solidcore::geom::extent1d::Extent1d;
|
||||
use solidcore::geom::geom_utility::p2::ContinuityType;
|
||||
use solidcore::geom::vector2d::{Point2, Vec2};
|
||||
use solidcore::geom::vector3d::Vec3;
|
||||
use solidcore::kernel::Kernel;
|
||||
@@ -45,7 +44,7 @@ use solidcore::surface::plane::Plane;
|
||||
use solidcore::surface::surf_of_extrusion::p2::{ExtrusionIntersection, IntersectTarget};
|
||||
use solidcore::surface::surf_of_extrusion::SurfOfExtrusion;
|
||||
use solidcore::surface::surface::p1::crossed_seam_or_pole;
|
||||
use solidcore::surface::surface::{Surface, SurfaceKind};
|
||||
use solidcore::surface::surface::Surface;
|
||||
use solidcore::topology::database_io::{DatabaseReader, FileType};
|
||||
use solidcore_ffi::units::surface_surf_of_extrusion_p2 as ffi;
|
||||
|
||||
@@ -307,12 +306,6 @@ enum QueryIn {
|
||||
dom: Option<[f64; 4]>,
|
||||
tol: f64,
|
||||
},
|
||||
Extend {
|
||||
#[serde(default)]
|
||||
dir: Option<u8>,
|
||||
dist: f64,
|
||||
cont: u8,
|
||||
},
|
||||
Planar {
|
||||
tol: f64,
|
||||
},
|
||||
@@ -714,7 +707,6 @@ fn ffi_query(q: &QueryIn) -> ffi::Query {
|
||||
QueryIn::BBox { dom, expand } => ffi::Query::BBox { dom, expand },
|
||||
QueryIn::DegenPoint { tol, dom } => ffi::Query::DegenPoint { tol, dom },
|
||||
QueryIn::DegenCurve { ang, dom, tol } => ffi::Query::DegenCurve { ang, dom, tol },
|
||||
QueryIn::Extend { dir, dist, cont } => ffi::Query::Extend { dir, dist, cont },
|
||||
QueryIn::Planar { tol } => ffi::Query::Planar { tol },
|
||||
QueryIn::NumCpts { dir } => ffi::Query::NumCpts { dir },
|
||||
QueryIn::Bilinear => ffi::Query::Bilinear,
|
||||
@@ -1226,40 +1218,6 @@ fn rust_query(k: &Kernel, s: &Surface, q: &QueryIn, o: &mut Vec<f64>) -> Result<
|
||||
QueryIn::DegenCurve { ang, dom, tol } => {
|
||||
truth(o, s.is_degenerate_curve(k, ang, opt(dom).as_ref(), tol))?;
|
||||
}
|
||||
QueryIn::Extend { dir, dist, cont } => {
|
||||
let cont = continuity_of(cont);
|
||||
let r = match dir {
|
||||
None => s.create_extended_surface(k, dist, cont),
|
||||
Some(d) => match SurfParamType::from_code(u64::from(d)) {
|
||||
Some(d) => s.create_extended_surface_dir(k, d, dist, cont),
|
||||
None => Err(solidcore::SolidError::invalid_input(
|
||||
"difftest",
|
||||
"parameter type",
|
||||
)),
|
||||
},
|
||||
};
|
||||
if let Some(e) = st(o, r)? {
|
||||
o.push(e.type_code() as f64);
|
||||
let sur = match e.kind() {
|
||||
SurfaceKind::BSpline(b) => b.gw_nurb(),
|
||||
_ => None,
|
||||
};
|
||||
o.push(flag(sur.is_some()));
|
||||
if let Some(sur) = sur {
|
||||
let ix = sur.indices();
|
||||
o.extend([ix.p, ix.q, ix.n + 1, ix.m + 1].map(|x| x as f64));
|
||||
for row in sur.rows() {
|
||||
for p in row {
|
||||
o.extend([p.x, p.y, p.z, p.w]);
|
||||
}
|
||||
}
|
||||
o.push(sur.u_knots().len() as f64);
|
||||
o.extend_from_slice(sur.u_knots());
|
||||
o.push(sur.v_knots().len() as f64);
|
||||
o.extend_from_slice(sur.v_knots());
|
||||
}
|
||||
}
|
||||
}
|
||||
QueryIn::Planar { tol } => match s.is_planar(k, tol) {
|
||||
Ok(p) => {
|
||||
o.push(flag(p.is_planar));
|
||||
@@ -1327,27 +1285,6 @@ fn param_code(c: u8) -> solidcore::Result<SurfParamType> {
|
||||
.ok_or_else(|| solidcore::SolidError::invalid_input("difftest", "parameter type"))
|
||||
}
|
||||
|
||||
/// `SmContinuityType` by code (as the `surface.surface_p2` runner).
|
||||
fn continuity_of(v: u8) -> ContinuityType {
|
||||
use ContinuityType as C;
|
||||
match v {
|
||||
0 => C::Undefined,
|
||||
1 => C::Discontinuous,
|
||||
2 => C::C0,
|
||||
3 => C::G1,
|
||||
4 => C::G1R,
|
||||
5 => C::G1G2,
|
||||
6 => C::G1G2G3,
|
||||
7 => C::C1,
|
||||
8 => C::C1G2,
|
||||
9 => C::C1G2G3,
|
||||
10 => C::C1C2,
|
||||
11 => C::C1C2G3,
|
||||
12 => C::C1C2C3,
|
||||
_ => C::CInfinity,
|
||||
}
|
||||
}
|
||||
|
||||
// ---- generation -----------------------------------------------------------------------
|
||||
|
||||
/// A built source with its Rust surface (to size the parameters).
|
||||
|
||||
@@ -232,15 +232,6 @@ enum OpIn {
|
||||
tol: f64,
|
||||
guess: Option<[f64; 2]>,
|
||||
},
|
||||
Extend {
|
||||
dist: f64,
|
||||
cont: u8,
|
||||
},
|
||||
ExtendDir {
|
||||
dir: u8,
|
||||
dist: f64,
|
||||
cont: u8,
|
||||
},
|
||||
AdjustStep {
|
||||
dom: [f64; 4],
|
||||
},
|
||||
@@ -452,8 +443,6 @@ impl OpIn {
|
||||
OpIn::Trim { .. } => Op::Trim,
|
||||
OpIn::Split { .. } => Op::Split,
|
||||
OpIn::StepInv { .. } => Op::StepInv,
|
||||
OpIn::Extend { .. } => Op::Extend,
|
||||
OpIn::ExtendDir { .. } => Op::ExtendDir,
|
||||
OpIn::AdjustStep { .. } => Op::AdjustStep,
|
||||
OpIn::MaxAnal => Op::MaxAnal,
|
||||
OpIn::Copy => Op::Copy,
|
||||
@@ -545,111 +534,6 @@ fn flag(b: bool) -> f64 {
|
||||
f64::from(u8::from(b))
|
||||
}
|
||||
|
||||
/// Whether `CreateExtendedSurface` would retry a failed `N_SrfExtendByDist`: upstream
|
||||
/// extends in place (`surP == surQ`), and a failed attempt leaves the surface on freshly
|
||||
/// allocated, partly written `NL_STACKS` storage that the halved retry then reads
|
||||
/// (undefined; its outcome depends on earlier calls in the process, see
|
||||
/// `plan/findings.md`). Such cases are not generated. The side sequence is the one of
|
||||
/// `create_extended_surface_dispatched` (singular and closed sides skipped); any failure
|
||||
/// of the emulation counts as a retry.
|
||||
fn extension_retries_in_place(i: &Inputs) -> bool {
|
||||
let (dir, dist, cont) = match &i.op {
|
||||
OpIn::Extend { dist, cont } => (SurfParamType::Both, *dist, *cont),
|
||||
OpIn::ExtendDir { dir, dist, cont } => match param_of(*dir) {
|
||||
Ok(d) => (d, *dist, *cont),
|
||||
Err(_) => return false,
|
||||
},
|
||||
_ => return false,
|
||||
};
|
||||
let Some(b) = i
|
||||
.this
|
||||
.as_ref()
|
||||
.filter(|t| !t.base)
|
||||
.and_then(|t| t.bss.as_ref())
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
let Ok(bs) = bss_of(b) else {
|
||||
return false;
|
||||
};
|
||||
!matches!(extension_needs_no_retry(&bs, dir, dist, cont), Ok(true))
|
||||
}
|
||||
|
||||
/// The first attempts of [`extension_retries_in_place`]: `Ok(true)` when every side
|
||||
/// extends at the full distance.
|
||||
fn extension_needs_no_retry(
|
||||
bs: &BSplineSurface,
|
||||
dir: SurfParamType,
|
||||
dist: f64,
|
||||
cont: u8,
|
||||
) -> solidcore::Result<bool> {
|
||||
use solidcore::nurbs::crv_adv::p1::CurveEnd;
|
||||
use solidcore::nurbs::srf_adv::p1::extend_by_dist;
|
||||
use solidcore::nurbs::srf_adv::p2::SrfContinuity;
|
||||
use solidcore::nurbs::srf_basic::Direction;
|
||||
use SurfParamType as P;
|
||||
|
||||
let Some(nurb) = bs.gw_nurb() else {
|
||||
return Ok(true);
|
||||
};
|
||||
let k = Kernel::default();
|
||||
let s = Surface::from(bs.clone_exact());
|
||||
let dom = bs.natural_uv_domain();
|
||||
let gflg = match continuity_of(cont) {
|
||||
Ok(ContinuityType::G1R) => SrfContinuity::G1R,
|
||||
Ok(ContinuityType::G1G2) => SrfContinuity::G2,
|
||||
Ok(ContinuityType::CInfinity) => SrfContinuity::Cmax,
|
||||
_ => SrfContinuity::G1,
|
||||
};
|
||||
let u_dir = matches!(dir, P::U | P::UMin | P::UMax);
|
||||
let v_dir = matches!(dir, P::V | P::VMin | P::VMax);
|
||||
let neither = dir == P::Neither;
|
||||
let mut skip_umin = neither || v_dir || dir == P::UMax;
|
||||
let mut skip_umax = neither || v_dir || dir == P::UMin;
|
||||
let mut skip_vmin = neither || u_dir || dir == P::VMax;
|
||||
let mut skip_vmax = neither || u_dir || dir == P::VMin;
|
||||
let sing = s.is_singularity(&k, dom.min(), solidcore::math::EFF_ZERO, false)?;
|
||||
if sing.singular {
|
||||
if sing.direction == P::U {
|
||||
skip_vmin = true;
|
||||
} else {
|
||||
skip_umin = true;
|
||||
}
|
||||
}
|
||||
let sing = s.is_singularity(&k, dom.max(), solidcore::math::EFF_ZERO, false)?;
|
||||
if sing.singular {
|
||||
if sing.direction == P::U {
|
||||
skip_vmax = true;
|
||||
} else {
|
||||
skip_umax = true;
|
||||
}
|
||||
}
|
||||
let mut cur = nurb.clone();
|
||||
let mut sides = Vec::new();
|
||||
if !v_dir && !s.is_closed(&k, &dom, P::U, None, false)?.0 {
|
||||
sides.extend([
|
||||
(Direction::U, CurveEnd::Start, skip_umin),
|
||||
(Direction::U, CurveEnd::End, skip_umax),
|
||||
]);
|
||||
}
|
||||
if !u_dir && !s.is_closed(&k, &dom, P::V, None, false)?.0 {
|
||||
sides.extend([
|
||||
(Direction::V, CurveEnd::Start, skip_vmin),
|
||||
(Direction::V, CurveEnd::End, skip_vmax),
|
||||
]);
|
||||
}
|
||||
for (d, e, skipped) in sides {
|
||||
if skipped {
|
||||
continue;
|
||||
}
|
||||
match extend_by_dist(&cur, dist, d, e, gflg) {
|
||||
Ok(x) => cur = x,
|
||||
Err(_) => return Ok(false),
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
// ---- C++ side -----------------------------------------------------------------------
|
||||
|
||||
fn push_srf(o: &mut Vec<f64>, s: &InlineSrf) -> Result<(), HarnessError> {
|
||||
@@ -800,10 +684,6 @@ fn cpp_input(i: &Inputs) -> Result<(Op, Vec<f64>), HarnessError> {
|
||||
o.push(flag(guess.is_some()));
|
||||
o.extend_from_slice(&guess.unwrap_or([0.0, 0.0]));
|
||||
}
|
||||
OpIn::Extend { dist, cont } => o.extend([*dist, f64::from(*cont)]),
|
||||
OpIn::ExtendDir { dir, dist, cont } => {
|
||||
o.extend([f64::from(*dir), *dist, f64::from(*cont)]);
|
||||
}
|
||||
OpIn::DropFast { dom, p } => {
|
||||
o.extend_from_slice(dom);
|
||||
o.extend_from_slice(p);
|
||||
@@ -1602,29 +1482,6 @@ fn rust_surface_op(k: &Kernel, op: &OpIn, t: &This) -> Result<Run, HarnessError>
|
||||
o.push(f64::from(loc as i32));
|
||||
Ok(())
|
||||
}),
|
||||
OpIn::Extend { dist, cont } => {
|
||||
let cont = continuity_of(*cont)?;
|
||||
record(|o| {
|
||||
let e = match &s {
|
||||
Some(x) => x.create_extended_surface(k, *dist, cont)?,
|
||||
None => sp2::base_create_extended_surface()?,
|
||||
};
|
||||
dump_state(o, Some(&e));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
OpIn::ExtendDir { dir, dist, cont } => {
|
||||
let cont = continuity_of(*cont)?;
|
||||
let dir = param_of(*dir)?;
|
||||
record(|o| {
|
||||
let e = match &s {
|
||||
Some(x) => x.create_extended_surface_dir(k, dir, *dist, cont)?,
|
||||
None => sp2::base_create_extended_surface()?,
|
||||
};
|
||||
dump_state(o, Some(&e));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
OpIn::AdjustStep { dom } => edit(&mut s, |x| {
|
||||
x.adjust_step_uv_domain(&Kernel::default(), &dom_of(*dom))
|
||||
}),
|
||||
@@ -2555,15 +2412,6 @@ fn gen_surface_case(rng: &mut Rng) -> Inputs {
|
||||
tol: 1.0e-6,
|
||||
guess: rng.chance(0.3).then(|| gen_uv(rng, d)),
|
||||
},
|
||||
13 => OpIn::Extend {
|
||||
dist: rng.uniform(0.05, 0.5),
|
||||
cont: *rng.choose(&[2u8, 3, 4, 5, 13]).unwrap_or(&3),
|
||||
},
|
||||
14 => OpIn::ExtendDir {
|
||||
dir: *rng.choose(&[0u8, 1, 3, 5, 6, 7, 8]).unwrap_or(&0),
|
||||
dist: rng.uniform(0.05, 0.5),
|
||||
cont: *rng.choose(&[2u8, 3, 4, 5, 13]).unwrap_or(&3),
|
||||
},
|
||||
15 => OpIn::AdjustStep {
|
||||
dom: gen_sub_dom(rng, d),
|
||||
},
|
||||
@@ -3085,9 +2933,6 @@ impl UnitDiff for Runner {
|
||||
let Ok((op, input)) = cpp_input(&i) else {
|
||||
continue;
|
||||
};
|
||||
if extension_retries_in_place(&i) {
|
||||
continue;
|
||||
}
|
||||
if ffi::validate(op, &input).is_ok() {
|
||||
return serde_json::to_value(i).ok();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"unit": "nlib.srf_adv_p1",
|
||||
"cases": [
|
||||
{"name": "fuzz01437_ffi_rejected_bad_index_count_flag", "origin": "fuzz-out/n2000/nlib.srf_adv_p1/fuzz01437.json (panic: rust: attempt to add with overflow at crates/solidcore/src/nurbs/srf_nurb.rs:1107)", "cpp": "harness: ffi rejected: bad index/count/flag 9223372036854776000", "rust": "ok", "inputs": {"cont": 3, "ndr": 0, "op": "unbounded", "sur": {"net": [[[2.44, -0.224, 8.48, -1.7976931348623157e+308], [3.64, -0.722, 7.63, -1.7976931348623157e+308], [5.41, 0.369, -9.12, -1.7976931348623157e+308], [0.00994, 0.135, 3.58, -1.7976931348623157e+308]], [[2.21, 6.08, -921.0, -1.7976931348623157e+308], [-7.0, 4.59, -9.03, -1.7976931348623157e+308], [-5.96, -4.15, 2.4, -1.7976931348623157e+308], [-8.46, -0.974, 1.28, -1.7976931348623157e+308]]], "p": 1, "q": 9223372036854775807, "u": [229.0], "v": [8.55]}, "uv": [[454.0, 8.55]]}},
|
||||
{"name": "fuzz01998_ffi_rejected_extend_by_distance_g1", "origin": "fuzz-out/n2000/nlib.srf_adv_p1/fuzz01998.json (timeout: cpp: no result after 20 s)", "issue": "HANG-6", "cpp": "harness: ffi rejected: extend by distance: G1/G2 step lost to rounding (distance too short)", "rust": "ok", "inputs": {"cont": 3, "dir": 1, "dist": -0.0, "end": 1, "op": "extend_by_dist", "sur": {"sms": {"$upstream_file": "offset_nurb.sms"}}}}
|
||||
{"name": "fuzz01437_ffi_rejected_bad_index_count_flag", "origin": "fuzz-out/n2000/nlib.srf_adv_p1/fuzz01437.json (panic: rust: attempt to add with overflow at crates/solidcore/src/nurbs/srf_nurb.rs:1107)", "cpp": "harness: ffi rejected: bad index/count/flag 9223372036854776000", "rust": "ok", "inputs": {"cont": 3, "ndr": 0, "op": "unbounded", "sur": {"net": [[[2.44, -0.224, 8.48, -1.7976931348623157e+308], [3.64, -0.722, 7.63, -1.7976931348623157e+308], [5.41, 0.369, -9.12, -1.7976931348623157e+308], [0.00994, 0.135, 3.58, -1.7976931348623157e+308]], [[2.21, 6.08, -921.0, -1.7976931348623157e+308], [-7.0, 4.59, -9.03, -1.7976931348623157e+308], [-5.96, -4.15, 2.4, -1.7976931348623157e+308], [-8.46, -0.974, 1.28, -1.7976931348623157e+308]]], "p": 1, "q": 9223372036854775807, "u": [229.0], "v": [8.55]}, "uv": [[454.0, 8.55]]}}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"unit": "nlib.srf_adv_p2",
|
||||
"cases": [
|
||||
{"name": "fuzz00017_ffi_rejected_invalid_knot_vector", "origin": "fuzz-out/n2000/nlib.srf_adv_p2/fuzz00017.json (timeout: rust: no result after 20 s)", "cpp": "harness: ffi rejected: invalid knot vector", "rust": "ok", "inputs": {"cont": 2, "dir": 1, "dist": 2.25, "end": 1, "op": "extend", "sur": {"net": [[[0.00541, 0.0163, 0.0, -1.7976931348623157e+308], [-0.0085, 1.32, -0.0776, -1.7976931348623157e+308], [-0.0112, 2.72, -0.31, -1.7976931348623157e+308], [0.0626, 4.07, -0.698, -1.7976931348623157e+308], [0.0554, 5.42, -1.24, -1.7976931348623157e+308]], [[1.32, -0.01, 0.173, -1.7976931348623157e+308], [1.32, 1.36, 0.0273, -1.7976931348623157e+308], [1.34, 2.71, -0.274, -1.7976931348623157e+308], [1.25, 4.03, -0.73, -1.7976931348623157e+308], [1.32, 5.39, -1.34, -1.7976931348623157e+308]], [[2.61, 0.0276, 0.693, -1.7976931348623157e+308], [2.59, 1.32, 0.479, -1.7976931348623157e+308], [2.59, 2.73, 0.109, -1.7976931348623157e+308], [2.65, 4.05, -0.415, -1.7976931348623157e+308], [2.61, 5.41, -1.09, -1.7976931348623157e+308]], [[3.95, -0.0361, 1.56, -1.7976931348623157e+308], [3.91, 1.33, 1.28, -1.7976931348623157e+308], [3.88, 2.67, 0.839, -1.7976931348623157e+308], [3.86, 3.98, 0.246, -1.7976931348623157e+308], [3.95, 5.42, -0.502, -1.7976931348623157e+308]]], "p": 3, "q": 3, "u": [297.0, 297.0, 297.0, 297.0], "v": [0.0, 0.0, 0.0, 2.11, 3.76, 3.76, 3.76]}}},
|
||||
{"name": "fuzz00349_ffi_rejected_mixed_or_non_positive", "origin": "fuzz-out/n2000/nlib.srf_adv_p2/fuzz00349.json (timeout: cpp: no result after 20 s)", "issue": "HANG-6", "cpp": "harness: ffi rejected: mixed or non-positive weights", "rust": "ok", "inputs": {"cont": 2, "dir": 2, "dist": 13.3, "end": 2, "op": "extend", "sur": {"net": [[[9.53, 0.189, 1.7976931348623157e+308, -1.7976931348623157e+308], [9.46, 4.84, 1.7976931348623157e+308, -1.7976931348623157e+308], [9.64, 9.21, 1.7976931348623157e+308, -0.0]], [[12.7, -0.032, 1.7976931348623157e+308, -1.7976931348623157e+308], [12.8, 4.55, 1.7976931348623157e+308, -1.7976931348623157e+308], [12.8, 9.51, 1.7976931348623157e+308, -1.7976931348623157e+308]], [[15.9, -0.0803, 1.7976931348623157e+308, -1.7976931348623157e+308], [15.9, 4.61, 1.7976931348623157e+308, -1.7976931348623157e+308], [15.8, 9.28, 1.7976931348623157e+308, -1.7976931348623157e+308]], [[19.2, 0.196, 1.7976931348623157e+308, -1.7976931348623157e+308], [19.0, 4.67, 1.7976931348623157e+308, -1.7976931348623157e+308], [19.0, 9.3, 1.7976931348623157e+308, -1.7976931348623157e+308]]], "p": 3, "q": 2, "u": [1.0], "v": [157.0]}}},
|
||||
{"name": "fuzz01877_ffi_rejected_extend_by_distance_g1", "origin": "fuzz-out/n2000/nlib.srf_adv_p2/fuzz01877.json (timeout: cpp: no result after 20 s)", "cpp": "harness: ffi rejected: extend by distance: G1/G2 step lost to rounding (distance too short)", "rust": "ok", "inputs": {"cont": 3, "dir": 1, "dist": 1e-300, "end": 1, "op": "extend", "sur": {"sms": {"$upstream_file": "Surface.sms"}}}}
|
||||
]
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
{"name": "fuzz00530_sweep_tolerance_too_small_for_the", "origin": "fuzz-out/n2000/nurbs.b_spline_surface_n_lib_p1/fuzz00530.json (timeout: cpp: no result after 20 s)", "issue": "HANG-5", "cpp": "harness: invalid argument: sweep: tolerance too small for the trajectory", "rust": "ok", "inputs": {"op": "sweep", "section": {"cpts": [[-2.73, -2.28, -2.87, -1.7976931348623157e+308], [-1.87, -1.05, -2.19, -1.7976931348623157e+308], [-1.0, 0.18, -1.48, -1.7976931348623157e+308], [-0.147, 1.42, -0.788, -1.7976931348623157e+308]], "p": 1, "u": [1.93, 1.93, 3.07, 3.78, 3.95, 3.95]}, "tol": 1e-300, "traj": {"cpts": [[1.1, 0.855, 1.72, -1.7976931348623157e+308], [-0.206, 0.48, 1.49, -1.7976931348623157e+308], [-1.59, 0.234, 1.12, -1.7976931348623157e+308], [-2.84, -0.144, 0.684, -1.7976931348623157e+308]], "p": 3, "u": [0.769, 0.769, 0.769, 0.769, 3.66, 3.66, 3.66, 3.66]}, "trans": false}},
|
||||
{"name": "fuzz00651_curve_euclidean_point_out_of_range", "origin": "fuzz-out/n2000/nurbs.b_spline_surface_n_lib_p1/fuzz00651.json (crash: rust: killed by signal 6 (realloc(): invalid next size))", "issue": "UB-225", "cpp": "harness: invalid argument: curve: Euclidean point out of range", "rust": "ok", "inputs": {"curve": {"cpts": [[-0.364, 0.0577, 2.43, 1.0], [-0.104, -0.216, 1.24, 0.551], [-0.062, -0.903, 1e+300, 0.907], [-0.0828, -1.07, 1.86, 0.814], [0.137, -0.702, 1.57, 0.69], [0.362, -1.68, 2.3, 1.0]], "p": 2, "u": [1.79, 1.79, 1.79, 2.35, 2.91, 3.48, 4.04, 4.04, 4.04]}, "op": "non_rat", "tol": 0.001}},
|
||||
{"name": "fuzz00902_runs", "origin": "fuzz-out/n2000/nurbs.b_spline_surface_n_lib_p1/fuzz00902.json (crash: cpp: killed by signal 6 (only after the 50 preceding cases) (double free or corruption (out)))", "issue": "UB-225", "cpp": "ok", "rust": "ok", "inputs": {"op": "ext_eval", "this": {"srf": {"net": [[[-0.124, -0.18, 0.827, -1.7976931348623157e+308], [-0.108, 1.06, -0.736, -1.7976931348623157e+308], [0.00887, 1.99, 0.215, -1.7976931348623157e+308]], [[1.16, 0.154, 0.417, -1.7976931348623157e+308], [1.11, 1.16, -1.0, -1.7976931348623157e+308], [0.89, 1.9, -0.557, -1.7976931348623157e+308]]], "p": 1, "q": 1, "u": [0.0, 0.0, 1.0, 1.0], "v": [0.0, 0.0, 0.5, 1.0, 1.0]}}, "uv": [0.584, 0.294]}},
|
||||
{"name": "fuzz01032_bad_index_count_9223372036854776000", "origin": "fuzz-out/n2000/nurbs.b_spline_surface_n_lib_p1/fuzz01032.json (panic: rust: capacity overflow)", "cpp": "harness: invalid argument: bad index/count 9223372036854776000", "rust": "harness: make_knots: knot count too large", "inputs": {"n": 3, "op": "make_knots", "p": 9223372036854775807, "u": [0.632]}},
|
||||
{"name": "fuzz01411_surface_euclidean_point_out_of_range", "origin": "fuzz-out/n2000/nurbs.b_spline_surface_n_lib_p1/fuzz01411.json (timeout: cpp: no result after 20 s)", "issue": "HANG-6", "cpp": "harness: invalid argument: surface: Euclidean point out of range", "rust": "ok", "inputs": {"cont": 5, "dir": 2, "dir_overload": true, "dist": 0.383, "op": "extend", "this": {"srf": {"net": [[[-0.101, -0.00117, 0.0614, -1.7976931348623157e+308], [0.0924, 1.17, 0.243, -1.7976931348623157e+308], [0.112, 1e+300, 0.00758, -1.7976931348623157e+308], [0.0343, 2.89, 0.156, -1.7976931348623157e+308]], [[1.04, 0.00631, 0.301, -1.7976931348623157e+308], [0.962, 0.907, 0.11, -1.7976931348623157e+308], [1.17, 1.9, -0.0589, -1.7976931348623157e+308], [0.854, 3.11, -0.215, -1.7976931348623157e+308]], [[2.0, -0.128, 0.289, -1.7976931348623157e+308], [1.82, 0.959, -0.0642, -1.7976931348623157e+308], [1.84, 2.12, -0.308, -1.7976931348623157e+308], [1.88, 2.84, -0.126, -1.7976931348623157e+308]], [[2.99, 0.1, -0.214, -1.7976931348623157e+308], [3.08, 0.979, 0.244, -1.7976931348623157e+308], [2.83, 1.98, 0.193, -1.7976931348623157e+308], [2.96, 2.87, -0.279, -1.7976931348623157e+308]], [[3.94, 0.0173, 0.245, -1.7976931348623157e+308], [4.13, 1.04, 0.292, -1.7976931348623157e+308], [3.99, 2.11, 0.172, -1.7976931348623157e+308], [3.85, 3.19, -0.219, -1.7976931348623157e+308]]], "p": 3, "q": 2, "u": [0.0, 0.0, 0.0, 0.0, 0.5, 1.0, 1.0, 1.0, 1.0], "v": [0.0, 0.0, 0.0, 0.5, 1.0, 1.0, 1.0]}}}}
|
||||
{"name": "fuzz01032_bad_index_count_9223372036854776000", "origin": "fuzz-out/n2000/nurbs.b_spline_surface_n_lib_p1/fuzz01032.json (panic: rust: capacity overflow)", "cpp": "harness: invalid argument: bad index/count 9223372036854776000", "rust": "harness: make_knots: knot count too large", "inputs": {"n": 3, "op": "make_knots", "p": 9223372036854775807, "u": [0.632]}}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
//!
|
||||
//! | [`Op`] | upstream | input |
|
||||
//! |-----------------|------------------------------------|-----------------------------------------|
|
||||
//! | `ExtendByDist` | `N_SrfExtendByDist` | surface, `dist dflg end cont inplace` |
|
||||
//! | `MaxDeriv` | `N_SrfOffsetGetMaxDeriv` | surface, `d` |
|
||||
//! | `Unbounded` | `N_SrfEvalPtDerivsUnbounded` | surface, `cflg ndr cnt`, `cnt` `(u, v)` |
|
||||
//! | `SkinSpine` | `N_CreateSkinSpine` | `k`, curves `0..=k`, spine, params `0..=k`, `cmp align z0x z0y z0z subdiv dflag deg dir` |
|
||||
@@ -20,9 +19,6 @@
|
||||
//! and rejects every argument that would make upstream index outside its arrays or
|
||||
//! loop without end:
|
||||
//!
|
||||
//! - `ExtendByDist`: `dflg` `1..=2`, `end` `1..=2`, `cont` one of `2, 3, 4, 6`; with
|
||||
//! `cont` `2` or `3` a distance of at least a parameter step that
|
||||
//! is not lost to rounding (see `g2_extension_ok`).
|
||||
//! - `Unbounded`: `cflg` one of `2, 3, 4, 6`; `ndr` `0..=`[`MAX_NDR`]; `1..=`[`MAX_EVALS`]
|
||||
//! evaluations; each parameter either within [`MAX_REACH`] domain lengths of the
|
||||
//! domain (bounding the periodic wrap loops) or beyond `1e12` in magnitude
|
||||
@@ -35,8 +31,11 @@
|
||||
//! - `VectorField`: `bndy` `1..=4`. Two valid curves with equal knot vectors have
|
||||
//! equal degrees and control point counts, so upstream never reads past the
|
||||
//! vector field's control points.
|
||||
//!
|
||||
//! Shim op `0` (`N_SrfExtendByDist`) was removed with the port's fitted extension;
|
||||
//! its number is not reused.
|
||||
|
||||
use crate::validate::{bad, euclid, NOW};
|
||||
use crate::validate::{bad, NOW};
|
||||
use crate::{call_with_buffer, FfiError};
|
||||
|
||||
/// Largest highest net index (`n`, `m`) of a surface accepted by [`eval`].
|
||||
@@ -59,8 +58,6 @@ pub const MAX_SUBDIV: usize = 16;
|
||||
/// The upstream routine run by the shim.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Op {
|
||||
/// `N_SrfExtendByDist`.
|
||||
ExtendByDist,
|
||||
/// `N_SrfOffsetGetMaxDeriv`.
|
||||
MaxDeriv,
|
||||
/// `N_SrfEvalPtDerivsUnbounded` (a sequence of calls sharing `esur`).
|
||||
@@ -74,7 +71,6 @@ pub enum Op {
|
||||
impl Op {
|
||||
fn code(self) -> i32 {
|
||||
match self {
|
||||
Op::ExtendByDist => 0,
|
||||
Op::MaxDeriv => 1,
|
||||
Op::Unbounded => 2,
|
||||
Op::SkinSpine => 3,
|
||||
@@ -188,89 +184,6 @@ fn curve<'a>(rd: &mut Reader<'a>) -> Result<(usize, usize, &'a [f64]), FfiError>
|
||||
Ok((n, p, u))
|
||||
}
|
||||
|
||||
/// Smallest parameter step of the `NL_G2` search, in units of `f64::EPSILON` times the
|
||||
/// largest knot magnitude (see `g2_extension_ok`).
|
||||
pub const MIN_G2_STEP_EPS: f64 = 1024.0;
|
||||
|
||||
/// The validated surface at the start of `input` (`n m p q`, the net, the u and v
|
||||
/// knots): a bound on the control polygon length of every iso-curve running in u
|
||||
/// (`udir`) or v, and the knots of that direction. The bound is the sum, over
|
||||
/// consecutive net rows (columns), of the largest distance between corresponding
|
||||
/// Euclidean points; it is infinite when a distance is not finite.
|
||||
fn iso_polygon_bound(input: &[f64], udir: bool) -> Result<(f64, &[f64]), FfiError> {
|
||||
let head = |k: usize| {
|
||||
input
|
||||
.get(k)
|
||||
.map(|&x| x as usize)
|
||||
.ok_or_else(|| bad("input too short"))
|
||||
};
|
||||
let (n, m, p, q) = (head(0)?, head(1)?, head(2)?, head(3)?);
|
||||
let pt = |i: usize, j: usize| -> Result<[f64; 3], FfiError> {
|
||||
let at = 4 + 4 * (i * (m + 1) + j);
|
||||
match input.get(at..at + 4) {
|
||||
Some(&[x, y, z, w]) => Ok(euclid([x, y, z, w])),
|
||||
_ => Err(bad("input too short")),
|
||||
}
|
||||
};
|
||||
let u0 = 4 + 4 * (n + 1) * (m + 1);
|
||||
let v0 = u0 + n + p + 2;
|
||||
let knots = if udir {
|
||||
input.get(u0..v0)
|
||||
} else {
|
||||
input.get(v0..v0 + m + q + 2)
|
||||
}
|
||||
.ok_or_else(|| bad("input too short"))?;
|
||||
let dist = |a: [f64; 3], b: [f64; 3]| {
|
||||
let [dx, dy, dz] = [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
||||
(dx * dx + dy * dy + dz * dz).sqrt()
|
||||
};
|
||||
let (rows, cols) = if udir { (n, m) } else { (m, n) };
|
||||
let mut total = 0.0;
|
||||
for a in 0..rows {
|
||||
let mut longest: f64 = 0.0;
|
||||
for b in 0..=cols {
|
||||
let (p0, p1) = if udir {
|
||||
(pt(a, b)?, pt(a + 1, b)?)
|
||||
} else {
|
||||
(pt(b, a)?, pt(b, a + 1)?)
|
||||
};
|
||||
let d = dist(p0, p1);
|
||||
if !d.is_finite() {
|
||||
return Ok((f64::INFINITY, knots));
|
||||
}
|
||||
longest = longest.max(d);
|
||||
}
|
||||
total += longest;
|
||||
}
|
||||
Ok((total, knots))
|
||||
}
|
||||
|
||||
/// `ST_surxsdr` extends sampled iso-curves with `ST_curxcdr` `NL_G2` for `cont`
|
||||
/// `NL_G1` / `NL_G2`. Its parameter search steps by `du = range * dist / d1` (`d1` the
|
||||
/// iso-curve's arc length) and never ends when that step is lost to rounding (zero
|
||||
/// or tiny `dist`, a huge or overflowing iso-curve length; HANG-6). So `dist` must
|
||||
/// be non-zero and the step
|
||||
/// estimated with [`iso_polygon_bound`] in place of `d1` (a lower bound of `du` for
|
||||
/// non-rational surfaces) at least [`MIN_G2_STEP_EPS`] `f64::EPSILON`s of the largest
|
||||
/// knot magnitude.
|
||||
fn g2_extension_ok(input: &[f64], dist: f64, udir: bool, cont: usize) -> Result<(), FfiError> {
|
||||
if cont != 2 && cont != 3 {
|
||||
return Ok(());
|
||||
}
|
||||
let (bound, knots) = iso_polygon_bound(input, udir)?;
|
||||
let (Some(&first), Some(&last)) = (knots.first(), knots.last()) else {
|
||||
return Err(bad("empty knot vector"));
|
||||
};
|
||||
let step = (last - first) * (dist / bound);
|
||||
let floor = MIN_G2_STEP_EPS * f64::EPSILON * first.abs().max(last.abs());
|
||||
if !(step.abs() >= floor && dist != 0.0) {
|
||||
return Err(bad(
|
||||
"extend by distance: G1/G2 step lost to rounding (distance too short)",
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cont_flag(rd: &mut Reader<'_>) -> Result<usize, FfiError> {
|
||||
let cont = rd.index(2, 6)?;
|
||||
if cont == 5 {
|
||||
@@ -301,15 +214,6 @@ fn check_reach(t: f64, k: &[f64]) -> Result<(), FfiError> {
|
||||
fn validate(op: Op, input: &[f64]) -> Result<(), FfiError> {
|
||||
let mut rd = Reader::new(input);
|
||||
match op {
|
||||
Op::ExtendByDist => {
|
||||
surface(&mut rd)?;
|
||||
let dist = rd.num()?;
|
||||
let udir = rd.index(1, 2)? == 1;
|
||||
rd.index(1, 2)?;
|
||||
let cont = cont_flag(&mut rd)?;
|
||||
rd.index(0, 1)?;
|
||||
g2_extension_ok(input, dist, udir, cont)?;
|
||||
}
|
||||
Op::MaxDeriv => {
|
||||
surface(&mut rd)?;
|
||||
rd.num()?;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
//!
|
||||
//! | [`Op`] | upstream |
|
||||
//! |-----------------|-------------------------------------------|
|
||||
//! | `ExtendByDist` | `ST_surxsdr` (core of `N_SrfExtendByDist`) |
|
||||
//! | `NormalDeriv` | `N_SrfEvalPtNormalDeriv` |
|
||||
//!
|
||||
//! Input encoding (flat doubles) and output records are described in the shim.
|
||||
@@ -14,14 +13,13 @@
|
||||
//! `r = n + p + 1`, `s = m + q + 1` and valid clamped knot vectors
|
||||
//! (`N_KnotVectorIsValid`: end multiplicities `p + 1`, non-decreasing, interior
|
||||
//! multiplicities at most `p`), weights all `NOW` or all positive; every coordinate
|
||||
//! and parameter must be finite. The
|
||||
//! extension takes `dist dflg end cont depth inplace` with `dflg` in `1..=2`, `end`
|
||||
//! in `1..=2`, `cont` one of `2, 3, 4, 6` (with `2` or `3`, a non-zero distance whose
|
||||
//! parameter step is not lost to rounding, see `g2_extension_ok`) and
|
||||
//! `depth` in `0..=11`; the normal
|
||||
//! derivatives take `u v ufl vfl` with side flags in `1..=2`.
|
||||
//! and parameter must be finite. The normal derivatives take `u v ufl vfl` with side
|
||||
//! flags in `1..=2`.
|
||||
//!
|
||||
//! Shim op `0` (`ST_surxsdr`, the surface extension by a distance) was removed with
|
||||
//! the port's fitted extension; its number is not reused.
|
||||
|
||||
use crate::validate::{bad, euclid, NOW};
|
||||
use crate::validate::{bad, NOW};
|
||||
use crate::{call_with_buffer, FfiError};
|
||||
|
||||
/// Largest highest index (`n`, `m`) accepted by [`eval`].
|
||||
@@ -32,8 +30,6 @@ pub const MAX_DEGREE: usize = 17;
|
||||
/// The upstream routine run by the shim.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Op {
|
||||
/// `ST_surxsdr`; then `dist dflg end cont depth inplace`.
|
||||
ExtendByDist,
|
||||
/// `N_SrfEvalPtNormalDeriv`; then `u v ufl vfl`.
|
||||
NormalDeriv,
|
||||
}
|
||||
@@ -41,7 +37,6 @@ pub enum Op {
|
||||
impl Op {
|
||||
fn code(self) -> i32 {
|
||||
match self {
|
||||
Op::ExtendByDist => 0,
|
||||
Op::NormalDeriv => 1,
|
||||
}
|
||||
}
|
||||
@@ -122,8 +117,7 @@ fn surface(rd: &mut Reader<'_>) -> Result<[usize; 4], FfiError> {
|
||||
}
|
||||
let net = rd.slice(4 * (n + 1) * (m + 1))?;
|
||||
// Weights: all NOW (non-rational) or all positive. Anything else is no valid
|
||||
// surface; a zero weight makes Euclidean points infinite, and the extension then
|
||||
// reads past its scratch rows when a reflection plane cannot be normalised.
|
||||
// surface.
|
||||
let ws: Vec<f64> = net.chunks(4).filter_map(|c| c.get(3).copied()).collect();
|
||||
if !(ws.iter().all(|&w| w == NOW) || ws.iter().all(|&w| w > 0.0)) {
|
||||
return Err(bad("mixed or non-positive weights"));
|
||||
@@ -136,105 +130,10 @@ fn surface(rd: &mut Reader<'_>) -> Result<[usize; 4], FfiError> {
|
||||
Ok([n, m, p, q])
|
||||
}
|
||||
|
||||
/// Smallest parameter step of the `NL_G2` search, in units of `f64::EPSILON` times the
|
||||
/// largest knot magnitude (see `g2_extension_ok`).
|
||||
pub const MIN_G2_STEP_EPS: f64 = 1024.0;
|
||||
|
||||
/// The validated surface at the start of `input` (`n m p q`, the net, the u and v
|
||||
/// knots): a bound on the control polygon length of every iso-curve running in u
|
||||
/// (`udir`) or v, and the knots of that direction. The bound is the sum, over
|
||||
/// consecutive net rows (columns), of the largest distance between corresponding
|
||||
/// Euclidean points; it is infinite when a distance is not finite.
|
||||
fn iso_polygon_bound(input: &[f64], udir: bool) -> Result<(f64, &[f64]), FfiError> {
|
||||
let head = |k: usize| {
|
||||
input
|
||||
.get(k)
|
||||
.map(|&x| x as usize)
|
||||
.ok_or_else(|| bad("input too short"))
|
||||
};
|
||||
let (n, m, p, q) = (head(0)?, head(1)?, head(2)?, head(3)?);
|
||||
let pt = |i: usize, j: usize| -> Result<[f64; 3], FfiError> {
|
||||
let at = 4 + 4 * (i * (m + 1) + j);
|
||||
match input.get(at..at + 4) {
|
||||
Some(&[x, y, z, w]) => Ok(euclid([x, y, z, w])),
|
||||
_ => Err(bad("input too short")),
|
||||
}
|
||||
};
|
||||
let u0 = 4 + 4 * (n + 1) * (m + 1);
|
||||
let v0 = u0 + n + p + 2;
|
||||
let knots = if udir {
|
||||
input.get(u0..v0)
|
||||
} else {
|
||||
input.get(v0..v0 + m + q + 2)
|
||||
}
|
||||
.ok_or_else(|| bad("input too short"))?;
|
||||
let dist = |a: [f64; 3], b: [f64; 3]| {
|
||||
let [dx, dy, dz] = [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
||||
(dx * dx + dy * dy + dz * dz).sqrt()
|
||||
};
|
||||
let (rows, cols) = if udir { (n, m) } else { (m, n) };
|
||||
let mut total = 0.0;
|
||||
for a in 0..rows {
|
||||
let mut longest: f64 = 0.0;
|
||||
for b in 0..=cols {
|
||||
let (p0, p1) = if udir {
|
||||
(pt(a, b)?, pt(a + 1, b)?)
|
||||
} else {
|
||||
(pt(b, a)?, pt(b, a + 1)?)
|
||||
};
|
||||
let d = dist(p0, p1);
|
||||
if !d.is_finite() {
|
||||
return Ok((f64::INFINITY, knots));
|
||||
}
|
||||
longest = longest.max(d);
|
||||
}
|
||||
total += longest;
|
||||
}
|
||||
Ok((total, knots))
|
||||
}
|
||||
|
||||
/// `ST_surxsdr` extends sampled iso-curves with `ST_curxcdr` `NL_G2` for `cont`
|
||||
/// `NL_G1` / `NL_G2`. Its parameter search steps by `du = range * dist / d1` (`d1` the
|
||||
/// iso-curve's arc length) and never ends when that step is lost to rounding (zero
|
||||
/// or tiny `dist`, a huge or overflowing iso-curve length; HANG-6). So `dist` must
|
||||
/// be non-zero and the step
|
||||
/// estimated with [`iso_polygon_bound`] in place of `d1` (a lower bound of `du` for
|
||||
/// non-rational surfaces) at least [`MIN_G2_STEP_EPS`] `f64::EPSILON`s of the largest
|
||||
/// knot magnitude.
|
||||
fn g2_extension_ok(input: &[f64], dist: f64, udir: bool, cont: usize) -> Result<(), FfiError> {
|
||||
if cont != 2 && cont != 3 {
|
||||
return Ok(());
|
||||
}
|
||||
let (bound, knots) = iso_polygon_bound(input, udir)?;
|
||||
let (Some(&first), Some(&last)) = (knots.first(), knots.last()) else {
|
||||
return Err(bad("empty knot vector"));
|
||||
};
|
||||
let step = (last - first) * (dist / bound);
|
||||
let floor = MIN_G2_STEP_EPS * f64::EPSILON * first.abs().max(last.abs());
|
||||
if !(step.abs() >= floor && dist != 0.0) {
|
||||
return Err(bad(
|
||||
"extend by distance: G1/G2 step lost to rounding (distance too short)",
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate(op: Op, input: &[f64]) -> Result<(), FfiError> {
|
||||
let mut rd = Reader::new(input);
|
||||
surface(&mut rd)?;
|
||||
match op {
|
||||
Op::ExtendByDist => {
|
||||
let dist = rd.num()?;
|
||||
let udir = rd.index(1, 2)? == 1;
|
||||
rd.index(1, 2)?;
|
||||
let cont = rd.index(2, 6)?;
|
||||
if cont == 5 {
|
||||
return Err(bad("bad continuity flag"));
|
||||
}
|
||||
rd.index(0, 11)?;
|
||||
rd.index(0, 1)?;
|
||||
g2_extension_ok(input, dist, udir, cont)?;
|
||||
}
|
||||
Op::NormalDeriv => {
|
||||
rd.num()?;
|
||||
rd.num()?;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
//! | [`Op`] | upstream | parameters |
|
||||
//! |---------------|---------------------------------------|----------------------------------------------|
|
||||
//! | `Offset` | `ApproximateOffsetSurface` | `dist tol param_type` |
|
||||
//! | `Extend` | `CreateExtendedSurface` | `dir_overload dir dist continuity` |
|
||||
//! | `Join` | `JoinSurface` | surface `dir tol` |
|
||||
//! | `ExtEval` | `EvaluatePointOnExtendedSurface` | `u v` |
|
||||
//! | `Reparam` | `Reparameterize` | `D[4]` |
|
||||
@@ -88,7 +87,6 @@ const FORM_POLYNOMIAL: usize = 10;
|
||||
#[allow(missing_docs)]
|
||||
pub enum Op {
|
||||
Offset,
|
||||
Extend,
|
||||
Join,
|
||||
ExtEval,
|
||||
Reparam,
|
||||
@@ -111,7 +109,8 @@ impl Op {
|
||||
fn code(self) -> i32 {
|
||||
match self {
|
||||
Op::Offset => 0,
|
||||
Op::Extend => 1,
|
||||
// 1: `CreateExtendedSurface`, removed with the port's fitted extension; not
|
||||
// reused.
|
||||
Op::Join => 2,
|
||||
Op::ExtEval => 3,
|
||||
Op::Reparam => 4,
|
||||
@@ -457,12 +456,6 @@ fn validate_surface_op(
|
||||
rd.positive("offset tol")?;
|
||||
rd.index(3)?;
|
||||
}
|
||||
Op::Extend => {
|
||||
rd.flag()?;
|
||||
rd.index(9)?;
|
||||
rd.positive("extend dist")?;
|
||||
rd.index(13)?;
|
||||
}
|
||||
Op::Join => {
|
||||
surface(rd)?;
|
||||
if rd.index(1)? > 1 {
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
//! | `Trim` | `TrimWithDomain` | the (snapped) domain |
|
||||
//! | `Split` | `SplitAt` | both halves' states |
|
||||
//! | `CreateOffset` | `CreateOffsetSurface` | the result's state |
|
||||
//! | `Extend`, `ExtendDir` | `CreateExtendedSurface` | (refused, see below) |
|
||||
//! | `Copy` | `Copy` | the copy's state |
|
||||
//! | `Eq` | `operator==` with another offset and itself | `eq self_eq` |
|
||||
//! | `IsPlanar` | `IsPlanar` (`SmSurface` body, virtual box) | `planar P N dev` |
|
||||
@@ -51,10 +50,8 @@
|
||||
//! may lie outside the base domain (the base-class box of the offset asks the offset
|
||||
//! again for the outside part, without bound once an extension exists, and its surface
|
||||
//! cache subdivision runs away for small trimmed bases), no planarity test once an
|
||||
//! extension may exist, and no `CreateExtendedSurface` ([`Step::Extend`],
|
||||
//! [`Step::ExtendDir`]): its in-place retry reads arrays a failed extension half-wrote
|
||||
//! (UB-107) and its arc-length searches can stall or crawl (HANG-6, HANG-22), which
|
||||
//! nothing outside NLib can detect before the C++ runs in this process. The shim checks
|
||||
//! extension may exist. (Step codes `15` and `16`, `CreateExtendedSurface`, were removed
|
||||
//! with the port's fitted extension; they are not reused.) The shim checks
|
||||
//! the state-dependent cases on the actual surfaces as the script runs: the non-zero
|
||||
//! offset writing past the caller's array for `bOnlyUpperHalf` orders (UB-171, only
|
||||
//! when the compound distance is not exactly `0.0`), zero-distance delegation to an
|
||||
@@ -269,22 +266,6 @@ pub enum Step {
|
||||
/// Signed distance.
|
||||
d: f64,
|
||||
},
|
||||
/// `CreateExtendedSurface(dist, cont)` (refused: module docs).
|
||||
Extend {
|
||||
/// Distance.
|
||||
dist: f64,
|
||||
/// `SmContinuityType` code.
|
||||
cont: u8,
|
||||
},
|
||||
/// `CreateExtendedSurface(param, dist, cont)` (refused: module docs).
|
||||
ExtendDir {
|
||||
/// `SmSurfParamType` code.
|
||||
param: u8,
|
||||
/// Distance.
|
||||
dist: f64,
|
||||
/// `SmContinuityType` code.
|
||||
cont: u8,
|
||||
},
|
||||
/// `Copy`.
|
||||
Copy,
|
||||
/// `operator==` with another offset, then with itself.
|
||||
@@ -347,8 +328,6 @@ impl Step {
|
||||
Step::Trim { .. } => 12,
|
||||
Step::Split { .. } => 13,
|
||||
Step::CreateOffset { .. } => 14,
|
||||
Step::Extend { .. } => 15,
|
||||
Step::ExtendDir { .. } => 16,
|
||||
Step::Copy => 17,
|
||||
Step::Eq { .. } => 18,
|
||||
Step::SetDist { .. } => 19,
|
||||
@@ -791,11 +770,6 @@ fn push_steps(o: &mut Vec<f64>, off: &Off, info: BaseInfo, steps: &[Step]) -> Re
|
||||
finite(&[*d], "offset distance")?;
|
||||
o.push(*d);
|
||||
}
|
||||
Step::Extend { .. } | Step::ExtendDir { .. } => {
|
||||
return Err(bad(
|
||||
"extend: CreateExtendedSurface is not run in process (UB-107, HANG-6, HANG-22)",
|
||||
));
|
||||
}
|
||||
Step::Eq { other } => {
|
||||
push_off(o, other, 1)?;
|
||||
}
|
||||
@@ -1136,41 +1110,6 @@ mod tests {
|
||||
assert!(refused(&script(&tiny, &[Step::State, eval(1, 1, false)])));
|
||||
}
|
||||
|
||||
/// UB-107, HANG-6 and HANG-22 are inside `CreateExtendedSurface`, which cannot be
|
||||
/// guarded in process: the FFI refuses every extension step, and the shim refuses
|
||||
/// one sent past the FFI without running it.
|
||||
#[test]
|
||||
fn extension_steps_are_refused_in_process() {
|
||||
let off = Off {
|
||||
base: bss(1.0),
|
||||
..plane(0.25)
|
||||
};
|
||||
let steps = [
|
||||
Step::Extend { dist: 0.1, cont: 5 },
|
||||
Step::ExtendDir {
|
||||
param: 7,
|
||||
dist: 0.1,
|
||||
cont: 13,
|
||||
},
|
||||
];
|
||||
for step in steps {
|
||||
assert!(matches!(
|
||||
check(&off, std::slice::from_ref(&step)),
|
||||
Err(FfiError::InvalidArgument(_))
|
||||
));
|
||||
assert!(refused(&script(&off, &[Step::State, step])));
|
||||
}
|
||||
let mut input = Vec::new();
|
||||
push_off(&mut input, &off, 0).unwrap();
|
||||
input.extend([2.0, 0.0, 15.0, 0.1, 5.0]);
|
||||
assert!(refused(&call(OP_SCRIPT, &input)));
|
||||
input.truncate(input.len() - 4);
|
||||
input.extend([16.0, 7.0, 0.1, 13.0]);
|
||||
assert!(refused(&call(OP_SCRIPT, &input)));
|
||||
// a refusal leaves nothing behind for the next call
|
||||
assert!(script(&off, &[Step::State, eval(1, 1, false)]).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_revolution_domain_cannot_bypass_polar_guard() {
|
||||
let off = Off {
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
//! | `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 |
|
||||
//! | `Extend` | of a copy: `CreateExtendedSurface(dist, cont)` or `CreateExtendedSurface(dir, dist, cont)` | the extension's NURB `has [..]` |
|
||||
//!
|
||||
//! [`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
|
||||
@@ -292,17 +291,6 @@ pub enum Op {
|
||||
Reverse(bool),
|
||||
/// `SwapUV()`.
|
||||
Swap,
|
||||
/// `CreateExtendedSurface` of a copy: without a direction the `SM_SP_BOTH`
|
||||
/// overload, else with `SmSurfParamType` `dir` (at most `SM_SP_VMAX`, 8). The
|
||||
/// distance must be positive.
|
||||
Extend {
|
||||
/// The `SmSurfParamType` code, or `None` for the overload without one.
|
||||
dir: Option<u8>,
|
||||
/// Extension distance.
|
||||
dist: f64,
|
||||
/// The `SmContinuityType` code (at most `SM_CT_CINFINITY`, 13).
|
||||
cont: u8,
|
||||
},
|
||||
}
|
||||
|
||||
fn finite(x: f64) -> Result<f64, FfiError> {
|
||||
@@ -662,21 +650,6 @@ impl Enc {
|
||||
self.flag(*v_dir);
|
||||
}
|
||||
Op::Swap => self.int(25),
|
||||
Op::Extend { dir, dist, cont } => {
|
||||
if dir.is_some_and(|d| d > 8) || *cont > 13 {
|
||||
return Err(bad("extension direction or continuity"));
|
||||
}
|
||||
// upstream halves a failing extension until it falls below dDist / 100,
|
||||
// which never happens for a zero distance (an endless loop)
|
||||
if dist.is_nan() || *dist <= 0.0 {
|
||||
return Err(bad("extension distance must be positive"));
|
||||
}
|
||||
self.int(26);
|
||||
self.flag(dir.is_some());
|
||||
self.int(usize::from(dir.unwrap_or(0)));
|
||||
self.num(*dist)?;
|
||||
self.int(usize::from(*cont));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -336,15 +336,6 @@ pub enum Query {
|
||||
/// `dOpt3DTol`.
|
||||
tol: f64,
|
||||
},
|
||||
/// `CreateExtendedSurface(ctx, [dir,] dist, cont, e)`.
|
||||
Extend {
|
||||
/// `eExtDirection` (`SmSurfParamType` code); `None` for the overload without it.
|
||||
dir: Option<u8>,
|
||||
/// `dDist`.
|
||||
dist: f64,
|
||||
/// `SmContinuityType` code.
|
||||
cont: u8,
|
||||
},
|
||||
/// `IsPlanar(tol, &point, &normal, &deviation)`.
|
||||
Planar {
|
||||
/// `dTol3d`.
|
||||
@@ -796,9 +787,7 @@ pub fn is_nurb(test: &TestSurf, scale: f64) -> Result<Vec<f64>, FfiError> {
|
||||
/// returns the shim's record: as [`is_nurb`] up to `0 1 had_nurb`, then the query's
|
||||
/// outputs. `EvalSimple`, `EvalFirst`: status, then on success the `(hu + 1) (hv + 1)`
|
||||
/// derivatives `x y z` (U order major). `BBox`: status, then on success the box corners.
|
||||
/// `DegenPoint`, `DegenCurve`: the flag. `Extend`: status, then on success the new
|
||||
/// surface's type, whether it has a NURB and that NURB (`p q n+1 m+1 cpts(x y z w) nu U nv
|
||||
/// V`). `Planar`: the flag, point, normal and deviation. `NumCpts`: the count.
|
||||
/// `DegenPoint`, `DegenCurve`: the flag. `Planar`: the flag, point, normal and deviation. `NumCpts`: the count.
|
||||
/// `Bilinear`, `Parallel`, `Seam`: the flag. `Knots`: status, then on success `n knots m
|
||||
/// mults`. `TessSplits`: status, then on success `n splits`.
|
||||
///
|
||||
@@ -864,21 +853,6 @@ pub fn lazy(test: &TestSurf, scale: f64, query: &Query) -> Result<Vec<f64>, FfiE
|
||||
opt_dom(o, *dom)?;
|
||||
o.push(*tol);
|
||||
}
|
||||
Query::Extend { dir, dist, cont } => {
|
||||
finite(&[*dist], "extension distance")?;
|
||||
if *dist <= 0.0 {
|
||||
return Err(bad("extension distance not positive"));
|
||||
}
|
||||
if dir.is_some_and(|d| d > 8) || *cont > 13 {
|
||||
return Err(bad("extension enumerator out of range"));
|
||||
}
|
||||
o.extend([
|
||||
if dir.is_some() { 6.0 } else { 5.0 },
|
||||
f64::from(dir.unwrap_or(0)),
|
||||
*dist,
|
||||
f64::from(*cont),
|
||||
]);
|
||||
}
|
||||
Query::Planar { tol } => {
|
||||
finite(&[*tol], "planar tolerance")?;
|
||||
o.extend([7.0, *tol]);
|
||||
|
||||
@@ -29,8 +29,6 @@
|
||||
//! | `Trim` | `TrimWithDomain` | `D` |
|
||||
//! | `Split` | `SplitAt` | `t dir` |
|
||||
//! | `StepInv` | `STEPInversion` | `D V tol has_guess gu gv` |
|
||||
//! | `Extend` | `CreateExtendedSurface` | `dist cont` |
|
||||
//! | `ExtendDir` | `CreateExtendedSurface` (dir) | `dir dist cont` |
|
||||
//! | `AdjustStep` | `AdjustSTEPUVDomain` | `D` |
|
||||
//! | `MaxAnal` | `GetMaxAnalyticDomain` | - |
|
||||
//! | `Copy`, `CopyAnal` | `Copy`, `CopyAndAddAnalytics` | - |
|
||||
@@ -95,8 +93,6 @@ pub enum Op {
|
||||
Trim,
|
||||
Split,
|
||||
StepInv,
|
||||
Extend,
|
||||
ExtendDir,
|
||||
AdjustStep,
|
||||
MaxAnal,
|
||||
Copy,
|
||||
@@ -149,8 +145,8 @@ impl Op {
|
||||
Trim => 9,
|
||||
Split => 10,
|
||||
StepInv => 11,
|
||||
Extend => 12,
|
||||
ExtendDir => 13,
|
||||
// 12, 13: `CreateExtendedSurface` (both forms), removed with the port's fitted
|
||||
// extension; not reused.
|
||||
AdjustStep => 14,
|
||||
MaxAnal => 15,
|
||||
Copy => 16,
|
||||
@@ -474,15 +470,6 @@ fn validate_surface_op(op: Op, rd: &mut Reader<'_>, dom: Option<[f64; 4]>) -> Re
|
||||
rd.flag()?;
|
||||
rd.finites(2)?;
|
||||
}
|
||||
Extend => {
|
||||
rd.positive("extension distance")?;
|
||||
rd.index(13)?;
|
||||
}
|
||||
ExtendDir => {
|
||||
rd.index(8)?;
|
||||
rd.positive("extension distance")?;
|
||||
rd.index(13)?;
|
||||
}
|
||||
DropFast => {
|
||||
rd.domain()?;
|
||||
rd.vec3()?;
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
//! | [`create_coons_srf`], [`CoonsWarning`] | `sm_CreateCoonsSrf` (static) |
|
||||
//! | [`BSplineSurface::create_coons_patch`], [`BSplineSurface::create_coons_patch_on_faces`] | `CreateCoonsPatch` |
|
||||
//! | `face_side_twist` (private) | the fork's `sm_FaceSideTwist` (static) |
|
||||
//! | [`BSplineSurface::create_extended_surface`] | `CreateExtendedSurface` (distance from each side) |
|
||||
//! | [`BSplineSurface::create_extended_surface_dir`] | `CreateExtendedSurface` (with `SmSurfParamType`) |
|
||||
//! | [`BSplineSurface::join_surface`] | `JoinSurface` |
|
||||
//! | [`BSplineSurface::create_n_sided_patch`] | `CreateNSidedPatch` |
|
||||
//! | [`BSplineSurface::create_ruled_surface`] | `CreateRuledSurface` |
|
||||
@@ -100,13 +98,6 @@
|
||||
//! `NER` for other curve types) and the caller's
|
||||
//! [`Surface`](crate::surface::surface::Surface) as base surface (with its kind and
|
||||
//! owner, as upstream's `SmSurface*`).
|
||||
//! - **`CreateExtendedSurface`** halves the extension distance after each failed
|
||||
//! extension until it drops below `dDist / 100`, which never happens for a zero or
|
||||
//! NaN distance (upstream then loops forever); the port stops after
|
||||
//! [`MAX_EXTEND_HALVINGS`] halvings with [`SolidError::NotConverged`]. After a
|
||||
//! successful extension it doubles the distance until it reaches `dDist / 2`, which
|
||||
//! a negative distance never does (it doubles towards `-inf`, upstream HANG-16); the
|
||||
//! port stops after [`MAX_EXTEND_DOUBLINGS`] doublings with the same error.
|
||||
//! - **`N_LinearMultiplyCrv`** returns nothing upstream; its port fails only for a
|
||||
//! degree-0 cross-boundary derivative (no Greville abscissae), which
|
||||
//! [`create_coons_srf`] passes on.
|
||||
@@ -119,7 +110,6 @@ use crate::error::{Result, SolidError};
|
||||
use crate::geom::axis2_placement::Axis2Placement;
|
||||
use crate::geom::extent1d::Extent1d;
|
||||
use crate::geom::extent2d::Extent2d;
|
||||
use crate::geom::geom_utility::p2::ContinuityType;
|
||||
use crate::geom::tol::are_same;
|
||||
use crate::geom::vector2d::Point2;
|
||||
use crate::geom::vector3d::{Point3, Vec3};
|
||||
@@ -137,7 +127,6 @@ use crate::nurbs::math::{
|
||||
combine2_pts, power_basis_srf_eval_pts, srf_power_basis_eval_derivs, CPoint,
|
||||
};
|
||||
use crate::nurbs::srf_adv::p1::eval_pt_derivs_unbounded;
|
||||
use crate::nurbs::srf_adv::p2::{extend_by_dist_in_place, SrfContinuity};
|
||||
use crate::nurbs::srf_adv::p3::{
|
||||
create_swung_srf, create_trans_sweep_srf, extend_by_param_dist, ExtendContinuity,
|
||||
};
|
||||
@@ -150,20 +139,6 @@ use crate::nurbs::srf_common::ruled_srf;
|
||||
use crate::nurbs::srf_tool::p3::{join, make_compatible as make_surfaces_compatible};
|
||||
use crate::nurbs::srf_tool::p4::make_compatible_uv;
|
||||
|
||||
/// Largest number of distance halvings of one side of
|
||||
/// [`BSplineSurface::create_extended_surface_dir`] (upstream has no bound and loops
|
||||
/// forever for a zero or NaN distance; halving a positive distance reaches zero after
|
||||
/// fewer than 1100 steps).
|
||||
pub const MAX_EXTEND_HALVINGS: usize = 1100;
|
||||
|
||||
/// Largest number of distance doublings of one side of
|
||||
/// [`BSplineSurface::create_extended_surface_dir`]. A positive distance is doubled back
|
||||
/// at most once more than it was halved, so only a negative distance reaches this:
|
||||
/// upstream doubles it away from the loop's exit (`dExtDist < dDist/2.0`) towards
|
||||
/// `-inf` and loops forever while the extensions succeed (`plan/upstream-issues.md`
|
||||
/// HANG-16).
|
||||
pub const MAX_EXTEND_DOUBLINGS: usize = MAX_EXTEND_HALVINGS + 1;
|
||||
|
||||
const OP_BUILD_CURVE: &str = "nurbs::b_spline_surface_n_lib::p1::build_nlib_curve";
|
||||
const OP_NLIB_CURVE: &str = "nurbs::b_spline_surface_n_lib::p1::create_nlib_curve";
|
||||
const OP_NLIB_SURFACE: &str = "nurbs::b_spline_surface_n_lib::p1::create_nlib_surface";
|
||||
@@ -172,8 +147,6 @@ const OP_OFFSET: &str =
|
||||
const OP_NONRAT: &str = "nurbs::b_spline_surface_n_lib::p1::create_non_rational_curve";
|
||||
const OP_COONS_SRF: &str = "nurbs::b_spline_surface_n_lib::p1::create_coons_srf";
|
||||
const OP_COONS: &str = "nurbs::b_spline_surface_n_lib::p1::BSplineSurface::create_coons_patch";
|
||||
const OP_EXTEND: &str =
|
||||
"nurbs::b_spline_surface_n_lib::p1::BSplineSurface::create_extended_surface_dir";
|
||||
const OP_JOIN: &str = "nurbs::b_spline_surface_n_lib::p1::BSplineSurface::join_surface";
|
||||
const OP_NSIDED: &str = "nurbs::b_spline_surface_n_lib::p1::BSplineSurface::create_n_sided_patch";
|
||||
const OP_RULED: &str = "nurbs::b_spline_surface_n_lib::p1::BSplineSurface::create_ruled_surface";
|
||||
@@ -222,25 +195,6 @@ fn surface_nurb<'a>(surface: &'a BSplineSurface, operation: &'static str) -> Res
|
||||
|
||||
// ---- unported SmSurface methods ------------------------------------------------------
|
||||
|
||||
/// `SmSurface::IsSingularity(uv, dir)`: whether `uv` is a singularity and its
|
||||
/// direction.
|
||||
fn is_singularity(k: &Kernel, srf: &BSplineSurface, uv: Point2) -> Result<(bool, SurfParamType)> {
|
||||
// the SmSurface body (surface.surface_p5), on a surface holding a copy
|
||||
let surface = crate::surface::surface::Surface::from(srf.clone_exact());
|
||||
let s = surface.is_singularity(k, uv, crate::math::EFF_ZERO, false)?;
|
||||
Ok((s.singular, s.direction))
|
||||
}
|
||||
|
||||
/// `SmSurface::IsClosed(domain, dir)`.
|
||||
fn is_closed(
|
||||
k: &Kernel,
|
||||
srf: &BSplineSurface,
|
||||
domain: &Extent2d,
|
||||
dir: SurfParamType,
|
||||
) -> Result<bool> {
|
||||
Ok(crate::surface::surface::p7::base_is_closed(k, srf, domain, dir, None, false)?.0)
|
||||
}
|
||||
|
||||
/// The curves dropped by `SmSurface::DropCurve` (with its two distance outputs, which
|
||||
/// the caller here ignores).
|
||||
struct DroppedCurves {
|
||||
@@ -1313,199 +1267,6 @@ impl BSplineSurface {
|
||||
Ok(Self::from_gw_nurb(&sur))
|
||||
}
|
||||
|
||||
/// The surface extended by `dist` across each side ([`SurfParamType::Both`]) with
|
||||
/// continuity `extension_continuity` (`CreateExtendedSurface(crContext, dDist,
|
||||
/// eExtensionContinuity, rpExtended)`).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// The errors of [`BSplineSurface::create_extended_surface_dir`].
|
||||
pub fn create_extended_surface(
|
||||
&self,
|
||||
k: &Kernel,
|
||||
dist: f64,
|
||||
extension_continuity: ContinuityType,
|
||||
) -> Result<BSplineSurface> {
|
||||
// pass the call along
|
||||
self.create_extended_surface_dir(k, SurfParamType::Both, dist, extension_continuity)
|
||||
}
|
||||
|
||||
/// The surface extended by `dist` across the sides named by `ext_direction`
|
||||
/// (`CreateExtendedSurface` with `SmSurfParamType`): [`SurfParamType::U`] /
|
||||
/// [`SurfParamType::V`] both sides of one direction, `UMin`, `VMin`, `UMax`,
|
||||
/// `VMax` one side, `Both` all four, `Neither` none.
|
||||
///
|
||||
/// The continuity is `NL_G1` for [`ContinuityType::G1`] (and any type not listed),
|
||||
/// `NL_G1R` for `G1R`, `NL_G2` for `G1G2` and `NL_CMAX` for `CInfinity`. Sides at a
|
||||
/// singularity and directions in which the surface is closed are not extended; a
|
||||
/// surface closed in both directions is copied. A failing extension is retried with
|
||||
/// half the distance (down to `dist / 100`), then repeated with doubled distances
|
||||
/// until about `dist` is reached.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// `SM_ERR` without NURB, when no extension succeeds, a repeated extension fails
|
||||
/// or an extended surface is invalid; [`SolidError::Unported`] from the
|
||||
/// singularity and closedness tests.
|
||||
pub fn create_extended_surface_dir(
|
||||
&self,
|
||||
k: &Kernel,
|
||||
ext_direction: SurfParamType,
|
||||
dist: f64,
|
||||
extension_continuity: ContinuityType,
|
||||
) -> Result<BSplineSurface> {
|
||||
self.create_extended_surface_dispatched(
|
||||
ext_direction,
|
||||
dist,
|
||||
extension_continuity,
|
||||
&|uv| is_singularity(k, self, uv),
|
||||
&|domain, dir| is_closed(k, self, domain, dir),
|
||||
)
|
||||
}
|
||||
|
||||
/// The body of [`BSplineSurface::create_extended_surface_dir`] with the virtual
|
||||
/// `IsSingularity(uv, dir)` and `IsClosed(domain, dir)` of the calling object
|
||||
/// passed in (an `SmPlane` overrides both), as upstream's virtual dispatch.
|
||||
pub(crate) fn create_extended_surface_dispatched(
|
||||
&self,
|
||||
ext_direction: SurfParamType,
|
||||
dist: f64,
|
||||
extension_continuity: ContinuityType,
|
||||
is_singularity: &dyn Fn(Point2) -> Result<(bool, SurfParamType)>,
|
||||
is_closed: &dyn Fn(&Extent2d, SurfParamType) -> Result<bool>,
|
||||
) -> Result<BSplineSurface> {
|
||||
const OP: &str = OP_EXTEND;
|
||||
|
||||
// copy input surface into local object
|
||||
let mut esur = surface_nurb(self, OP)?.clone();
|
||||
|
||||
let uv_domain = self.natural_uv_domain();
|
||||
let mut ext_dist = dist;
|
||||
|
||||
// pick continuity level
|
||||
let gflg = match extension_continuity {
|
||||
ContinuityType::G1R => SrfContinuity::G1R,
|
||||
ContinuityType::G1G2 => SrfContinuity::G2,
|
||||
ContinuityType::CInfinity => SrfContinuity::Cmax,
|
||||
_ => SrfContinuity::G1, // SM_CT_G1 and anything else
|
||||
};
|
||||
|
||||
// sort out where extension is needed
|
||||
let u_dir = matches!(
|
||||
ext_direction,
|
||||
SurfParamType::U | SurfParamType::UMin | SurfParamType::UMax
|
||||
);
|
||||
let v_dir = matches!(
|
||||
ext_direction,
|
||||
SurfParamType::V | SurfParamType::VMin | SurfParamType::VMax
|
||||
);
|
||||
let ext_u_or_v = if u_dir {
|
||||
SurfParamType::U
|
||||
} else if v_dir {
|
||||
SurfParamType::V
|
||||
} else {
|
||||
SurfParamType::Both
|
||||
};
|
||||
let neither = ext_direction == SurfParamType::Neither;
|
||||
let mut skip_umin = neither || v_dir || ext_direction == SurfParamType::UMax;
|
||||
let mut skip_vmin = neither || u_dir || ext_direction == SurfParamType::VMax;
|
||||
let mut skip_umax = neither || v_dir || ext_direction == SurfParamType::UMin;
|
||||
let mut skip_vmax = neither || u_dir || ext_direction == SurfParamType::VMin;
|
||||
|
||||
// Don't extend through singularities in U or V
|
||||
let (sing, sing_direction) = is_singularity(uv_domain.min())?;
|
||||
if sing {
|
||||
if sing_direction == SurfParamType::U {
|
||||
skip_vmin = true;
|
||||
} else {
|
||||
skip_umin = true;
|
||||
}
|
||||
}
|
||||
let (sing, sing_direction) = is_singularity(uv_domain.max())?;
|
||||
if sing {
|
||||
if sing_direction == SurfParamType::U {
|
||||
skip_vmax = true;
|
||||
} else {
|
||||
skip_umax = true;
|
||||
}
|
||||
}
|
||||
|
||||
// when asked and allowed - extend in U direction
|
||||
if (ext_u_or_v == SurfParamType::U || ext_u_or_v == SurfParamType::Both)
|
||||
&& !is_closed(&uv_domain, SurfParamType::U)?
|
||||
{
|
||||
// when extending in the UMin direction
|
||||
if !skip_umin {
|
||||
extend_side(
|
||||
&mut esur,
|
||||
dist,
|
||||
&mut ext_dist,
|
||||
Direction::U,
|
||||
CurveEnd::Start,
|
||||
gflg,
|
||||
)?;
|
||||
}
|
||||
|
||||
// reset the extension distance
|
||||
ext_dist = dist;
|
||||
|
||||
// when asked - extend in the UMax direction
|
||||
if !skip_umax {
|
||||
extend_side(
|
||||
&mut esur,
|
||||
dist,
|
||||
&mut ext_dist,
|
||||
Direction::U,
|
||||
CurveEnd::End,
|
||||
gflg,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
// when asked - extend in the V direction
|
||||
if (ext_u_or_v == SurfParamType::V || ext_u_or_v == SurfParamType::Both)
|
||||
&& !is_closed(&uv_domain, SurfParamType::V)?
|
||||
{
|
||||
// reset extension distance
|
||||
ext_dist = dist;
|
||||
|
||||
// when asked - extend in the V Min direction
|
||||
if !skip_vmin {
|
||||
extend_side(
|
||||
&mut esur,
|
||||
dist,
|
||||
&mut ext_dist,
|
||||
Direction::V,
|
||||
CurveEnd::Start,
|
||||
gflg,
|
||||
)?;
|
||||
}
|
||||
|
||||
// reset the extension distance
|
||||
ext_dist = dist;
|
||||
|
||||
// when asked - extend in the V Max direction
|
||||
if !skip_vmax {
|
||||
extend_side(
|
||||
&mut esur,
|
||||
dist,
|
||||
&mut ext_dist,
|
||||
Direction::V,
|
||||
CurveEnd::End,
|
||||
gflg,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
// build output SmBSplineSurface from esur SURFACE object
|
||||
if is_closed(&uv_domain, SurfParamType::U)? && is_closed(&uv_domain, SurfParamType::V)? {
|
||||
// surfaces closed in both directions - don't get extended - copy input
|
||||
Ok(self.copy())
|
||||
} else {
|
||||
Ok(Self::from_gw_nurb(&esur))
|
||||
}
|
||||
}
|
||||
|
||||
/// Joins the surface with `surface_to_join` along a common boundary
|
||||
/// (`JoinSurface`): with [`SurfParamType::U`] the v-maximum isocurve of `self`
|
||||
/// meets the v-minimum isocurve of `surface_to_join` (made compatible in v), with
|
||||
@@ -2135,69 +1896,6 @@ impl BSplineSurface {
|
||||
}
|
||||
}
|
||||
|
||||
/// One side of `CreateExtendedSurface`: extends `esur` across `end` of `dir` by
|
||||
/// `ext_dist`, halving the distance after each failure (`SM_ERR` below
|
||||
/// `dist / 100`), checks validity, then repeats with doubled distances while the
|
||||
/// distance is below `dist / 2` (at most [`MAX_EXTEND_DOUBLINGS`] times).
|
||||
fn extend_side(
|
||||
esur: &mut Surface,
|
||||
dist: f64,
|
||||
ext_dist: &mut f64,
|
||||
dir: Direction,
|
||||
end: CurveEnd,
|
||||
gflg: SrfContinuity,
|
||||
) -> Result<()> {
|
||||
const OP: &str = OP_EXTEND;
|
||||
|
||||
// extend Surf - shorter dists until extension succeeds (in place: a failed
|
||||
// extension can leave its extended surface in esur, which the retry extends)
|
||||
let mut halvings = 0usize;
|
||||
loop {
|
||||
match extend_by_dist_in_place(esur, *ext_dist, dir, end, gflg) {
|
||||
Ok(()) => break,
|
||||
Err(e @ (SolidError::Unported { .. } | SolidError::Cancelled { .. })) => return Err(e),
|
||||
Err(_) => {
|
||||
*ext_dist /= 2.0;
|
||||
if *ext_dist < dist / 100.0 {
|
||||
return Err(SolidError::sm_err(OP));
|
||||
}
|
||||
// upstream loops forever when dist is 0 or NaN
|
||||
halvings += 1;
|
||||
if halvings > MAX_EXTEND_HALVINGS {
|
||||
return Err(SolidError::NotConverged {
|
||||
operation: OP,
|
||||
iterations: u32::try_from(MAX_EXTEND_HALVINGS).unwrap_or(u32::MAX),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// quit - when extended surface is no good
|
||||
if esur.is_valid().is_err() {
|
||||
return Err(SolidError::sm_err(OP));
|
||||
}
|
||||
|
||||
// if needed - multiple extensions until Extension gets to be near requested size
|
||||
let mut doublings = 0usize;
|
||||
while *ext_dist < dist / 2.0 {
|
||||
// upstream loops forever for a negative distance (HANG-16)
|
||||
doublings += 1;
|
||||
if doublings > MAX_EXTEND_DOUBLINGS {
|
||||
return Err(SolidError::NotConverged {
|
||||
operation: OP,
|
||||
iterations: u32::try_from(MAX_EXTEND_DOUBLINGS).unwrap_or(u32::MAX),
|
||||
});
|
||||
}
|
||||
*ext_dist *= 2.0;
|
||||
extend_by_dist_in_place(esur, *ext_dist, dir, end, gflg).map_err(SolidError::nl_ser(OP))?;
|
||||
if esur.is_valid().is_err() {
|
||||
return Err(SolidError::sm_err(OP));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -2358,14 +2056,6 @@ mod tests {
|
||||
assert_eq!(s.natural_uv_domain().max().x, 2.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extended_surface_runs() {
|
||||
let k = &Kernel::default();
|
||||
let s = plane_surface();
|
||||
let r = s.create_extended_surface(k, 0.1, ContinuityType::G1);
|
||||
assert!(r.is_ok(), "{r:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_nurb() {
|
||||
let s = BSplineSurface::new();
|
||||
@@ -2378,18 +2068,6 @@ mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negative_extension_distance_stops() {
|
||||
// upstream doubles a negative distance towards -inf forever (HANG-16)
|
||||
let k = &Kernel::default();
|
||||
let r = plane_surface().create_extended_surface(k, -5.0, ContinuityType::G1R);
|
||||
assert!(matches!(r, Err(SolidError::NotConverged { .. })), "{r:?}");
|
||||
// a positive distance is unaffected
|
||||
assert!(plane_surface()
|
||||
.create_extended_surface(k, 0.5, ContinuityType::G1R)
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn make_knots_absurd_sizes_are_errors() {
|
||||
// fuzz regression: n + p + 2 overflowed
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
//! Surface extension by a distance, maximal second derivatives of an offset surface,
|
||||
//! evaluation outside the parameter domain (by extension or periodic wrap), skinning
|
||||
//! Maximal second derivatives of an offset surface, evaluation outside the parameter domain (by extension or periodic wrap), skinning
|
||||
//! along a spine curve, and cross-boundary derivative strips from a vector field.
|
||||
//!
|
||||
//! Upstream: `source/SMLib/src/NL_SrfAdv.cpp`, `source/SMLib/inc/NL_SrfAdv.h`.
|
||||
//!
|
||||
//! | Rust | Upstream |
|
||||
//! |-------------------------------------------------------|------------------------------------|
|
||||
//! | [`extend_by_dist`] | `N_SrfExtendByDist` |
|
||||
//! | [`offset_get_max_deriv`], [`MaxSecondDerivs`] | `N_SrfOffsetGetMaxDeriv` |
|
||||
//! | [`eval_pt_derivs_unbounded`], [`UnboundedExtension`] | `N_SrfEvalPtDerivsUnbounded` |
|
||||
//! | [`create_skin_spine`], [`SkinSpineOptions`], [`SpineAlignment`], [`SkinDerivs`] | `N_CreateSkinSpine` |
|
||||
@@ -68,7 +66,7 @@ use crate::nurbs::geom::{dist_cpt_cpt, dist_pt_pt, transform_matrix_from_axes, F
|
||||
use crate::nurbs::index::{at, at2, oob, put};
|
||||
use crate::nurbs::knots::{KnotVector, Side};
|
||||
use crate::nurbs::math::{CPoint, MODEL_TOL};
|
||||
use crate::nurbs::srf_adv::p2::{eval_pt_normal_deriv, extend_by_dist_at_depth, SrfContinuity};
|
||||
use crate::nurbs::srf_adv::p2::eval_pt_normal_deriv;
|
||||
use crate::nurbs::srf_adv::p3::{create_skin_srf, extend_by_param_dist, ExtendContinuity};
|
||||
use crate::nurbs::srf_adv::p4::create_b_vectors;
|
||||
use crate::nurbs::srf_basic::{Direction, Surface};
|
||||
@@ -88,27 +86,6 @@ const OP_UNB: &str = "nurbs::srf_adv::p1::eval_pt_derivs_unbounded";
|
||||
const OP_SPINE: &str = "nurbs::srf_adv::p1::create_skin_spine";
|
||||
const OP_VFIELD: &str = "nurbs::srf_adv::p1::cross_boundary_derivs_vector_field";
|
||||
|
||||
// ---- N_SrfExtendByDist ----------------------------------------------------------------
|
||||
|
||||
/// Extends `sur` across the boundary `end` of direction `dir` by approximately the
|
||||
/// distance `dist` (upstream `N_SrfExtendByDist`), with the continuity `cont` at the
|
||||
/// old boundary. This is the entry point of the recursive
|
||||
/// [`extend_by_dist_at_depth`] at depth `0`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// The errors of [`extend_by_dist_at_depth`].
|
||||
pub fn extend_by_dist(
|
||||
sur: &Surface,
|
||||
dist: f64,
|
||||
dir: Direction,
|
||||
end: CurveEnd,
|
||||
cont: SrfContinuity,
|
||||
) -> Result<Surface> {
|
||||
// Pass the call along to the recursive function.
|
||||
extend_by_dist_at_depth(sur, dist, dir, end, cont, 0)
|
||||
}
|
||||
|
||||
// ---- N_SrfOffsetGetMaxDeriv -----------------------------------------------------------
|
||||
|
||||
/// Bounds of the second derivatives of the offset of a surface (upstream `Muu`,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,10 @@
|
||||
//! A surface extended past one side of its parameter box by a distance measured across that
|
||||
//! side, exactly: [`Surface::extended_by_distance`].
|
||||
//!
|
||||
//! Not upstream: the kernel's own extension (`N_SrfExtendByDist`, `srf_adv::p2`) estimates
|
||||
//! Not upstream: upstream's extension (`CreateExtendedSurface` on `N_SrfExtendByDist`) estimates
|
||||
//! its distance, fits a rational curvature-continuous extension by least squares and ends by
|
||||
//! removing knots within a tolerance, which can move the source's own part. This is a separate
|
||||
//! exact path; that one and its callers are unchanged.
|
||||
//! removing knots within a tolerance, which can move the source's own part. It is not ported;
|
||||
//! every surface extension in the kernel is this exact one.
|
||||
//!
|
||||
//! **The construction.** The surface's B-spline form (an analytic kind's only once proved on
|
||||
//! its label, [`AnalyticSupport::check`]) is worked on its homogeneous net `(H, w)`, one row of
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
//! callers that extend a surface "on every side" take.
|
||||
//!
|
||||
//! Not upstream: upstream's `CreateExtendedSurface` extends every side by an estimated
|
||||
//! distance, fits its extension by least squares and removes knots within a tolerance; this
|
||||
//! is the exact path side by side.
|
||||
//! distance, fits its extension by least squares and removes knots within a tolerance; it is
|
||||
//! not ported, this exact path replaces it.
|
||||
//!
|
||||
//! Each asked side, in [`BoxSide::ALL`]'s order, is extended on the box the sides before it
|
||||
//! left (the result's natural box), so a later side runs along the earlier extensions. A side
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
//! | [`OffsetSurface`], [`OffsetSurface::new`] | `SmOffsetSurface`, `SmOffsetSurface(d, rSurface, bOwnsSurface, lSingularities)`, `~SmOffsetSurface` (`Drop`) |
|
||||
//! | [`OffsetSurface::copy`] | `Copy` |
|
||||
//! | [`OffsetSurface::approx_eq`] | `operator==` |
|
||||
//! | [`OffsetSurface::create_extended_surface`], [`OffsetSurface::create_extended_surface_dir`] | `CreateExtendedSurface` (both) |
|
||||
//! | [`OffsetSurface::calculate_bounding_box`] | `CalculateBoundingBox` |
|
||||
//! | `extend_surface` (private) | `sm_ExtendSurface` (static) |
|
||||
//! | `extend_as_itself` (private) | the fork's `sm_ExtendAsItself` (static; WR-633, WR-650) |
|
||||
@@ -56,7 +55,7 @@
|
||||
//! - **Context.** Every surface has a context here, so the clamp-instead-of-extend
|
||||
//! branch for `GetContext() == NULL` is never taken; `SetContext` has nothing to set.
|
||||
//! The constructor and every member that constructs an offset ([`OffsetSurface::copy`],
|
||||
//! [`OffsetSurface::split_at`], [`OffsetSurface::create_extended_surface_dir`],
|
||||
//! [`OffsetSurface::split_at`],
|
||||
//! [`OffsetSurface::create_offset_surface`], [`OffsetSurface::read_from_db`]) take the
|
||||
//! kernel for the base's `GetSingularities`.
|
||||
//! - **Extension cache.** Upstream's `const` evaluators (`EvaluatePoint`, `Evaluate`,
|
||||
@@ -118,7 +117,6 @@ use crate::geom::axis2_placement::Axis2Placement;
|
||||
use crate::geom::extent1d::Extent1d;
|
||||
use crate::geom::extent2d::{Extent2d, SS_UMAX, SS_UMIN, SS_VMAX, SS_VMIN};
|
||||
use crate::geom::extent3d::Extent3d;
|
||||
use crate::geom::geom_utility::p2::ContinuityType;
|
||||
use crate::geom::object::ObjectRef;
|
||||
use crate::geom::polar_box::{PolarBox, PolarBoxState};
|
||||
use crate::geom::pseudo_box::PseudoBox;
|
||||
@@ -776,52 +774,6 @@ impl OffsetSurface {
|
||||
|
||||
// ---- new surfaces ---------------------------------------------------------------------
|
||||
|
||||
/// An offset of the base surface extended by `dist` on every side
|
||||
/// (`CreateExtendedSurface(ctx, dist, continuity, ..)`: the directional overload
|
||||
/// with `SM_SP_BOTH`).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Those of [`OffsetSurface::create_extended_surface_dir`].
|
||||
pub fn create_extended_surface(
|
||||
&self,
|
||||
k: &Kernel,
|
||||
dist: f64,
|
||||
extension_continuity: ContinuityType,
|
||||
) -> Result<Surface> {
|
||||
// pass the call along
|
||||
self.create_extended_surface_dir(k, SurfParamType::Both, dist, extension_continuity)
|
||||
}
|
||||
|
||||
/// An offset (same distance, owning, default singularities) of the base surface
|
||||
/// extended by `dist` in `ext_direction` (`CreateExtendedSurface` with a direction).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Those of the base surface's [`Surface::create_extended_surface_dir`] and of
|
||||
/// [`OffsetSurface::new`].
|
||||
pub fn create_extended_surface_dir(
|
||||
&self,
|
||||
k: &Kernel,
|
||||
ext_direction: SurfParamType,
|
||||
dist: f64,
|
||||
extension_continuity: ContinuityType,
|
||||
) -> Result<Surface> {
|
||||
let surface = self.surface.create_extended_surface_dir(
|
||||
k,
|
||||
ext_direction,
|
||||
dist,
|
||||
extension_continuity,
|
||||
)?;
|
||||
Ok(Surface::from(Self::new(
|
||||
k,
|
||||
self.offset_distance.get(),
|
||||
surface,
|
||||
true,
|
||||
SS_UNKNOWN,
|
||||
)?))
|
||||
}
|
||||
|
||||
/// The fork's `SmOffsetSurface::ContinuesAsItself` (tangency-labs/smlib@db4a655):
|
||||
/// whether `surface` continues as itself past its domain ([`Self::continue_as_itself`]):
|
||||
/// a cylinder, cone, sphere or torus.
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
//! | [`Plane::is_kind_of`], [`Plane::TYPE`], [`Plane::type_string`], [`Plane::class_string`] | `IsKindOf`, `GetType`, `GetTypeString`, `GetClassString` (`SM_COMMON`) |
|
||||
//! | [`Plane::base`], [`Plane::base_with_nurb`], [`Plane::natural_uv_domain`] | the `SmBSplineSurface` part, `GetOrCreateGwNurbPointer`, `GetNaturalUVDomain` (inherited) |
|
||||
//! | [`Plane::max_analytic_domain`], [`Plane::is_parallel_to_vector`], [`Plane::is_parallel_to_vector_opt_in`], [`Plane::is_parallel_to_vector_with_kernel`], [`Plane::reverse`], [`Plane::swap_uv`] | `SmBSplineSurface::GetMaxAnalyticDomain`, `IsParallelToVector`, `Reverse`, `SwapUV` (inherited, re-dispatched) |
|
||||
//! | [`Plane::create_extended_surface`], [`Plane::create_extended_surface_dir`] | `SmBSplineSurface::CreateExtendedSurface` (both overloads; inherited, re-dispatched to the `IsSingularity` / `IsClosed` overrides) |
|
||||
//!
|
||||
//! Deviations:
|
||||
//! - **`SwapUV` keeps the plane's definition (WR-701, opt-in).** With
|
||||
@@ -548,46 +547,6 @@ impl Plane {
|
||||
(false, SurfParamType::Neither)
|
||||
}
|
||||
|
||||
/// `SmBSplineSurface::CreateExtendedSurface(dDist, ..)` (inherited): the plane's
|
||||
/// NURB extended by `dist` across each side, through the `SmPlane` overrides of
|
||||
/// `IsSingularity` and `IsClosed` (none, never closed).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Those of [`Plane::create_extended_surface_dir`].
|
||||
pub fn create_extended_surface(
|
||||
&self,
|
||||
dist: f64,
|
||||
extension_continuity: ContinuityType,
|
||||
) -> Result<BSplineSurface> {
|
||||
// pass the call along
|
||||
self.create_extended_surface_dir(SurfParamType::Both, dist, extension_continuity)
|
||||
}
|
||||
|
||||
/// `SmBSplineSurface::CreateExtendedSurface(eExtDirection, dDist, ..)` (inherited):
|
||||
/// [`BSplineSurface::create_extended_surface_dir`] of the plane's NURB, with the
|
||||
/// virtual `IsSingularity` and `IsClosed` dispatched to the `SmPlane` overrides
|
||||
/// ([`Plane::is_singularity`], [`Plane::is_closed`]) as upstream.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// `SM_ERR` when no NURB can be made (upstream copies a `NULL` NURB) and the
|
||||
/// errors of the extension.
|
||||
pub fn create_extended_surface_dir(
|
||||
&self,
|
||||
ext_direction: SurfParamType,
|
||||
dist: f64,
|
||||
extension_continuity: ContinuityType,
|
||||
) -> Result<BSplineSurface> {
|
||||
self.base_for_query().create_extended_surface_dispatched(
|
||||
ext_direction,
|
||||
dist,
|
||||
extension_continuity,
|
||||
&|_| Ok(self.is_singularity()),
|
||||
&|_, _| Ok(self.is_closed().0),
|
||||
)
|
||||
}
|
||||
|
||||
/// `SmBSplineSurface::GetMaxAnalyticDomain` (inherited): planar, so the natural
|
||||
/// domain expanded relatively by 100.
|
||||
#[must_use]
|
||||
@@ -2765,25 +2724,6 @@ mod tests {
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_uses_plane_overrides() {
|
||||
// the B-spline IsSingularity / IsClosed are unported; the plane's are not
|
||||
let p = Plane::new(
|
||||
Point3::ZERO,
|
||||
Vec3::new(1.0, 0.0, 0.0),
|
||||
Vec3::new(0.0, 1.0, 0.0),
|
||||
Vec2::new(1.0, 1.0),
|
||||
&Extent2d::from_bounds(0.0, 0.0, 1.0, 1.0),
|
||||
);
|
||||
let e = p.create_extended_surface(0.5, ContinuityType::G1).unwrap();
|
||||
let d = e.natural_uv_domain();
|
||||
assert!(d.max().x - d.min().x > 1.0);
|
||||
let e = p
|
||||
.create_extended_surface_dir(SurfParamType::UMax, 0.5, ContinuityType::G1)
|
||||
.unwrap();
|
||||
assert!(e.gw_nurb().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wr41_plane_reparam_default_keeps_origin() {
|
||||
let mut p = Plane::new(
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
//! | [`Surface::owner`] | `GetOwner` |
|
||||
//! | [`Surface::copy`], [`Surface::copy_and_add_analytics`] | `Copy`, `CopyAndAddAnalytics` |
|
||||
//! | [`Surface::adjust_step_uv_domain`] | `AdjustSTEPUVDomain` |
|
||||
//! | [`Surface::create_extended_surface`], [`Surface::create_extended_surface_dir`] | `CreateExtendedSurface` (both) |
|
||||
//! | [`Surface::drop_point_fast`] | `DropPointFast` |
|
||||
//! | [`Surface::evaluate`], [`Surface::evaluate_point`] | `Evaluate`, `EvaluatePoint` |
|
||||
//! | [`Surface::natural_uv_domain_if_built`], [`Surface::step_uv_domain`] | `GetNaturalUVDomain`, `GetSTEPUVDomain` |
|
||||
@@ -3793,257 +3792,15 @@ impl Surface {
|
||||
}
|
||||
}
|
||||
|
||||
/// The surface extended by `dist` on every side (`CreateExtendedSurface`; the
|
||||
/// kernel is the context the new surface is constructed in).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Those of the kind's override (the base default is `SM_ERR`).
|
||||
pub fn create_extended_surface(
|
||||
&self,
|
||||
k: &Kernel,
|
||||
dist: f64,
|
||||
extension_continuity: ContinuityType,
|
||||
) -> Result<Self> {
|
||||
let made = self.create_extended_surface_of_kind(k, dist, extension_continuity)?;
|
||||
Ok(self.carry_exact_round_on_extension(made, extension_continuity))
|
||||
}
|
||||
|
||||
/// An exact round's definition carried onto `extension`, this surface extended by the
|
||||
/// same NURBS pieces continued (`CInfinity`, #2487): the analytic class continued, still
|
||||
/// on S. Another continuity (a ruled `G1R` extension) leaves S, so it carries none, and
|
||||
/// so does a corner's face (#2488), whose net continued leaves its patch's definition.
|
||||
fn carry_exact_round_on_extension(
|
||||
&self,
|
||||
extension: Self,
|
||||
extension_continuity: ContinuityType,
|
||||
) -> Self {
|
||||
let round = matches!(self.definition.as_deref(), Some(KeptDefinition::Round(_)));
|
||||
if round && extension_continuity == ContinuityType::CInfinity {
|
||||
self.carry_definition(extension)
|
||||
} else {
|
||||
extension
|
||||
}
|
||||
}
|
||||
|
||||
/// An exact round's definition carried onto `continuation`, this surface's own NURBS
|
||||
/// net continued Natural (`surface::extend`'s analytic kinds): still on S, as
|
||||
/// [`Surface::create_extended_surface`]'s `CInfinity` carries it (#2487).
|
||||
/// net continued Natural (`surface::extend`'s analytic kinds): the analytic class
|
||||
/// continued, still on S (#2487). A corner's face (#2488), whose net continued leaves its
|
||||
/// patch's definition, carries none.
|
||||
pub(crate) fn carry_round_on_continuation(&self, continuation: Self) -> Self {
|
||||
self.carry_exact_round_on_extension(continuation, ContinuityType::CInfinity)
|
||||
}
|
||||
|
||||
/// [`Surface::create_extended_surface`] of the kind alone.
|
||||
fn create_extended_surface_of_kind(
|
||||
&self,
|
||||
k: &Kernel,
|
||||
dist: f64,
|
||||
extension_continuity: ContinuityType,
|
||||
) -> Result<Self> {
|
||||
match self.kind_with_nurb(k)? {
|
||||
SurfaceKind::BSpline(s) => Ok(Self::from(s.create_extended_surface(
|
||||
k,
|
||||
dist,
|
||||
extension_continuity,
|
||||
)?)),
|
||||
// (the fork's SmBallPatch inherits SmBSplineSurface's: the net extended, a
|
||||
// plain B-spline)
|
||||
SurfaceKind::BallPatch(s) => Ok(Self::from(s.net().create_extended_surface(
|
||||
k,
|
||||
dist,
|
||||
extension_continuity,
|
||||
)?)),
|
||||
// SmBSplineSurface's: the net extended, its singularity and closure tests
|
||||
// (virtual, on the blend) taking the definition's points
|
||||
SurfaceKind::ProceduralBlend(pb) => {
|
||||
Ok(Self::from(pb.net().create_extended_surface_dispatched(
|
||||
SurfParamType::Both,
|
||||
dist,
|
||||
extension_continuity,
|
||||
&|uv| {
|
||||
self.is_singularity(k, uv, EFF_ZERO, false)
|
||||
.map(|r| (r.singular, r.direction))
|
||||
},
|
||||
&|domain, param| self.is_closed(k, domain, param, None, false).map(|r| r.0),
|
||||
)?))
|
||||
}
|
||||
SurfaceKind::CurveBounded(_) | SurfaceKind::Step(_) | SurfaceKind::SrfInVolume(_) => {
|
||||
base_create_extended_surface()
|
||||
}
|
||||
SurfaceKind::Plane(s) => Ok(Self::from(
|
||||
s.create_extended_surface(dist, extension_continuity)?,
|
||||
)),
|
||||
SurfaceKind::Sphere(s) => Ok(Self::from(
|
||||
s.base().base().create_extended_surface_dispatched(
|
||||
SurfParamType::Both,
|
||||
dist,
|
||||
extension_continuity,
|
||||
&|uv| {
|
||||
self.is_singularity(k, uv, EFF_ZERO, false)
|
||||
.map(|r| (r.singular, r.direction))
|
||||
},
|
||||
&|domain, param| self.is_closed(k, domain, param, None, false).map(|r| r.0),
|
||||
)?,
|
||||
)),
|
||||
SurfaceKind::Torus(s) => Ok(Self::from(
|
||||
s.base().base().create_extended_surface_dispatched(
|
||||
SurfParamType::Both,
|
||||
dist,
|
||||
extension_continuity,
|
||||
&|uv| {
|
||||
self.is_singularity(k, uv, EFF_ZERO, false)
|
||||
.map(|r| (r.singular, r.direction))
|
||||
},
|
||||
&|domain, param| self.is_closed(k, domain, param, None, false).map(|r| r.0),
|
||||
)?,
|
||||
)),
|
||||
SurfaceKind::Cone(s) => Ok(Self::from(
|
||||
s.rev().base().create_extended_surface_dispatched(
|
||||
SurfParamType::Both,
|
||||
dist,
|
||||
extension_continuity,
|
||||
&|uv| {
|
||||
self.is_singularity(k, uv, EFF_ZERO, false)
|
||||
.map(|r| (r.singular, r.direction))
|
||||
},
|
||||
&|domain, param| self.is_closed(k, domain, param, None, false).map(|r| r.0),
|
||||
)?,
|
||||
)),
|
||||
SurfaceKind::OffsetSurface(s) => {
|
||||
s.create_extended_surface(k, dist, extension_continuity)
|
||||
}
|
||||
SurfaceKind::SurfOfRevolution(s) => Ok(Self::from(s.base().create_extended_surface(
|
||||
k,
|
||||
dist,
|
||||
extension_continuity,
|
||||
)?)),
|
||||
// (`GetOrCreateGwNurbPointer` makes a recognised extrusion's NURB)
|
||||
SurfaceKind::SurfOfExtrusion(s) => Ok(Self::from(
|
||||
s.base_with_nurb(k)?
|
||||
.create_extended_surface(k, dist, extension_continuity)?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// The surface extended by `dist` in `ext_direction` (`CreateExtendedSurface`
|
||||
/// with a direction; the kernel is the context the new surface is constructed in).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Those of the kind's override (the base default is `SM_ERR`).
|
||||
pub fn create_extended_surface_dir(
|
||||
&self,
|
||||
k: &Kernel,
|
||||
ext_direction: SurfParamType,
|
||||
dist: f64,
|
||||
extension_continuity: ContinuityType,
|
||||
) -> Result<Self> {
|
||||
crate::profile::profile_scope!(k, "surface::create_extended_surface_dir");
|
||||
let made =
|
||||
self.create_extended_surface_dir_of_kind(k, ext_direction, dist, extension_continuity)?;
|
||||
Ok(self.carry_exact_round_on_extension(made, extension_continuity))
|
||||
}
|
||||
|
||||
/// [`Surface::create_extended_surface_dir`] of the kind alone.
|
||||
fn create_extended_surface_dir_of_kind(
|
||||
&self,
|
||||
k: &Kernel,
|
||||
ext_direction: SurfParamType,
|
||||
dist: f64,
|
||||
extension_continuity: ContinuityType,
|
||||
) -> Result<Self> {
|
||||
match self.kind_with_nurb(k)? {
|
||||
SurfaceKind::BSpline(s) => Ok(Self::from(s.create_extended_surface_dir(
|
||||
k,
|
||||
ext_direction,
|
||||
dist,
|
||||
extension_continuity,
|
||||
)?)),
|
||||
// (the fork's SmBallPatch inherits SmBSplineSurface's: the net extended)
|
||||
SurfaceKind::BallPatch(s) => Ok(Self::from(s.net().create_extended_surface_dir(
|
||||
k,
|
||||
ext_direction,
|
||||
dist,
|
||||
extension_continuity,
|
||||
)?)),
|
||||
// SmBSplineSurface's: the net extended, its singularity and closure tests
|
||||
// (virtual, on the blend) taking the definition's points
|
||||
SurfaceKind::ProceduralBlend(pb) => {
|
||||
Ok(Self::from(pb.net().create_extended_surface_dispatched(
|
||||
ext_direction,
|
||||
dist,
|
||||
extension_continuity,
|
||||
&|uv| {
|
||||
self.is_singularity(k, uv, EFF_ZERO, false)
|
||||
.map(|r| (r.singular, r.direction))
|
||||
},
|
||||
&|domain, param| self.is_closed(k, domain, param, None, false).map(|r| r.0),
|
||||
)?))
|
||||
}
|
||||
SurfaceKind::CurveBounded(_) | SurfaceKind::Step(_) | SurfaceKind::SrfInVolume(_) => {
|
||||
base_create_extended_surface()
|
||||
}
|
||||
SurfaceKind::Plane(s) => Ok(Self::from(s.create_extended_surface_dir(
|
||||
ext_direction,
|
||||
dist,
|
||||
extension_continuity,
|
||||
)?)),
|
||||
SurfaceKind::Sphere(s) => Ok(Self::from(
|
||||
s.base().base().create_extended_surface_dispatched(
|
||||
ext_direction,
|
||||
dist,
|
||||
extension_continuity,
|
||||
&|uv| {
|
||||
self.is_singularity(k, uv, EFF_ZERO, false)
|
||||
.map(|r| (r.singular, r.direction))
|
||||
},
|
||||
&|domain, param| self.is_closed(k, domain, param, None, false).map(|r| r.0),
|
||||
)?,
|
||||
)),
|
||||
SurfaceKind::Torus(s) => Ok(Self::from(
|
||||
s.base().base().create_extended_surface_dispatched(
|
||||
ext_direction,
|
||||
dist,
|
||||
extension_continuity,
|
||||
&|uv| {
|
||||
self.is_singularity(k, uv, EFF_ZERO, false)
|
||||
.map(|r| (r.singular, r.direction))
|
||||
},
|
||||
&|domain, param| self.is_closed(k, domain, param, None, false).map(|r| r.0),
|
||||
)?,
|
||||
)),
|
||||
SurfaceKind::Cone(s) => Ok(Self::from(
|
||||
s.rev().base().create_extended_surface_dispatched(
|
||||
ext_direction,
|
||||
dist,
|
||||
extension_continuity,
|
||||
&|uv| {
|
||||
self.is_singularity(k, uv, EFF_ZERO, false)
|
||||
.map(|r| (r.singular, r.direction))
|
||||
},
|
||||
&|domain, param| self.is_closed(k, domain, param, None, false).map(|r| r.0),
|
||||
)?,
|
||||
)),
|
||||
SurfaceKind::OffsetSurface(s) => {
|
||||
s.create_extended_surface_dir(k, ext_direction, dist, extension_continuity)
|
||||
}
|
||||
SurfaceKind::SurfOfRevolution(s) => {
|
||||
Ok(Self::from(s.base().create_extended_surface_dir(
|
||||
k,
|
||||
ext_direction,
|
||||
dist,
|
||||
extension_continuity,
|
||||
)?))
|
||||
}
|
||||
// (`GetOrCreateGwNurbPointer` makes a recognised extrusion's NURB)
|
||||
SurfaceKind::SurfOfExtrusion(s) => Ok(Self::from(
|
||||
s.base_with_nurb(k)?.create_extended_surface_dir(
|
||||
k,
|
||||
ext_direction,
|
||||
dist,
|
||||
extension_continuity,
|
||||
)?,
|
||||
)),
|
||||
if matches!(self.definition.as_deref(), Some(KeptDefinition::Round(_))) {
|
||||
self.carry_definition(continuation)
|
||||
} else {
|
||||
continuation
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5150,11 +4907,6 @@ pub const fn base_adjust_step_uv_domain() -> Result<()> {
|
||||
Err(base_err())
|
||||
}
|
||||
|
||||
/// `SmSurface::CreateExtendedSurface` (both overloads): `SM_ERR`.
|
||||
pub const fn base_create_extended_surface() -> Result<Surface> {
|
||||
Err(base_err())
|
||||
}
|
||||
|
||||
/// `SmSurface::DropPointFast`: `SM_ERR`.
|
||||
pub const fn base_drop_point_fast<O>() -> Result<SolutionArray<O>> {
|
||||
Err(base_err())
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -982,203 +982,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "surf1_extend_cinfinity",
|
||||
"upstream_ref": "tests/prog_test/src/smSurfaceTest.cpp:4627 my_test_surface_interface (surface: the surf1 points as control net; ApproximatePoints belongs to another unit) (CreateExtendedSurface 0.5 SM_CT_CINFINITY)",
|
||||
"inputs": {
|
||||
"this": {
|
||||
"srf": {
|
||||
"net": [
|
||||
[
|
||||
[
|
||||
0,
|
||||
0,
|
||||
-0.6,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
1,
|
||||
0.1,
|
||||
0.4,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
2,
|
||||
0.2,
|
||||
0.3,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
3,
|
||||
0.3,
|
||||
0,
|
||||
-1.7976931348623157e+308
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
0,
|
||||
1,
|
||||
0.2,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
1,
|
||||
1,
|
||||
1.5,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
2,
|
||||
1,
|
||||
1.6,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
3,
|
||||
1,
|
||||
0.2,
|
||||
-1.7976931348623157e+308
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0.3,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
1,
|
||||
2,
|
||||
1.6,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
2,
|
||||
2,
|
||||
1.5,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
3,
|
||||
2,
|
||||
0.4,
|
||||
-1.7976931348623157e+308
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
0,
|
||||
3,
|
||||
0.4,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
1,
|
||||
3,
|
||||
1.2,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
2,
|
||||
3,
|
||||
1.1,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
3,
|
||||
3,
|
||||
0.7,
|
||||
-1.7976931348623157e+308
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
0,
|
||||
4,
|
||||
0.4,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
1,
|
||||
4,
|
||||
-1.2,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
2,
|
||||
4,
|
||||
-1.1,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
3,
|
||||
4,
|
||||
0.5,
|
||||
-1.7976931348623157e+308
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
0,
|
||||
5,
|
||||
0.4,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
1,
|
||||
5,
|
||||
0.2,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
2,
|
||||
5,
|
||||
0.1,
|
||||
-1.7976931348623157e+308
|
||||
],
|
||||
[
|
||||
3,
|
||||
5,
|
||||
0.5,
|
||||
-1.7976931348623157e+308
|
||||
]
|
||||
]
|
||||
],
|
||||
"p": 3,
|
||||
"q": 3,
|
||||
"u": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.3333333333333333,
|
||||
0.6666666666666666,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"v": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
]
|
||||
}
|
||||
},
|
||||
"op": "extend",
|
||||
"dir_overload": false,
|
||||
"dir": 3,
|
||||
"dist": 0.5,
|
||||
"cont": 13
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sweep_translational",
|
||||
"upstream_ref": "tests/prog_test/src/smSurfaceTest.cpp:5715 my_test_bsplinesurface_nlib (CreateSweepSurface, translational)",
|
||||
|
||||
@@ -1,827 +0,0 @@
|
||||
{
|
||||
"cases": [
|
||||
{
|
||||
"name": "extend_unit",
|
||||
"inputs": {
|
||||
"make": {
|
||||
"kind": "bounded",
|
||||
"o": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"x": [
|
||||
1,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"y": [
|
||||
0,
|
||||
1,
|
||||
0
|
||||
],
|
||||
"scale": [
|
||||
1,
|
||||
1
|
||||
],
|
||||
"domain": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
"ops": [
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 4
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 5
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 13
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 2
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 0,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 1,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 2,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 3,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 4,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 5,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 6,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 7,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 8,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 3,
|
||||
"dist": 10.0,
|
||||
"cont": 4
|
||||
},
|
||||
{
|
||||
"op": "state"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "extend_scaled",
|
||||
"inputs": {
|
||||
"make": {
|
||||
"kind": "bounded",
|
||||
"o": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"x": [
|
||||
0.6,
|
||||
0.8,
|
||||
0
|
||||
],
|
||||
"y": [
|
||||
-0.8,
|
||||
0.6,
|
||||
0
|
||||
],
|
||||
"scale": [
|
||||
2,
|
||||
3
|
||||
],
|
||||
"domain": [
|
||||
-1,
|
||||
-2,
|
||||
3,
|
||||
4
|
||||
]
|
||||
},
|
||||
"ops": [
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 4
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 5
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 13
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 2
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 0,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 1,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 2,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 3,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 4,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 5,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 6,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 7,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 8,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 3,
|
||||
"dist": 10.0,
|
||||
"cont": 4
|
||||
},
|
||||
{
|
||||
"op": "state"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "extend_infinite",
|
||||
"inputs": {
|
||||
"make": {
|
||||
"kind": "normal",
|
||||
"o": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"n": [
|
||||
0,
|
||||
0,
|
||||
1
|
||||
]
|
||||
},
|
||||
"ops": [
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 4
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 5
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 13
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 2
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 0,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 1,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 2,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 3,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 4,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 5,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 6,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 7,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 8,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 3,
|
||||
"dist": 10.0,
|
||||
"cont": 4
|
||||
},
|
||||
{
|
||||
"op": "state"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "extend_canonical",
|
||||
"inputs": {
|
||||
"make": {
|
||||
"kind": "canonical",
|
||||
"o": [
|
||||
0,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"x": [
|
||||
1,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"y": [
|
||||
0,
|
||||
0,
|
||||
1
|
||||
]
|
||||
},
|
||||
"ops": [
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 4
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 5
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 13
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 2
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 0,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 1,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 2,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 3,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 4,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 5,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 6,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 7,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 8,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 3,
|
||||
"dist": 10.0,
|
||||
"cont": 4
|
||||
},
|
||||
{
|
||||
"op": "state"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "extend_degen",
|
||||
"inputs": {
|
||||
"make": {
|
||||
"kind": "bounded",
|
||||
"o": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"x": [
|
||||
1,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"y": [
|
||||
0,
|
||||
1,
|
||||
0
|
||||
],
|
||||
"scale": [
|
||||
1,
|
||||
1
|
||||
],
|
||||
"domain": [
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
"ops": [
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 4
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 5
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 13
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 2
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 0,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 1,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 2,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 3,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 4,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 5,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 6,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 7,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 8,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 3,
|
||||
"dist": 10.0,
|
||||
"cont": 4
|
||||
},
|
||||
{
|
||||
"op": "state"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "extend_inverted",
|
||||
"inputs": {
|
||||
"make": {
|
||||
"kind": "bounded",
|
||||
"o": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"x": [
|
||||
1,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"y": [
|
||||
0,
|
||||
1,
|
||||
0
|
||||
],
|
||||
"scale": [
|
||||
1,
|
||||
1
|
||||
],
|
||||
"domain": [
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"ops": [
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 4
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 5
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 13
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.25,
|
||||
"cont": 2
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 0,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 1,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 2,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 3,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 4,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 5,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 6,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 7,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 8,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 3,
|
||||
"dist": 10.0,
|
||||
"cont": 4
|
||||
},
|
||||
{
|
||||
"op": "state"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "extend_after_toggle",
|
||||
"inputs": {
|
||||
"make": {
|
||||
"kind": "bounded",
|
||||
"o": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"x": [
|
||||
1,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"y": [
|
||||
0,
|
||||
1,
|
||||
0
|
||||
],
|
||||
"scale": [
|
||||
2,
|
||||
3
|
||||
],
|
||||
"domain": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
2
|
||||
]
|
||||
},
|
||||
"ops": [
|
||||
{
|
||||
"op": "make_nurb"
|
||||
},
|
||||
{
|
||||
"op": "toggle"
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 5,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.5,
|
||||
"cont": 5
|
||||
},
|
||||
{
|
||||
"op": "state"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "extend_after_swap",
|
||||
"inputs": {
|
||||
"make": {
|
||||
"kind": "bounded",
|
||||
"o": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"x": [
|
||||
1,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"y": [
|
||||
0,
|
||||
1,
|
||||
0
|
||||
],
|
||||
"scale": [
|
||||
2,
|
||||
3
|
||||
],
|
||||
"domain": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
2
|
||||
]
|
||||
},
|
||||
"ops": [
|
||||
{
|
||||
"op": "swap"
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dir": 7,
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "reverse"
|
||||
},
|
||||
{
|
||||
"op": "extend",
|
||||
"dist": 0.5,
|
||||
"cont": 3
|
||||
},
|
||||
{
|
||||
"op": "state"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -239,24 +239,6 @@
|
||||
"dir": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Surface_extend",
|
||||
"upstream_ref": "prog_test smSurfaceTest.cpp:4117 (my_test_surface_closest_point, my_test_drop_point)",
|
||||
"inputs": {
|
||||
"this": {
|
||||
"oob": false,
|
||||
"form": 11,
|
||||
"srf": {
|
||||
"sms": {
|
||||
"$upstream_file": "Surface.sms"
|
||||
}
|
||||
}
|
||||
},
|
||||
"op": "extend",
|
||||
"dist": 0.1,
|
||||
"cont": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Surface_seval",
|
||||
"upstream_ref": "prog_test smSurfaceTest.cpp:4117 (my_test_surface_closest_point, my_test_drop_point)",
|
||||
@@ -569,24 +551,6 @@
|
||||
"dir": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "DropCurveSurface_extend",
|
||||
"upstream_ref": "pt_TestFiles/DropCurveSurface.sms (prog_test drop-curve surface input)",
|
||||
"inputs": {
|
||||
"this": {
|
||||
"oob": false,
|
||||
"form": 11,
|
||||
"srf": {
|
||||
"sms": {
|
||||
"$upstream_file": "DropCurveSurface.sms"
|
||||
}
|
||||
}
|
||||
},
|
||||
"op": "extend",
|
||||
"dist": 0.1,
|
||||
"cont": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "DropCurveSurface_seval",
|
||||
"upstream_ref": "pt_TestFiles/DropCurveSurface.sms (prog_test drop-curve surface input)",
|
||||
@@ -899,24 +863,6 @@
|
||||
"dir": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "offset_nurb_extend",
|
||||
"upstream_ref": "prog_test smFilletTest.cpp:3769 (offset_nurb.sms)",
|
||||
"inputs": {
|
||||
"this": {
|
||||
"oob": false,
|
||||
"form": 11,
|
||||
"srf": {
|
||||
"sms": {
|
||||
"$upstream_file": "offset_nurb.sms"
|
||||
}
|
||||
}
|
||||
},
|
||||
"op": "extend",
|
||||
"dist": 0.1,
|
||||
"cont": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "offset_nurb_seval",
|
||||
"upstream_ref": "prog_test smFilletTest.cpp:3769 (offset_nurb.sms)",
|
||||
@@ -1191,31 +1137,6 @@
|
||||
"op": "max_anal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "base_extend",
|
||||
"upstream_ref": "SmSurface.h inline base-class defaults",
|
||||
"inputs": {
|
||||
"this": {
|
||||
"base": true
|
||||
},
|
||||
"op": "extend",
|
||||
"dist": 0.1,
|
||||
"cont": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "base_extend_dir",
|
||||
"upstream_ref": "SmSurface.h inline base-class defaults",
|
||||
"inputs": {
|
||||
"this": {
|
||||
"base": true
|
||||
},
|
||||
"op": "extend_dir",
|
||||
"dir": 0,
|
||||
"dist": 0.1,
|
||||
"cont": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "base_adjust_step",
|
||||
"upstream_ref": "SmSurface.h inline base-class defaults",
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// difftest shim for unit `nlib.srf_adv_p1`.
|
||||
// Wraps N_SrfExtendByDist, N_SrfOffsetGetMaxDeriv, N_SrfEvalPtDerivsUnbounded,
|
||||
// N_CreateSkinSpine and N_CrossBoundaryDerivsVectorField (upstream
|
||||
// source/SMLib/src/NL_SrfAdv.cpp).
|
||||
// Wraps N_SrfOffsetGetMaxDeriv, N_SrfEvalPtDerivsUnbounded, N_CreateSkinSpine and
|
||||
// N_CrossBoundaryDerivsVectorField (upstream source/SMLib/src/NL_SrfAdv.cpp).
|
||||
//
|
||||
// One entry point, sc_nlib_srf_adv_p1_eval(op, in, n_in, out, cap, out_len): `op`
|
||||
// selects the routine, `in` holds its inputs, `out` receives a flat record of
|
||||
// doubles: the status (0 = ok, else NL_ERROR.eno, cleared before the call; 1000 if
|
||||
// the flag was set without a number), then, on success only, the results.
|
||||
// the flag was set without a number), then, on success only, the results. Op 0 (the
|
||||
// surface extension by a distance, N_SrfExtendByDist) was removed with the port's
|
||||
// fitted extension; its number is not reused.
|
||||
//
|
||||
// A surface in `in` is: n m p q, then (n+1)*(m+1) control points (x y z w,
|
||||
// row-major), n+p+2 u knots, m+q+2 v knots. A curve is: n p, then n+1 control points
|
||||
@@ -64,7 +65,6 @@ NL_CURVE* read_crv(const double* in, int64_t* at, NL_STACKS* S)
|
||||
|
||||
} // namespace
|
||||
|
||||
// op 0 extend by distance: surface, dist dflg end cont inplace -> status, surface.
|
||||
// op 1 offset max derivatives: surface, d -> status, Muu Muv Mvv.
|
||||
// op 2 unbounded evaluation: surface, cflg ndr cnt, cnt (u v) pairs -> for each pair
|
||||
// until the first failure: status, then D[i][j] (xyz, i + j <= ndr, i outer; the
|
||||
@@ -87,28 +87,6 @@ SC_EXPORT int32_t sc_nlib_srf_adv_p1_eval(int32_t op, const double* in, int64_t
|
||||
N_ErrClear();
|
||||
switch (op)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
NL_SURFACE* sur = read_srf(in, &at, &S);
|
||||
if (sur == NULL)
|
||||
{
|
||||
rc = SC_ERR_ARG;
|
||||
break;
|
||||
}
|
||||
const double* prm = in + at;
|
||||
bool inplace = prm[4] != 0.0;
|
||||
NL_SURFACE* res = inplace ? sur : null_srf(&S);
|
||||
if (res == NULL)
|
||||
{
|
||||
rc = SC_ERR_ARG;
|
||||
break;
|
||||
}
|
||||
err = N_SrfExtendByDist(sur, prm[0], (NL_FLAG)prm[1], (NL_FLAG)prm[2], (NL_FLAG)prm[3], res, &S, &S);
|
||||
o.push_back(nl_status(err));
|
||||
if (err == NL_NO)
|
||||
dump_srf(o, res);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
NL_SURFACE* sur = read_srf(in, &at, &S);
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// difftest shim for unit `nlib.srf_adv_p2`.
|
||||
// Wraps ST_surxsdr (the recursive core of N_SrfExtendByDist) and
|
||||
// N_SrfEvalPtNormalDeriv (upstream source/SMLib/src/NL_SrfAdv.cpp).
|
||||
// Wraps N_SrfEvalPtNormalDeriv (upstream source/SMLib/src/NL_SrfAdv.cpp).
|
||||
//
|
||||
// One entry point, sc_nlib_srf_adv_p2_eval(op, in, n_in, out, cap, out_len): `op`
|
||||
// selects the routine, `in` holds its inputs, `out` receives a flat record of
|
||||
// doubles: the status (0 = ok, else NL_ERROR.eno, cleared before the call; 1000 if
|
||||
// the flag was set without a number), then, on success only, the results.
|
||||
// the flag was set without a number), then, on success only, the results. Op 0 (the
|
||||
// surface extension by a distance, ST_surxsdr) was removed with the port's fitted
|
||||
// extension; its number is not reused.
|
||||
//
|
||||
// A surface in `in` is: n m p q, then (n+1)*(m+1) control points (x y z w,
|
||||
// row-major), n+p+2 u knots, m+q+2 v knots (storage = indices in use). Everything is
|
||||
// allocated on one NL_STACKS. The Rust wrapper validates the surface (valid clamped
|
||||
// knot vectors, degrees 1..=NL_MAXDEG) and the parameters.
|
||||
//
|
||||
// A surface is dumped as n m p q r s, the control points, the u and v knots.
|
||||
#include "sc_shim.h"
|
||||
|
||||
#include <nurbs.h>
|
||||
@@ -23,15 +22,9 @@
|
||||
|
||||
#include "sc_nlib.h"
|
||||
|
||||
using sc::dump_srf;
|
||||
using sc::nl_status;
|
||||
using sc::null_srf;
|
||||
using sc::read_srf;
|
||||
|
||||
// File-local in spirit, but defined with external linkage in NL_SrfAdv.cpp.
|
||||
NL_FLAG ST_surxsdr(NL_SURFACE* surP, NL_REAL dist, NL_FLAG dflg, NL_FLAG end, NL_FLAG cont, NL_SURFACE* surQ,
|
||||
NL_STACKS* SP, NL_STACKS* SQ, int depth);
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
@@ -44,7 +37,6 @@ void push_pt(std::vector<double>& o, const NL_POINT& p)
|
||||
|
||||
} // namespace
|
||||
|
||||
// op 0 extension: surface, dist dflg end cont depth inplace -> status, surface.
|
||||
// op 1 normal derivatives: surface, u v ufl vfl -> status, Nu Nv Nuu Nuv Nvv (xyz each).
|
||||
SC_EXPORT int32_t sc_nlib_srf_adv_p2_eval(int32_t op, const double* in, int64_t n_in, double* out, int64_t cap,
|
||||
int64_t* out_len)
|
||||
@@ -64,22 +56,6 @@ SC_EXPORT int32_t sc_nlib_srf_adv_p2_eval(int32_t op, const double* in, int64_t
|
||||
N_ErrClear();
|
||||
switch (rc == SC_OK ? op : -1)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
bool inplace = prm[5] != 0.0;
|
||||
NL_SURFACE* res = inplace ? sur : null_srf(&S);
|
||||
if (res == NULL)
|
||||
{
|
||||
rc = SC_ERR_ARG;
|
||||
break;
|
||||
}
|
||||
err = ST_surxsdr(sur, prm[0], (NL_FLAG)prm[1], (NL_FLAG)prm[2], (NL_FLAG)prm[3], res, &S, &S,
|
||||
(int)prm[4]);
|
||||
o.push_back(nl_status(err));
|
||||
if (err == NL_NO)
|
||||
dump_srf(o, res);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
NL_POINT** ND = N_AllocPt2dArray(2, 2, &S);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// difftest shim for unit `nurbs.b_spline_surface_n_lib_p1`.
|
||||
// Wraps the SmBSplineSurface members owned by the unit (source/SMLib/src/SmBSplineSurfaceNLib.cpp):
|
||||
// ApproximateOffsetSurface, CreateCoonsPatch (with the static sm_CreateCoonsSrf),
|
||||
// CreateExtendedSurface (both overloads), JoinSurface, CreateNSidedPatch, CreateRuledSurface
|
||||
// JoinSurface, CreateNSidedPatch, CreateRuledSurface
|
||||
// (with the static sm_BuildNlibCurve), CreateSweepSurface, CreateSwungSurface,
|
||||
// EvaluatePointOnExtendedSurface, Reparameterize, Reverse, ExpandBoundarySpan, EvaluateSimple
|
||||
// and EvaluatePoint.
|
||||
@@ -347,7 +347,7 @@ enum : int32_t
|
||||
{
|
||||
// surface ops (`this` from a surface block)
|
||||
OP_OFFSET = 0,
|
||||
OP_EXTEND = 1,
|
||||
// 1: CreateExtendedSurface, removed with the port's fitted extension.
|
||||
OP_JOIN = 2,
|
||||
OP_EXT_EVAL = 3,
|
||||
OP_REPARAM = 4,
|
||||
@@ -568,18 +568,6 @@ void surface_op(int32_t op, Rd& rd, const SmContext& ctx, SmBSplineSurface* s, s
|
||||
put_new(o, stt, r);
|
||||
break;
|
||||
}
|
||||
case OP_EXTEND:
|
||||
{
|
||||
bool dir_overload = rd.flag();
|
||||
SmSurfParamType dir = (SmSurfParamType)rd.idx();
|
||||
double dist = rd.num();
|
||||
SmContinuityType cont = (SmContinuityType)rd.idx();
|
||||
SmSurface* r = NULL;
|
||||
SmStatus stt = dir_overload ? s->CreateExtendedSurface(ctx, dir, dist, cont, r)
|
||||
: s->CreateExtendedSurface(ctx, dist, cont, r);
|
||||
put_new(o, stt, r);
|
||||
break;
|
||||
}
|
||||
case OP_JOIN:
|
||||
{
|
||||
SmBSplineSurface* other = new (ctx) SmBSplineSurface(read_nurb(rd), FALSE, NULL);
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
// STEPInversion, SwapUV, Transform, TrimWithDomain, CreateOffsetSurface, ReadFromDB,
|
||||
// IsKindOf, AssertValid and the setters, run as a script on one offset so that the extension upstream's
|
||||
// const evaluators store is seen by the following ops. WriteToDB is a generator helper.
|
||||
// CreateExtendedSurface steps are refused: UB-107 and the HANG-6/HANG-22 searches inside it
|
||||
// cannot be guarded in process.
|
||||
//
|
||||
// One entry point, sc_surface_offset_surface_eval(op, in, n_in, out, cap, out_len). Layouts
|
||||
// are documented in crates/solidcore-ffi/src/units/surface_offset_surface.rs, which
|
||||
@@ -92,8 +90,7 @@ enum : int64_t
|
||||
S_TRIM = 12,
|
||||
S_SPLIT = 13,
|
||||
S_CREATE_OFFSET = 14,
|
||||
S_EXTEND = 15,
|
||||
S_EXTEND_DIR = 16,
|
||||
// 15, 16: CreateExtendedSurface (refused here), removed with the port's fitted extension.
|
||||
S_COPY = 17,
|
||||
S_EQ = 18,
|
||||
S_SET_DIST = 19,
|
||||
@@ -752,11 +749,6 @@ void run_step(int64_t step, Rd& rd, const SmContext& ctx, SmOffsetSurface* s, st
|
||||
}
|
||||
break;
|
||||
}
|
||||
case S_EXTEND:
|
||||
case S_EXTEND_DIR:
|
||||
// CreateExtendedSurface reaches UB-107 and the HANG-6/HANG-22 searches, which cannot
|
||||
// be guarded in process: refused (the FFI refuses these steps before calling).
|
||||
throw ScOffsetUnsafeInput{};
|
||||
case S_CREATE_OFFSET:
|
||||
case S_COPY:
|
||||
{
|
||||
|
||||
@@ -83,8 +83,7 @@ enum : int32_t
|
||||
OP_INHERITED = 23,
|
||||
OP_REVERSE = 24,
|
||||
OP_SWAP = 25,
|
||||
OP_EXTEND = 26,
|
||||
OP_LAST = 26,
|
||||
OP_LAST = 25,
|
||||
};
|
||||
|
||||
// Reads the protected STEP flags through pointers to members named in a derived class.
|
||||
@@ -781,30 +780,6 @@ void run_op(int32_t op, Rd& rd, const SmContext& ctx, SmPlane* a, std::vector<do
|
||||
break;
|
||||
}
|
||||
case OP_SWAP: o.push_back(st(a->SwapUV())); break;
|
||||
case OP_EXTEND:
|
||||
{
|
||||
bool has_dir = rd.flag();
|
||||
SmSurfParamType dir = (SmSurfParamType)rd.idx(0, SM_SP_VMAX);
|
||||
double dist = rd.num();
|
||||
SmContinuityType cont = (SmContinuityType)rd.idx(0, SM_CT_CINFINITY);
|
||||
// upstream copies a NULL NURB (N_SrfCopy) when none can be made
|
||||
if (a->GetOrCreateGwNurbPointer() == NULL)
|
||||
{
|
||||
o.push_back(st(SM_ERR)); // the port returns SM_ERR (no NURB)
|
||||
break;
|
||||
}
|
||||
SmSurface* e = NULL;
|
||||
SmStatus stt = has_dir ? a->CreateExtendedSurface(ctx, dir, dist, cont, e)
|
||||
: a->CreateExtendedSurface(ctx, dist, cont, e);
|
||||
o.push_back(st(stt));
|
||||
if (stt == SM_SUCCESS)
|
||||
{
|
||||
SmBSplineSurface* bs = e ? SM_CAST_PTR(SmBSplineSurface, e) : NULL;
|
||||
put_surface_nurb(o, bs ? bs->GetGwNurbPointer() : NULL);
|
||||
}
|
||||
delete e;
|
||||
break;
|
||||
}
|
||||
default: throw Bad();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,8 +79,7 @@ enum : int32_t
|
||||
Q_BBOX = 2,
|
||||
Q_DEGEN_POINT = 3,
|
||||
Q_DEGEN_CURVE = 4,
|
||||
Q_EXTEND = 5,
|
||||
Q_EXTEND_DIR = 6,
|
||||
// 5, 6: CreateExtendedSurface (both forms), removed with the port's fitted extension.
|
||||
Q_PLANAR = 7,
|
||||
Q_NUM_CPTS = 8,
|
||||
Q_BILINEAR = 9,
|
||||
@@ -656,29 +655,6 @@ SmBSplineSurface* read_test_surface(Rd& rd, const SmContext& ctx, const uint8_t*
|
||||
return t;
|
||||
}
|
||||
|
||||
// A NURB `p q n+1 m+1 cpts(x y z w, v fastest) nu U nv V`.
|
||||
void put_nurb(std::vector<double>& o, const gw_SURFACE* sur)
|
||||
{
|
||||
o.push_back((double)sur->p);
|
||||
o.push_back((double)sur->q);
|
||||
o.push_back((double)(sur->net->n + 1));
|
||||
o.push_back((double)(sur->net->m + 1));
|
||||
for (gw_INDEX i = 0; i <= sur->net->n; i++)
|
||||
for (gw_INDEX j = 0; j <= sur->net->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);
|
||||
}
|
||||
o.push_back((double)(sur->knu->m + 1));
|
||||
for (gw_INDEX i = 0; i <= sur->knu->m; i++)
|
||||
o.push_back(sur->knu->U[i]);
|
||||
o.push_back((double)(sur->knv->m + 1));
|
||||
for (gw_INDEX i = 0; i <= sur->knv->m; i++)
|
||||
o.push_back(sur->knv->U[i]);
|
||||
}
|
||||
|
||||
// Copy of the file-local sm_CrossedSeamOrPole of SmSurface.cpp.
|
||||
SmBoolean crossed_seam_or_pole(SmSurface* pSurf, const SmPoint2d& crPrevUV, const SmPoint2d& crCurrUV,
|
||||
const SmExtent2d& crDomain)
|
||||
@@ -777,29 +753,6 @@ void lazy_query(Rd& rd, const SmContext& ctx, SmSurface* s, std::vector<double>&
|
||||
o.push_back(flg(s->IsDegenerateCurve(ang, has ? &d : NULL, tol)));
|
||||
break;
|
||||
}
|
||||
case Q_EXTEND:
|
||||
case Q_EXTEND_DIR:
|
||||
{
|
||||
SmSurfParamType dir = (SmSurfParamType)rd.idx();
|
||||
double dist = rd.num();
|
||||
SmContinuityType cont = (SmContinuityType)rd.idx();
|
||||
SmSurface* e = NULL;
|
||||
SmStatus stt = q == Q_EXTEND ? s->CreateExtendedSurface(ctx, dist, cont, e)
|
||||
: s->CreateExtendedSurface(ctx, dir, dist, cont, e);
|
||||
o.push_back(st(stt));
|
||||
if (stt == SM_SUCCESS)
|
||||
{
|
||||
const gw_SURFACE* sur = e && e->IsKindOf(SmBSplineSurface_TYPE)
|
||||
? ((SmBSplineSurface*)e)->GetGwNurbPointer()
|
||||
: NULL;
|
||||
o.push_back(e ? (double)e->GetType() : 0.0);
|
||||
o.push_back(flg(sur != NULL));
|
||||
if (sur)
|
||||
put_nurb(o, sur);
|
||||
}
|
||||
delete e;
|
||||
break;
|
||||
}
|
||||
case Q_PLANAR:
|
||||
{
|
||||
double tol = rd.num();
|
||||
|
||||
@@ -662,8 +662,7 @@ enum : int32_t
|
||||
OP_TRIM = 9,
|
||||
OP_SPLIT = 10,
|
||||
OP_STEP_INV = 11,
|
||||
OP_EXTEND = 12,
|
||||
OP_EXTEND_DIR = 13,
|
||||
// 12, 13: CreateExtendedSurface (both forms), removed with the port's fitted extension.
|
||||
OP_ADJUST_STEP = 14,
|
||||
OP_MAX_ANAL = 15,
|
||||
OP_COPY = 16,
|
||||
@@ -1099,23 +1098,6 @@ void surface_op(int32_t op, Rd& rd, const SmContext& ctx, SmSurface* s, std::vec
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_EXTEND:
|
||||
case OP_EXTEND_DIR:
|
||||
{
|
||||
SmSurfParamType dir = SM_SP_U;
|
||||
if (op == OP_EXTEND_DIR)
|
||||
dir = (SmSurfParamType)rd.idx();
|
||||
double dist = rd.num();
|
||||
SmContinuityType cont = (SmContinuityType)rd.idx();
|
||||
SmSurface* e = NULL;
|
||||
SmStatus stt = op == OP_EXTEND ? s->CreateExtendedSurface(ctx, dist, cont, e)
|
||||
: s->CreateExtendedSurface(ctx, dir, dist, cont, e);
|
||||
o.push_back(st(stt));
|
||||
if (stt == SM_SUCCESS)
|
||||
put_state(o, e);
|
||||
delete e;
|
||||
break;
|
||||
}
|
||||
case OP_ADJUST_STEP:
|
||||
{
|
||||
SmExtent2d d = rd.domain();
|
||||
|
||||
@@ -9,4 +9,4 @@ tests = ["prog_test:my_test_surface_closest_point", "prog_test:my_test_drop_poin
|
||||
status = "green"
|
||||
claimed_by = "swarm-nlib.srf_adv_p1-a1"
|
||||
claimed_at = "2026-10-12T23:52:37Z"
|
||||
notes = "5 fns; largest: N_CreateSkinSpine, N_SrfEvalPtDerivsUnbounded, N_CrossBoundaryDerivsVectorField, N_SrfOffsetGetMaxDeriv; unit-SCC #22 (220 units); 317 upstream test entry points reach this unit; green: 2886 difftest cases (386 fixtures + 2500 generated), 0 deferred, bit-identical"
|
||||
notes = "5 fns; largest: N_CreateSkinSpine, N_SrfEvalPtDerivsUnbounded, N_CrossBoundaryDerivsVectorField, N_SrfOffsetGetMaxDeriv; unit-SCC #22 (220 units); 317 upstream test entry points reach this unit; green: 2886 difftest cases (386 fixtures + 2500 generated), 0 deferred, bit-identical UPDATE (2026-11-04): N_SrfExtendByDist's port deleted with the fitted surface extension; its difftest op extend_by_dist removed (128 fixtures, 1 fuzz-corpus entry, generator arm, shim op 0). difftest 2758 cases (258 fixtures + 2500 generated), all pass bit-identical."
|
||||
|
||||
@@ -9,4 +9,4 @@ tests = ["prog_test:my_test_ss_nurb", "prog_test:my_test_surface_point_intersect
|
||||
status = "green"
|
||||
claimed_by = "swarm-nlib.srf_adv_p2-a1"
|
||||
claimed_at = "2026-10-12T23:00:10Z"
|
||||
notes = "2 fns; largest: ST_surxsdr, N_SrfEvalPtNormalDeriv; unit-SCC #22 (220 units); 182 upstream test entry points reach this unit; difftest 1695 cases (195 fixtures + 1500 generated), 0 deferred, bit-identical"
|
||||
notes = "2 fns; largest: ST_surxsdr, N_SrfEvalPtNormalDeriv; unit-SCC #22 (220 units); 182 upstream test entry points reach this unit; difftest 1695 cases (195 fixtures + 1500 generated), 0 deferred, bit-identical UPDATE (2026-11-04): ST_surxsdr's port deleted with the fitted surface extension; its difftest op extend removed (175 fixtures, the 3-entry fuzz corpus, shim op 0). difftest covers N_SrfEvalPtNormalDeriv only: 1520 cases (20 fixtures + 1500 generated), all pass bit-identical."
|
||||
|
||||
@@ -9,4 +9,4 @@ tests = ["prog_test:my_test_surface_closest_point", "prog_test:my_test_drop_poin
|
||||
status = "green"
|
||||
claimed_by = "swarm-nurbs.b_spline_surface_n_lib_p1-a1"
|
||||
claimed_at = "2026-10-13T00:47:51Z"
|
||||
notes = "main CI e33985411 (2026-10-23): PASS nurbs.b_spline_surface_n_lib_p1 cases=699 passed=699 failed=0 deferred=0. difftest: 665 cases (65 fixtures + 600 generated), 638 pass bit-identical, 27 deferred (surface.surface_p5: SmSurface::IsSingularity in CreateExtendedSurface; then surface.surface_p7 IsClosed); DropCurve path of sm_CreateNonRationalCurve not diffed (upstream crash, see findings). 23 fns; largest: sm_CreateCoonsSrf, SmBSplineSurface::CreateExtendedSurface, SmBSplineSurface::CreateCoonsPatch, SmBSplineSurface::CreateNSidedPatch; cycle back-edges (call into later units; stub or co-port): surface.surface_p2(4), curve.curve_p2(1), intersect.surface_drop_lib_p3(1), surface.surface_p5(1), surface.surface_p7(1); unit-SCC #22 (220 units); 317 upstream test entry points reach this unit Re-run with surface.surface_p5's back-edges wired (PR #50): cases=665 passed=638 failed=0 deferred=27 max_rel_err=0e0 blocked_by=surface.surface_p3(27). GREEN (PR #52, after merging surface_p4/#49 and surface_p5/#50): GetSingularities wired, 665/665 pass, 0 deferred."
|
||||
notes = "main CI e33985411 (2026-10-23): PASS nurbs.b_spline_surface_n_lib_p1 cases=699 passed=699 failed=0 deferred=0. difftest: 665 cases (65 fixtures + 600 generated), 638 pass bit-identical, 27 deferred (surface.surface_p5: SmSurface::IsSingularity in CreateExtendedSurface; then surface.surface_p7 IsClosed); DropCurve path of sm_CreateNonRationalCurve not diffed (upstream crash, see findings). 23 fns; largest: sm_CreateCoonsSrf, SmBSplineSurface::CreateExtendedSurface, SmBSplineSurface::CreateCoonsPatch, SmBSplineSurface::CreateNSidedPatch; cycle back-edges (call into later units; stub or co-port): surface.surface_p2(4), curve.curve_p2(1), intersect.surface_drop_lib_p3(1), surface.surface_p5(1), surface.surface_p7(1); unit-SCC #22 (220 units); 317 upstream test entry points reach this unit Re-run with surface.surface_p5's back-edges wired (PR #50): cases=665 passed=638 failed=0 deferred=27 max_rel_err=0e0 blocked_by=surface.surface_p3(27). GREEN (PR #52, after merging surface_p4/#49 and surface_p5/#50): GetSingularities wired, 665/665 pass, 0 deferred. UPDATE (2026-11-04): BSplineSurface::create_extended_surface(_dir) deleted with the fitted surface extension; difftest op extend removed (1 fixture, 1 fuzz-corpus entry, generator arm, shim op 1). difftest 701 cases, all pass bit-identical."
|
||||
|
||||
@@ -9,4 +9,4 @@ tests = ["prog_test:my_test_extent3d", "prog_test:my_test_vector2d", "prog_test:
|
||||
status = "green"
|
||||
claimed_by = ""
|
||||
claimed_at = ""
|
||||
notes = "GREEN (PR #62): 3077 difftest cases (77 fixtures + 3000 generated), 3077 pass bit-identical, 0 failed, 0 deferred, max_rel_err 0. Extended 100,000-seed run: 100,076 cases, 100,076 pass, 0 failed, 0 deferred, no crash or hang. Promotes intersect.surface_drop_lib_p2 and nurbs.b_spline_surface_p1 to green (their last deferrals were this unit). Adds SurfaceKind::OffsetSurface, kernel-aware copy/split/extension/planarity dispatch, state-preserving STEP inversion and database dispatch; Surface::create_offset_surface dispatches to the plane, revolution (with approx_tol_3d) and offset overrides; an owning offset is its base's owner (BASE_OWNER, upstream m_pSurface->SetOwner(this)) and hands down the face its own owner chain reaches, so the base's GetFace-based tolerances follow upstream (a face-owned offset's expanded box returns Unported(topology.face_p2) instead of the default zone tolerance; LocalPointSolve_1 on the base takes GetZoneTol3d(GetOwner()) as its loose tolerance), and a non-owning offset's base keeps its caller's owner (Surface::clone_keeping_owner); SmSurface::IsPeriodic ignores Evaluate statuses as upstream (Surface::evaluate_ignoring_status; curve-bounded and STEP surfaces pass the array on to their basis); step_inversion_extending stores the extension an offset base's inversion makes. Wires the offset base into the point solver and the intersector kind test. Constructor nesting bounded at 64, JSON bit-roundtrips preserved. The C++ reference runs in process: the shim guards UB-171, UB-172, UB-173 (boxes and IsPlanar's natural-domain box at every level of the offset chain) and HANG-21 on the actual surfaces as the script runs, without changing valid upstream numerics, and CreateExtendedSurface steps are refused (UB-107, HANG-6 and HANG-22 cannot be guarded outside NLib; the per-case worker process that compared them was split out of this PR for a maintainer-reviewed one); the static FFI checks are otherwise well-formedness (no coordinate cap, scale floor or order cap below 64; a revolution base keeps surface.surf_of_revolution_p1's 1e-6 minimum sizes); generated cases cover magnitudes 1e-9 to 1e150, zero/negative scales and orders up to 8; a BaseOffset op compares the CreateOffsetSurface dispatch through SmSurface* over every base kind (distances relative to the base size: a degenerate generator offset copies an uninitialised dMaxGap3d upstream). Candidates the shim refuses are discarded at generation, never counted as passes. The generator keeps reflection extensions of degree-1 NURBs to C-max (UB-32, as nlib.srf_adv_p1). WR-160..WR-165 intentionally retained (WR-166 fixed in nlib.srf_nurb / nlib.srf_adv_p1). Dispatch gaps: the surface cache box callback of an offset (no kernel, no forwarded basis guesses; unreached); SetOffsetDistance through the intersector's shared surfaces waits on blend.fillet_executive_p1. 37 fns; largest: SmOffsetSurface::EvaluatePlainOrSimple, SmOffsetSurface::CalculateBoundingBox, sm_ExtendSurface, SmOffsetSurface::EvaluatePoint; unit-SCC #22 (220 units); 319 upstream test entry points reach this unit. Back-edges wired (PR #62): the SmOffsetSurface CO-PORTED in blend.fillet_custom_solver (PR #78) is now solidcore::surface::offset_surface::OffsetSurface plus the base-pointer SurfaceId; its evaluation stubs call the port, storing extensions (OffsetSurface::with_evaluator runs inherited SmSurface bodies on the offset), and rpSurface2->LocalPointSolve runs SmSurface::LocalPointSolve on the offset; topology.face_p2's thick-face SmOffsetSurface::EvaluateSimple (PR #91) is wired. No UNPORTED(surface.offset_surface) sites remain. Re-runs: blend.fillet_custom_solver 448 cases, 401 pass, 0 failed, 47 deferred (blend.fillet_standard_solver_p2); topology.face_p2 2692 cases, 2322 pass, 0 failed, 370 deferred (offset deferrals 112 -> 0); mass.integrator 1528 cases, 1521 pass, 0 failed, 7 deferred.; CO-PORTED in intersect.surface_drop_lib_p2 (PR #112): the SmOffsetSurface(d, *this, FALSE, lSingularities) DropCurve hands to SmDropPt -> solidcore::intersect::surface_drop_lib::p2::DropOffset (distance and singularities); wired in PR #62: its DropVectors / EvaluateDirectionalDerivs build the ported OffsetSurface over a copy of the drop surface and run the SmSurface bodies (intersect.surface_drop_lib_p2: 1531 cases, 1531 pass, 0 failed, 0 deferred: green) TOUCHED (blend.fillet_solver_p4, PR #212): m_dOffsetDistance is a Cell (set_offset_distance takes &self) so a fillet trace's copy of a solver's offset surface follows the distances the solver sets (upstream: one shared object); 3082/3082 pass."
|
||||
notes = "GREEN (PR #62): 3077 difftest cases (77 fixtures + 3000 generated), 3077 pass bit-identical, 0 failed, 0 deferred, max_rel_err 0. Extended 100,000-seed run: 100,076 cases, 100,076 pass, 0 failed, 0 deferred, no crash or hang. Promotes intersect.surface_drop_lib_p2 and nurbs.b_spline_surface_p1 to green (their last deferrals were this unit). Adds SurfaceKind::OffsetSurface, kernel-aware copy/split/extension/planarity dispatch, state-preserving STEP inversion and database dispatch; Surface::create_offset_surface dispatches to the plane, revolution (with approx_tol_3d) and offset overrides; an owning offset is its base's owner (BASE_OWNER, upstream m_pSurface->SetOwner(this)) and hands down the face its own owner chain reaches, so the base's GetFace-based tolerances follow upstream (a face-owned offset's expanded box returns Unported(topology.face_p2) instead of the default zone tolerance; LocalPointSolve_1 on the base takes GetZoneTol3d(GetOwner()) as its loose tolerance), and a non-owning offset's base keeps its caller's owner (Surface::clone_keeping_owner); SmSurface::IsPeriodic ignores Evaluate statuses as upstream (Surface::evaluate_ignoring_status; curve-bounded and STEP surfaces pass the array on to their basis); step_inversion_extending stores the extension an offset base's inversion makes. Wires the offset base into the point solver and the intersector kind test. Constructor nesting bounded at 64, JSON bit-roundtrips preserved. The C++ reference runs in process: the shim guards UB-171, UB-172, UB-173 (boxes and IsPlanar's natural-domain box at every level of the offset chain) and HANG-21 on the actual surfaces as the script runs, without changing valid upstream numerics, and CreateExtendedSurface steps are refused (UB-107, HANG-6 and HANG-22 cannot be guarded outside NLib; the per-case worker process that compared them was split out of this PR for a maintainer-reviewed one); the static FFI checks are otherwise well-formedness (no coordinate cap, scale floor or order cap below 64; a revolution base keeps surface.surf_of_revolution_p1's 1e-6 minimum sizes); generated cases cover magnitudes 1e-9 to 1e150, zero/negative scales and orders up to 8; a BaseOffset op compares the CreateOffsetSurface dispatch through SmSurface* over every base kind (distances relative to the base size: a degenerate generator offset copies an uninitialised dMaxGap3d upstream). Candidates the shim refuses are discarded at generation, never counted as passes. The generator keeps reflection extensions of degree-1 NURBs to C-max (UB-32, as nlib.srf_adv_p1). WR-160..WR-165 intentionally retained (WR-166 fixed in nlib.srf_nurb / nlib.srf_adv_p1). Dispatch gaps: the surface cache box callback of an offset (no kernel, no forwarded basis guesses; unreached); SetOffsetDistance through the intersector's shared surfaces waits on blend.fillet_executive_p1. 37 fns; largest: SmOffsetSurface::EvaluatePlainOrSimple, SmOffsetSurface::CalculateBoundingBox, sm_ExtendSurface, SmOffsetSurface::EvaluatePoint; unit-SCC #22 (220 units); 319 upstream test entry points reach this unit. Back-edges wired (PR #62): the SmOffsetSurface CO-PORTED in blend.fillet_custom_solver (PR #78) is now solidcore::surface::offset_surface::OffsetSurface plus the base-pointer SurfaceId; its evaluation stubs call the port, storing extensions (OffsetSurface::with_evaluator runs inherited SmSurface bodies on the offset), and rpSurface2->LocalPointSolve runs SmSurface::LocalPointSolve on the offset; topology.face_p2's thick-face SmOffsetSurface::EvaluateSimple (PR #91) is wired. No UNPORTED(surface.offset_surface) sites remain. Re-runs: blend.fillet_custom_solver 448 cases, 401 pass, 0 failed, 47 deferred (blend.fillet_standard_solver_p2); topology.face_p2 2692 cases, 2322 pass, 0 failed, 370 deferred (offset deferrals 112 -> 0); mass.integrator 1528 cases, 1521 pass, 0 failed, 7 deferred.; CO-PORTED in intersect.surface_drop_lib_p2 (PR #112): the SmOffsetSurface(d, *this, FALSE, lSingularities) DropCurve hands to SmDropPt -> solidcore::intersect::surface_drop_lib::p2::DropOffset (distance and singularities); wired in PR #62: its DropVectors / EvaluateDirectionalDerivs build the ported OffsetSurface over a copy of the drop surface and run the SmSurface bodies (intersect.surface_drop_lib_p2: 1531 cases, 1531 pass, 0 failed, 0 deferred: green) TOUCHED (blend.fillet_solver_p4, PR #212): m_dOffsetDistance is a Cell (set_offset_distance takes &self) so a fillet trace's copy of a solver's offset surface follows the distances the solver sets (upstream: one shared object); 3082/3082 pass. UPDATE (2026-11-04): OffsetSurface::create_extended_surface(_dir) deleted with the fitted surface extension; the (already refused) Extend / ExtendDir steps removed from runner, FFI and shim (steps 15/16), with the degree-1 reflection-extension guard. difftest 3182 cases, all pass bit-identical."
|
||||
|
||||
@@ -9,4 +9,4 @@ tests = ["prog_test:my_test_extent3d", "prog_test:my_test_vector2d", "prog_test:
|
||||
status = "green"
|
||||
claimed_by = ""
|
||||
claimed_at = ""
|
||||
notes = "partial: difftest 2705 cases (1205 fixture: 18 upstream-derived, 1179 adversarial, 8 extension; 1500 generated): 2538 passed, 167 deferred (intersect.surface_drop_lib_p3 125, surface.surface_p1 40: IsPlanar of a B-spline reaches SmSurface::GetFace through owner_zone_tolerance once surface.surface_p7 wired IsPlanar (PR #24), surface.surface_p8 2), max_rel_err 0, perturb 2538/2538 detected; private Evaluate1st/2ndDerivatives copies now call surface::surface::p7 (PR #24); CreateExtendedSurface re-dispatched to the plane overrides; surface cache plane paths are UNPORTED(surface.plane_p1) (iso curve needs Curve storage in the cache, CalculateBoundingBox not re-dispatched); 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; FOLLOW-UP (difftest --ssi-corpus, PR #84): model the NURB cache of upstream const queries (GetNaturalUVDomain makes and keeps m_pNurb through GetOrCreateGwNurbPointer; the port makes it on a copy), which IntersectWithPlane's parallel branch (SmPlane.cpp:2183) depends on; see plan/findings.md surface.plane_p1 [surface.surface_p6 wired (PR #92): 2705 cases, 2570 pass, 135 deferred (intersect.surface_drop_lib_p3 129, surface.surface_p5 4, surface.surface_p8 2)] Review PR #106 recertification: 2705 cases, 2574 pass, 131 deferred, 0 failed, max_rel_err=0.0; deferred: intersect.surface_drop_lib_p3 129, surface.surface_p8 2. GREEN (2026-10-23, PR #587): difftest PASS surface.plane_p1 cases=2705 passed=2705 failed=0 deferred=0 max_rel_err=0e0 with_values=2705 no_values=0 vacuous=0; the former 131 deferrals (intersect.surface_drop_lib_p3 129, surface.surface_p8 2) no longer occur on main 9fd1c67c5 (wired by #463, intersect.surface_drop_lib_p3); local rerun and CI run 39923812958 agree. NURB cache modelled (PR #1300): const queries make and keep the plane's NURB as GetOrCreateGwNurbPointer (resolves the FOLLOW-UP above; plan/findings/surface.plane_p1--smplane-part-1--addendum-2026-10-27-nurb-cache-modelled.md); the shim asks const queries of the plane itself and ToggleSwapUVBit / operator== are generated anywhere: difftest PASS surface.plane_p1 cases=2705 passed=2705 failed=0 deferred=0 max_rel_err=0e0."
|
||||
notes = "partial: difftest 2705 cases (1205 fixture: 18 upstream-derived, 1179 adversarial, 8 extension; 1500 generated): 2538 passed, 167 deferred (intersect.surface_drop_lib_p3 125, surface.surface_p1 40: IsPlanar of a B-spline reaches SmSurface::GetFace through owner_zone_tolerance once surface.surface_p7 wired IsPlanar (PR #24), surface.surface_p8 2), max_rel_err 0, perturb 2538/2538 detected; private Evaluate1st/2ndDerivatives copies now call surface::surface::p7 (PR #24); CreateExtendedSurface re-dispatched to the plane overrides; surface cache plane paths are UNPORTED(surface.plane_p1) (iso curve needs Curve storage in the cache, CalculateBoundingBox not re-dispatched); 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; FOLLOW-UP (difftest --ssi-corpus, PR #84): model the NURB cache of upstream const queries (GetNaturalUVDomain makes and keeps m_pNurb through GetOrCreateGwNurbPointer; the port makes it on a copy), which IntersectWithPlane's parallel branch (SmPlane.cpp:2183) depends on; see plan/findings.md surface.plane_p1 [surface.surface_p6 wired (PR #92): 2705 cases, 2570 pass, 135 deferred (intersect.surface_drop_lib_p3 129, surface.surface_p5 4, surface.surface_p8 2)] Review PR #106 recertification: 2705 cases, 2574 pass, 131 deferred, 0 failed, max_rel_err=0.0; deferred: intersect.surface_drop_lib_p3 129, surface.surface_p8 2. GREEN (2026-10-23, PR #587): difftest PASS surface.plane_p1 cases=2705 passed=2705 failed=0 deferred=0 max_rel_err=0e0 with_values=2705 no_values=0 vacuous=0; the former 131 deferrals (intersect.surface_drop_lib_p3 129, surface.surface_p8 2) no longer occur on main 9fd1c67c5 (wired by #463, intersect.surface_drop_lib_p3); local rerun and CI run 39923812958 agree. NURB cache modelled (PR #1300): const queries make and keep the plane's NURB as GetOrCreateGwNurbPointer (resolves the FOLLOW-UP above; plan/findings/surface.plane_p1--smplane-part-1--addendum-2026-10-27-nurb-cache-modelled.md); the shim asks const queries of the plane itself and ToggleSwapUVBit / operator== are generated anywhere: difftest PASS surface.plane_p1 cases=2705 passed=2705 failed=0 deferred=0 max_rel_err=0e0. UPDATE (2026-11-04): Plane::create_extended_surface(_dir) deleted with the fitted surface extension; difftest op extend removed (fixture file extend.json, 8 cases; generator arm; shim op 26). difftest 2698 cases, all pass bit-identical."
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user