mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-08 13:01:52 +00:00
wip: surface.surface_point_solve_p2 port, shim, ffi, runner
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BY8UpXKuMci15rAWGXsbsm
This commit is contained in:
@@ -240,7 +240,7 @@ fn cpp(i: &Inputs) -> Result<Output, HarnessError> {
|
||||
|
||||
// ---- Rust side ----------------------------------------------------------------------
|
||||
|
||||
fn wrapped_of(wrap: bool, s: &SurfIn) -> Result<Surface, HarnessError> {
|
||||
pub(crate) fn wrapped_of(wrap: bool, s: &SurfIn) -> Result<Surface, HarnessError> {
|
||||
let s = surface_in(s)?;
|
||||
Ok(if wrap {
|
||||
Surface::from(StepSurface::new(s, true))
|
||||
@@ -249,15 +249,15 @@ fn wrapped_of(wrap: bool, s: &SurfIn) -> Result<Surface, HarnessError> {
|
||||
})
|
||||
}
|
||||
|
||||
fn p2(uv: [f64; 2]) -> Point2 {
|
||||
pub(crate) fn p2(uv: [f64; 2]) -> Point2 {
|
||||
Point2::new(uv[0], uv[1])
|
||||
}
|
||||
|
||||
fn push2(o: &mut Vec<f64>, v: Point2) {
|
||||
pub(crate) fn push2(o: &mut Vec<f64>, v: Point2) {
|
||||
o.extend([v.x, v.y]);
|
||||
}
|
||||
|
||||
fn solver_op(code: u8) -> Result<SolverOperation, HarnessError> {
|
||||
pub(crate) fn solver_op(code: u8) -> Result<SolverOperation, HarnessError> {
|
||||
SolverOperation::ALL
|
||||
.get(usize::from(code))
|
||||
.copied()
|
||||
@@ -485,10 +485,10 @@ fn rust(i: &Inputs) -> Result<Output, HarnessError> {
|
||||
// ---- generated cases ----------------------------------------------------------------
|
||||
|
||||
/// The supported solver operations.
|
||||
const SOLVERS: [u8; 5] = [0, 3, 4, 5, 10];
|
||||
pub(crate) const SOLVERS: [u8; 5] = [0, 3, 4, 5, 10];
|
||||
|
||||
/// A point of `d`: a corner, a point on a side, or inside.
|
||||
fn gen_uv(rng: &mut Rng, d: [f64; 4]) -> [f64; 2] {
|
||||
pub(crate) fn gen_uv(rng: &mut Rng, d: [f64; 4]) -> [f64; 2] {
|
||||
let pick = |rng: &mut Rng, a: f64, b: f64| -> f64 {
|
||||
match rng.below(6) {
|
||||
0 => a,
|
||||
@@ -500,7 +500,7 @@ fn gen_uv(rng: &mut Rng, d: [f64; 4]) -> [f64; 2] {
|
||||
}
|
||||
|
||||
/// A point in `d` or, half the time, somewhat outside it.
|
||||
fn gen_uv_any(rng: &mut Rng, d: [f64; 4]) -> [f64; 2] {
|
||||
pub(crate) fn gen_uv_any(rng: &mut Rng, d: [f64; 4]) -> [f64; 2] {
|
||||
let [u, v] = gen_uv(rng, d);
|
||||
if rng.chance(0.5) {
|
||||
return [u, v];
|
||||
@@ -514,7 +514,7 @@ fn gen_uv_any(rng: &mut Rng, d: [f64; 4]) -> [f64; 2] {
|
||||
|
||||
/// A test point for the surface: on it, slightly or well off it along its normal, or
|
||||
/// anywhere around it.
|
||||
fn gen_test(rng: &mut Rng, s: &Surface, d: [f64; 4], size: f64) -> [f64; 3] {
|
||||
pub(crate) fn gen_test(rng: &mut Rng, s: &Surface, d: [f64; 4], size: f64) -> [f64; 3] {
|
||||
let uv = gen_uv(rng, d);
|
||||
let Ok((p, su, sv)) = s.evaluate_1st_derivatives(p2(uv), false, false) else {
|
||||
return gvec(rng, size * 2.0);
|
||||
@@ -539,7 +539,7 @@ fn gen_test(rng: &mut Rng, s: &Surface, d: [f64; 4], size: f64) -> [f64; 3] {
|
||||
|
||||
/// A test point on the surface normal at `uv`: on the surface, or off it on either
|
||||
/// side by a small to large distance.
|
||||
fn test_on_normal(rng: &mut Rng, s: &Surface, uv: [f64; 2], size: f64) -> [f64; 3] {
|
||||
pub(crate) fn test_on_normal(rng: &mut Rng, s: &Surface, uv: [f64; 2], size: f64) -> [f64; 3] {
|
||||
let Ok((p, su, sv)) = s.evaluate_1st_derivatives(p2(uv), false, false) else {
|
||||
return gvec(rng, size);
|
||||
};
|
||||
@@ -555,7 +555,7 @@ fn test_on_normal(rng: &mut Rng, s: &Surface, uv: [f64; 2], size: f64) -> [f64;
|
||||
}
|
||||
|
||||
/// A closed B-spline: a full rational circle (or a closed polygon) in u swept in v.
|
||||
fn gen_closed(rng: &mut Rng) -> InlineSrf {
|
||||
pub(crate) fn gen_closed(rng: &mut Rng) -> InlineSrf {
|
||||
let r = rng.uniform(0.5, 5.0);
|
||||
let h = rng.uniform(0.5, 5.0);
|
||||
let v_rows = 2 + rng.below(2) as usize;
|
||||
@@ -623,7 +623,7 @@ fn gen_closed(rng: &mut Rng) -> InlineSrf {
|
||||
}
|
||||
|
||||
/// A bilinear (2 x 2) surface, sometimes twisted.
|
||||
fn gen_bilinear(rng: &mut Rng) -> InlineSrf {
|
||||
pub(crate) fn gen_bilinear(rng: &mut Rng) -> InlineSrf {
|
||||
let tw = if rng.chance(0.5) { 0.0 } else { rng.uniform(-1.0, 1.0) };
|
||||
let net = vec![
|
||||
vec![[0.0, 0.0, 0.0, NOW], [0.0, 1.0, 0.0, NOW]],
|
||||
@@ -675,7 +675,7 @@ fn gen_zigzag(rng: &mut Rng) -> InlineSrf {
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_surface(rng: &mut Rng) -> SurfIn {
|
||||
pub(crate) fn gen_surface(rng: &mut Rng) -> SurfIn {
|
||||
let bss = |s: InlineSrf| BssIn {
|
||||
oob: false,
|
||||
form: default_form(),
|
||||
@@ -690,7 +690,7 @@ fn gen_surface(rng: &mut Rng) -> SurfIn {
|
||||
}
|
||||
|
||||
/// The control-net counts of a surface input (a plane's net is 2 x 2).
|
||||
fn net_counts(s: &SurfIn) -> Result<(usize, usize), HarnessError> {
|
||||
pub(crate) fn net_counts(s: &SurfIn) -> Result<(usize, usize), HarnessError> {
|
||||
match s {
|
||||
SurfIn::Plane(_) => Ok((2, 2)),
|
||||
SurfIn::Bspline(b) | SurfIn::CurveBounded(b) => {
|
||||
@@ -845,7 +845,7 @@ fn gen_case(rng: &mut Rng) -> Result<Inputs, HarnessError> {
|
||||
|
||||
/// The test points of `my_test_surface_closest_point` on `Surface.sms`
|
||||
/// (`tests/prog_test/src/smSurfaceTest.cpp:4117`).
|
||||
const SURFACE_SMS_POINTS: [[f64; 3]; 15] = [
|
||||
pub(crate) const SURFACE_SMS_POINTS: [[f64; 3]; 15] = [
|
||||
[-1.0616429349692744, -20.167126109553802, 20.020823739437560],
|
||||
[-1.1583434957685239, -19.953438722680168, 20.005921768970701],
|
||||
[-4.5941574701649142, -9.6528635542995485, 19.290949275641431],
|
||||
|
||||
@@ -0,0 +1,948 @@
|
||||
//! Runner for unit `surface.surface_point_solve_p2`: the global solver's guesses,
|
||||
//! refinements, seam solutions and `Solve`, and the local stepper's step adjustment,
|
||||
//! norms, convergence and boundary tests, steps and line searches
|
||||
//! (`SmSurfacePointSolve.cpp`).
|
||||
//!
|
||||
//! C++: `solidcore_ffi::units::surface_surface_point_solve_p2` (the shim compiles the
|
||||
//! upstream file, whose classes are file-local). Rust:
|
||||
//! `solidcore::surface::surface_point_solve::p2`.
|
||||
//!
|
||||
//! A case is a problem as in the `surface.surface_point_solve_p1` runner (surface `surf`,
|
||||
//! wrapped in a STEP surface when `wrap`; solver domain `dom`, operation `solver` as an
|
||||
//! `SmSolverOperationType` code, test point `test`, tolerance `tol`) and an `op` with its
|
||||
//! parameters. Both sides produce the flat record documented in the ffi module; the
|
||||
//! runner reports its first entry as the `status` (`0` = `SM_SUCCESS`, else the
|
||||
//! `SmStatus`; `-1` for a Rust error upstream cannot produce) and the rest as the `out`
|
||||
//! values. Solutions are `n` then `u v value objects variables object0_is_surface` each.
|
||||
//!
|
||||
//! Cases: inline cases on the upstream `Surface.sms` with the test points of
|
||||
//! `my_test_surface_closest_point` (`tests/prog_test/src/smSurfaceTest.cpp:4117`), and
|
||||
//! generated cases (planes, B-splines incl. closed, rational, bilinear, zigzag and
|
||||
//! huge / tiny ones, curve-bounded and STEP-wrapped surfaces; sub-domains; test points on,
|
||||
//! near and far from the surface; stepper points inside, on and near the boundary).
|
||||
//!
|
||||
//! Deferred cases: `SmSurface::LocalPointSolve` (`surface.surface_p5`; every `Solve` that
|
||||
//! gets a rough guess), `SmSurface::IsOnSeam` (`surface.surface_p4`; `RefineGuess` on any
|
||||
//! surface but a plane), `SmSurfaceEval::Normal` (`surface.surface_p4`; convergence at a
|
||||
//! singularity), `SmSurface::InvertPointNearSingularity` (`surface.surface_p8`; a step
|
||||
//! from a singularity), and the bounding box of a curve-bounded or STEP surface for the
|
||||
//! directed operation (`surface.surface_p6`, as in p1).
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use solidcore::geom::extent2d::Extent2d;
|
||||
use solidcore::geom::global_solver::SolutionRequested;
|
||||
use solidcore::geom::solution_array::{ObjectKind, Solution, SolutionArray, SolutionObject};
|
||||
use solidcore::kernel::Kernel;
|
||||
use solidcore::surface::surface::Surface;
|
||||
use solidcore::surface::surface_point_solve::p1::ErrorNormType;
|
||||
use solidcore::surface::surface_point_solve::{SurfPtGlobalSolver, SurfPtSolveStepper};
|
||||
use solidcore_ffi::units::surface_surface_point_solve_p2::{self as ffi, Op, MAX_GRID, MAX_HITS};
|
||||
|
||||
use super::surface_surface_cache_p1::{dom_of, flag, gen_sub_dom, harness, srf, status, v3, BssIn, SrfIn};
|
||||
use super::surface_surface_cache_p3::{push_surf, surf_domain, surf_size, surface_in, SurfIn};
|
||||
use super::surface_surface_point_solve_p1::{
|
||||
gen_surface, gen_test, gen_uv, gen_uv_any, net_counts, p2, push2, solver_op, test_on_normal,
|
||||
wrapped_of, SOLVERS, SURFACE_SMS_POINTS,
|
||||
};
|
||||
use crate::case::{CaseContext, CaseSource};
|
||||
use crate::runner::default_cases;
|
||||
use crate::{Case, HarnessError, Output, Rng, UnitDiff};
|
||||
|
||||
/// Generated cases per run, whatever `--seeds` says (it can only raise this).
|
||||
pub const MIN_GENERATED: usize = 900;
|
||||
|
||||
/// `surface.surface_point_solve_p2` runner.
|
||||
pub struct Runner;
|
||||
|
||||
// ---- inputs -----------------------------------------------------------------------
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "op", rename_all = "snake_case")]
|
||||
enum OpIn {
|
||||
Rough,
|
||||
Poly {
|
||||
iu: usize,
|
||||
iv: usize,
|
||||
},
|
||||
Refine {
|
||||
uv: [f64; 2],
|
||||
},
|
||||
Patch {
|
||||
sub: [f64; 4],
|
||||
nu: i32,
|
||||
nv: i32,
|
||||
uv: [f64; 2],
|
||||
best: f64,
|
||||
},
|
||||
Grid {
|
||||
uv: [f64; 2],
|
||||
iu: usize,
|
||||
iv: usize,
|
||||
},
|
||||
Seams {
|
||||
uv: [f64; 2],
|
||||
value: f64,
|
||||
},
|
||||
Solve,
|
||||
Adjust {
|
||||
uv0: [f64; 2],
|
||||
tight: f64,
|
||||
uv1: [f64; 2],
|
||||
hu: i32,
|
||||
hv: i32,
|
||||
},
|
||||
Norms {
|
||||
uv0: [f64; 2],
|
||||
tight: f64,
|
||||
},
|
||||
Conv {
|
||||
uv0: [f64; 2],
|
||||
tight: f64,
|
||||
ctol: f64,
|
||||
},
|
||||
Push {
|
||||
uv0: [f64; 2],
|
||||
tight: f64,
|
||||
ptol: f64,
|
||||
},
|
||||
Snap {
|
||||
uv0: [f64; 2],
|
||||
tight: f64,
|
||||
stol: f64,
|
||||
},
|
||||
Step {
|
||||
uv0: [f64; 2],
|
||||
tight: f64,
|
||||
#[serde(default)]
|
||||
plane: bool,
|
||||
},
|
||||
Line {
|
||||
uv0: [f64; 2],
|
||||
tight: f64,
|
||||
uv1: [f64; 2],
|
||||
},
|
||||
LineB {
|
||||
uv0: [f64; 2],
|
||||
tight: f64,
|
||||
uv1: [f64; 2],
|
||||
norm: u8,
|
||||
},
|
||||
}
|
||||
|
||||
impl OpIn {
|
||||
fn ffi(&self) -> Op {
|
||||
match self {
|
||||
OpIn::Rough => Op::Rough,
|
||||
OpIn::Poly { .. } => Op::Poly,
|
||||
OpIn::Refine { .. } => Op::Refine,
|
||||
OpIn::Patch { .. } => Op::Patch,
|
||||
OpIn::Grid { .. } => Op::Grid,
|
||||
OpIn::Seams { .. } => Op::Seams,
|
||||
OpIn::Solve => Op::Solve,
|
||||
OpIn::Adjust { .. } => Op::Adjust,
|
||||
OpIn::Norms { .. } => Op::Norms,
|
||||
OpIn::Conv { .. } => Op::Conv,
|
||||
OpIn::Push { .. } => Op::Push,
|
||||
OpIn::Snap { .. } => Op::Snap,
|
||||
OpIn::Step { .. } => Op::Step,
|
||||
OpIn::Line { .. } => Op::Line,
|
||||
OpIn::LineB { .. } => Op::LineB,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct Inputs {
|
||||
/// Wrap the surface in an `SmSTEPSurface`.
|
||||
#[serde(default)]
|
||||
wrap: bool,
|
||||
surf: SurfIn,
|
||||
/// The solver domain.
|
||||
dom: [f64; 4],
|
||||
/// The `SmSolverOperationType` code.
|
||||
solver: u8,
|
||||
test: [f64; 3],
|
||||
tol: f64,
|
||||
#[serde(flatten)]
|
||||
op: OpIn,
|
||||
}
|
||||
|
||||
// ---- C++ side -----------------------------------------------------------------------
|
||||
|
||||
fn cpp_input(i: &Inputs) -> Result<(Op, Vec<f64>), HarnessError> {
|
||||
let mut o = vec![flag(i.wrap)];
|
||||
push_surf(&mut o, &i.surf)?;
|
||||
o.extend(i.dom);
|
||||
o.push(f64::from(i.solver));
|
||||
o.extend(i.test);
|
||||
o.push(i.tol);
|
||||
match &i.op {
|
||||
OpIn::Rough | OpIn::Solve => {}
|
||||
OpIn::Poly { iu, iv } => o.extend([*iu as f64, *iv as f64]),
|
||||
OpIn::Refine { uv } => o.extend(uv),
|
||||
OpIn::Patch {
|
||||
sub,
|
||||
nu,
|
||||
nv,
|
||||
uv,
|
||||
best,
|
||||
} => {
|
||||
o.extend(sub);
|
||||
o.extend([f64::from(*nu), f64::from(*nv)]);
|
||||
o.extend(uv);
|
||||
o.push(*best);
|
||||
}
|
||||
OpIn::Grid { uv, iu, iv } => {
|
||||
o.extend(uv);
|
||||
o.extend([*iu as f64, *iv as f64]);
|
||||
}
|
||||
OpIn::Seams { uv, value } => {
|
||||
o.extend(uv);
|
||||
o.push(*value);
|
||||
}
|
||||
OpIn::Adjust {
|
||||
uv0,
|
||||
tight,
|
||||
uv1,
|
||||
hu,
|
||||
hv,
|
||||
} => {
|
||||
o.extend(uv0);
|
||||
o.push(*tight);
|
||||
o.extend(uv1);
|
||||
o.extend([f64::from(*hu), f64::from(*hv)]);
|
||||
}
|
||||
OpIn::Norms { uv0, tight } => {
|
||||
o.extend(uv0);
|
||||
o.push(*tight);
|
||||
}
|
||||
OpIn::Conv {
|
||||
uv0,
|
||||
tight,
|
||||
ctol: t,
|
||||
}
|
||||
| OpIn::Push {
|
||||
uv0,
|
||||
tight,
|
||||
ptol: t,
|
||||
}
|
||||
| OpIn::Snap {
|
||||
uv0,
|
||||
tight,
|
||||
stol: t,
|
||||
} => {
|
||||
o.extend(uv0);
|
||||
o.extend([*tight, *t]);
|
||||
}
|
||||
OpIn::Step { uv0, tight, plane } => {
|
||||
o.extend(uv0);
|
||||
o.extend([*tight, flag(*plane)]);
|
||||
}
|
||||
OpIn::Line { uv0, tight, uv1 } => {
|
||||
o.extend(uv0);
|
||||
o.push(*tight);
|
||||
o.extend(uv1);
|
||||
}
|
||||
OpIn::LineB {
|
||||
uv0,
|
||||
tight,
|
||||
uv1,
|
||||
norm,
|
||||
} => {
|
||||
o.extend(uv0);
|
||||
o.push(*tight);
|
||||
o.extend(uv1);
|
||||
o.push(f64::from(*norm));
|
||||
}
|
||||
}
|
||||
Ok((i.op.ffi(), o))
|
||||
}
|
||||
|
||||
fn to_output(flat: &[f64]) -> Output {
|
||||
let (status, rest) = flat
|
||||
.split_first()
|
||||
.map_or((f64::NAN, &[][..]), |(s, r)| (*s, r));
|
||||
Output::new()
|
||||
.values("status", vec![status])
|
||||
.values("out", rest.to_vec())
|
||||
}
|
||||
|
||||
fn cpp(i: &Inputs) -> Result<Output, HarnessError> {
|
||||
let (op, input) = cpp_input(i)?;
|
||||
let flat = ffi::eval(op, &input).map_err(|e| harness(e.to_string()))?;
|
||||
Ok(to_output(&flat))
|
||||
}
|
||||
|
||||
// ---- Rust side ----------------------------------------------------------------------
|
||||
|
||||
/// The solutions' handle for the case's surface: it reports the surface's natural
|
||||
/// domain (`GetNaturalUVDomain` of the solution object, for `IsEqual`).
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
struct SrfObj(Extent2d);
|
||||
|
||||
impl SolutionObject for SrfObj {
|
||||
fn kind(&self) -> ObjectKind {
|
||||
ObjectKind::Surface
|
||||
}
|
||||
|
||||
fn natural_uv_domain(&self) -> Option<Extent2d> {
|
||||
Some(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
fn push_solutions(o: &mut Vec<f64>, sols: &SolutionArray<SrfObj>, obj: SrfObj) {
|
||||
o.push(sols.len() as f64);
|
||||
for s in sols.as_slice() {
|
||||
o.extend([
|
||||
s.start.parameters[0],
|
||||
s.start.parameters[1],
|
||||
s.start.solution_value,
|
||||
s.num_objects as f64,
|
||||
s.num_variables as f64,
|
||||
flag(s.objects[0] == Some(obj)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
fn norm_of(code: u8) -> ErrorNormType {
|
||||
match code {
|
||||
0 => ErrorNormType::SumAbs,
|
||||
1 => ErrorNormType::Sum,
|
||||
2 => ErrorNormType::F,
|
||||
3 => ErrorNormType::G,
|
||||
4 => ErrorNormType::FAbs,
|
||||
5 => ErrorNormType::GAbs,
|
||||
6 => ErrorNormType::Max,
|
||||
7 => ErrorNormType::MaxAbs,
|
||||
_ => ErrorNormType::Gap,
|
||||
}
|
||||
}
|
||||
|
||||
const ALL_NORMS: [ErrorNormType; 9] = [
|
||||
ErrorNormType::SumAbs,
|
||||
ErrorNormType::Sum,
|
||||
ErrorNormType::F,
|
||||
ErrorNormType::G,
|
||||
ErrorNormType::FAbs,
|
||||
ErrorNormType::GAbs,
|
||||
ErrorNormType::Max,
|
||||
ErrorNormType::MaxAbs,
|
||||
ErrorNormType::Gap,
|
||||
];
|
||||
|
||||
/// `UV BoundHitsU BoundHitsV NRStepSuspect IterCount` of a stepper.
|
||||
fn push_state(o: &mut Vec<f64>, s: &SurfPtSolveStepper<'_>) {
|
||||
push2(o, s.uv());
|
||||
o.extend([
|
||||
f64::from(s.bound_hits_u()),
|
||||
f64::from(s.bound_hits_v()),
|
||||
flag(s.nr_step_suspect()),
|
||||
f64::from(s.iter_count()),
|
||||
]);
|
||||
}
|
||||
|
||||
/// `HitBound(which, sign)` `|n|` times.
|
||||
fn hits(s: &mut SurfPtSolveStepper<'_>, which: solidcore::nurbs::b_spline_surface::SurfParamType, n: i32) {
|
||||
let sign = if n < 0 { -1 } else { 1 };
|
||||
for _ in 0..n.unsigned_abs() {
|
||||
s.hit_bound(which, sign);
|
||||
}
|
||||
}
|
||||
|
||||
/// An error status record (`-1` for an error upstream cannot produce), or the deferral.
|
||||
fn err_record(e: &solidcore::SolidError) -> Result<Vec<f64>, solidcore::SolidError> {
|
||||
if e.is_unported() {
|
||||
return Err(e.clone());
|
||||
}
|
||||
Ok(vec![status(e)])
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn rust_op(i: &Inputs) -> Result<solidcore::Result<Vec<f64>>, HarnessError> {
|
||||
use solidcore::nurbs::b_spline_surface::SurfParamType;
|
||||
let k = Kernel::default();
|
||||
let surface: Surface = wrapped_of(i.wrap, &i.surf)?;
|
||||
let dom = dom_of(i.dom);
|
||||
let op = solver_op(i.solver)?;
|
||||
let test = v3(i.test);
|
||||
let tol = i.tol;
|
||||
let obj = SrfObj(surface.natural_uv_domain());
|
||||
let global = || {
|
||||
SurfPtGlobalSolver::new(
|
||||
&k,
|
||||
&surface,
|
||||
&dom,
|
||||
op,
|
||||
test,
|
||||
tol,
|
||||
None,
|
||||
SolutionRequested::Single,
|
||||
)
|
||||
};
|
||||
let stepper = |uv: [f64; 2], tight: f64| {
|
||||
SurfPtSolveStepper::new(&k, &surface, &dom, test, p2(uv), tight, tol, op)
|
||||
};
|
||||
let run = || -> solidcore::Result<Vec<f64>> {
|
||||
let mut o = Vec::new();
|
||||
match &i.op {
|
||||
OpIn::Rough => {
|
||||
let mut g = global()?;
|
||||
match g.find_rough_guess() {
|
||||
Ok(r) => {
|
||||
o.push(0.0);
|
||||
push2(&mut o, r.uv);
|
||||
o.extend([r.best_idx_u as f64, r.best_idx_v as f64]);
|
||||
}
|
||||
Err(e) => return err_record(&e),
|
||||
}
|
||||
}
|
||||
OpIn::Poly { iu, iv } => {
|
||||
let g = global()?;
|
||||
match g.find_polygon_guess((*iu, *iv)) {
|
||||
Ok(r) => {
|
||||
o.push(0.0);
|
||||
push2(&mut o, r.uv);
|
||||
o.extend([r.best_idx_u as f64, r.best_idx_v as f64, flag(r.suspicious)]);
|
||||
}
|
||||
Err(e) => return err_record(&e),
|
||||
}
|
||||
}
|
||||
OpIn::Refine { uv } => {
|
||||
let mut g = global()?;
|
||||
let mut uv = p2(*uv);
|
||||
match g.refine_guess(&mut uv) {
|
||||
Ok(()) => o.push(0.0),
|
||||
Err(e) => o = err_record(&e)?,
|
||||
}
|
||||
push2(&mut o, uv);
|
||||
}
|
||||
OpIn::Patch {
|
||||
sub,
|
||||
nu,
|
||||
nv,
|
||||
uv,
|
||||
best,
|
||||
} => {
|
||||
let g = global()?;
|
||||
let mut uv = p2(*uv);
|
||||
let mut best = *best;
|
||||
g.refine_patch_by_grid(&dom_of(*sub), *nu, *nv, &mut uv, &mut best)?;
|
||||
o.push(0.0);
|
||||
push2(&mut o, uv);
|
||||
o.push(best);
|
||||
}
|
||||
OpIn::Grid { uv, iu, iv } => {
|
||||
let g = global()?;
|
||||
let r = g.refine_guess_by_grid(p2(*uv), *iu, *iv)?;
|
||||
o.push(0.0);
|
||||
push2(&mut o, r);
|
||||
}
|
||||
OpIn::Seams { uv, value } => {
|
||||
let mut g = global()?;
|
||||
let mut sol: Solution<SrfObj> = Solution::new();
|
||||
sol.num_variables = 2;
|
||||
sol.num_objects = 1;
|
||||
sol.objects[0] = Some(obj);
|
||||
sol.start.parameters[0] = uv[0];
|
||||
sol.start.parameters[1] = uv[1];
|
||||
sol.start.solution_value = *value;
|
||||
let tight = g.base.tight_tol();
|
||||
let sols = g.check_seams(&k, tight, &sol, Some(obj))?;
|
||||
o.push(0.0);
|
||||
push_solutions(&mut o, &sols, obj);
|
||||
}
|
||||
OpIn::Solve => {
|
||||
let mut g = global()?;
|
||||
match g.solve(&k, Some(obj)) {
|
||||
Ok(sols) => {
|
||||
o.push(0.0);
|
||||
push_solutions(&mut o, &sols, obj);
|
||||
}
|
||||
Err(e) => return err_record(&e),
|
||||
}
|
||||
}
|
||||
OpIn::Adjust {
|
||||
uv0,
|
||||
tight,
|
||||
uv1,
|
||||
hu,
|
||||
hv,
|
||||
} => {
|
||||
let mut prev = stepper(*uv0, *tight)?;
|
||||
hits(&mut prev, SurfParamType::U, *hu);
|
||||
hits(&mut prev, SurfParamType::V, *hv);
|
||||
let mut next = SurfPtSolveStepper::copy_of(&prev);
|
||||
next.set_uv(p2(*uv1));
|
||||
match next.adjust_step(&prev) {
|
||||
Ok((adjusted, scale)) => o.extend([0.0, flag(adjusted), scale]),
|
||||
Err(e) => {
|
||||
o = err_record(&e)?;
|
||||
// upstream's outputs on SM_ERR
|
||||
o.extend([flag(true), 0.0]);
|
||||
}
|
||||
}
|
||||
push_state(&mut o, &next);
|
||||
}
|
||||
OpIn::Norms { uv0, tight } => {
|
||||
let mut s = stepper(*uv0, *tight)?;
|
||||
o.push(0.0);
|
||||
for n in ALL_NORMS {
|
||||
o.push(s.error_norm(n)?);
|
||||
}
|
||||
let (b, which) = s.is_singular()?;
|
||||
o.extend([flag(b), f64::from(which as u8)]);
|
||||
}
|
||||
OpIn::Conv { uv0, tight, ctol } => {
|
||||
let mut s = stepper(*uv0, *tight)?;
|
||||
o.push(0.0);
|
||||
o.push(flag(s.converged(*ctol)?));
|
||||
o.push(flag(s.converged_with(true)?));
|
||||
o.push(flag(s.converged_with(false)?));
|
||||
}
|
||||
OpIn::Push { uv0, tight, ptol } => {
|
||||
let mut s = stepper(*uv0, *tight)?;
|
||||
let mut c = SurfPtSolveStepper::copy_of(&s);
|
||||
o.push(0.0);
|
||||
let (b, which) = s.pushing_boundary(*ptol)?;
|
||||
o.extend([flag(b), f64::from(which as u8)]);
|
||||
let (b, which) = c.converged_on_boundary(*ptol)?;
|
||||
o.extend([flag(b), f64::from(which as u8)]);
|
||||
}
|
||||
OpIn::Snap { uv0, tight, stol } => {
|
||||
let mut s = stepper(*uv0, *tight)?;
|
||||
let b = s.snap_to_boundaries(*stol)?;
|
||||
o.extend([0.0, flag(b)]);
|
||||
push_state(&mut o, &s);
|
||||
}
|
||||
OpIn::Step { uv0, tight, plane } => {
|
||||
let mut prev = stepper(*uv0, *tight)?;
|
||||
let mut next = SurfPtSolveStepper::copy_of(&prev);
|
||||
let r = next.step(&mut prev, *plane)?;
|
||||
o.extend([0.0, f64::from(r.code())]);
|
||||
push_state(&mut o, &next);
|
||||
}
|
||||
OpIn::Line { uv0, tight, uv1 } => {
|
||||
let mut prev = stepper(*uv0, *tight)?;
|
||||
let mut next = SurfPtSolveStepper::copy_of(&prev);
|
||||
next.set_uv(p2(*uv1));
|
||||
match next.line_search(&mut prev) {
|
||||
Ok(()) => o.push(0.0),
|
||||
Err(e) => o = err_record(&e)?,
|
||||
}
|
||||
push_state(&mut o, &next);
|
||||
}
|
||||
OpIn::LineB {
|
||||
uv0,
|
||||
tight,
|
||||
uv1,
|
||||
norm,
|
||||
} => {
|
||||
let mut prev = stepper(*uv0, *tight)?;
|
||||
let mut next = SurfPtSolveStepper::copy_of(&prev);
|
||||
next.set_uv(p2(*uv1));
|
||||
match next.line_search_bracketted(&mut prev, norm_of(*norm)) {
|
||||
Ok(()) => o.push(0.0),
|
||||
Err(e) => o = err_record(&e)?,
|
||||
}
|
||||
push_state(&mut o, &next);
|
||||
}
|
||||
}
|
||||
Ok(o)
|
||||
};
|
||||
Ok(run())
|
||||
}
|
||||
|
||||
fn rust(i: &Inputs) -> Result<Output, HarnessError> {
|
||||
Ok(match rust_op(i)? {
|
||||
Ok(o) => to_output(&o),
|
||||
Err(e) => Output::unported(&e).unwrap_or_else(|| to_output(&[status(&e)])),
|
||||
})
|
||||
}
|
||||
|
||||
// ---- generated cases ----------------------------------------------------------------
|
||||
|
||||
/// A point on the boundary of `d` (a side or a corner), or just inside or outside it.
|
||||
fn gen_uv_edge(rng: &mut Rng, d: [f64; 4]) -> [f64; 2] {
|
||||
let [mut u, mut v] = gen_uv(rng, d);
|
||||
let (w, h) = (d[2] - d[0], d[3] - d[1]);
|
||||
match rng.below(4) {
|
||||
0 => u = if rng.chance(0.5) { d[0] } else { d[2] },
|
||||
1 => v = if rng.chance(0.5) { d[1] } else { d[3] },
|
||||
2 => {
|
||||
// near a side, by a tiny to small amount
|
||||
let e = 10f64.powf(rng.uniform(-12.0, -2.0));
|
||||
u = if rng.chance(0.5) { d[0] + e * w } else { d[2] - e * w };
|
||||
}
|
||||
_ => {
|
||||
let e = 10f64.powf(rng.uniform(-12.0, -2.0));
|
||||
v = if rng.chance(0.5) { d[1] + e * h } else { d[3] - e * h };
|
||||
}
|
||||
}
|
||||
[u, v]
|
||||
}
|
||||
|
||||
/// A point of `d` clipped into the surface domain `sd`.
|
||||
fn clip(uv: [f64; 2], sd: [f64; 4]) -> [f64; 2] {
|
||||
[uv[0].clamp(sd[0], sd[2]), uv[1].clamp(sd[1], sd[3])]
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn gen_op(rng: &mut Rng, s: &SurfIn, d: [f64; 4], sd: [f64; 4], size: f64) -> Result<OpIn, HarnessError> {
|
||||
let (nu, nv) = net_counts(s)?;
|
||||
let idx = |rng: &mut Rng, n: usize| -> usize {
|
||||
if rng.chance(0.3) {
|
||||
if rng.chance(0.5) {
|
||||
0
|
||||
} else {
|
||||
n.saturating_sub(1)
|
||||
}
|
||||
} else {
|
||||
rng.below(n.max(1) as u64) as usize
|
||||
}
|
||||
};
|
||||
let small_tol = |rng: &mut Rng| -> f64 {
|
||||
match rng.below(5) {
|
||||
0 => 1e-10,
|
||||
1 => size * 1e-6,
|
||||
2 => -size * 1e-6,
|
||||
3 => 0.0,
|
||||
_ => size * rng.uniform(1e-4, 0.1),
|
||||
}
|
||||
};
|
||||
// stepper points: inside, on or near the boundary of the solver domain
|
||||
let st_uv = |rng: &mut Rng| -> [f64; 2] {
|
||||
if rng.chance(0.5) {
|
||||
gen_uv(rng, sd)
|
||||
} else {
|
||||
clip(gen_uv_edge(rng, sd), d)
|
||||
}
|
||||
};
|
||||
let tight = |rng: &mut Rng| -> f64 {
|
||||
match rng.below(3) {
|
||||
0 => 1e-12,
|
||||
1 => size * 1e-10,
|
||||
_ => size * 1e-7,
|
||||
}
|
||||
};
|
||||
Ok(match rng.below(40) {
|
||||
0..=2 => OpIn::Rough,
|
||||
3..=5 => OpIn::Poly {
|
||||
iu: idx(rng, nu),
|
||||
iv: idx(rng, nv),
|
||||
},
|
||||
6..=7 => OpIn::Refine {
|
||||
uv: clip(gen_uv_any(rng, sd), d),
|
||||
},
|
||||
8..=10 => {
|
||||
let sub = if rng.chance(0.3) {
|
||||
sd
|
||||
} else {
|
||||
gen_sub_dom(rng, d)
|
||||
};
|
||||
let g = |rng: &mut Rng| -> i32 {
|
||||
if rng.chance(0.2) {
|
||||
rng.range_i64(-2, 2) as i32
|
||||
} else {
|
||||
rng.range_i64(2, MAX_GRID.min(9)) as i32
|
||||
}
|
||||
};
|
||||
OpIn::Patch {
|
||||
sub,
|
||||
nu: g(rng),
|
||||
nv: g(rng),
|
||||
uv: gen_uv(rng, sd),
|
||||
best: match rng.below(3) {
|
||||
0 => 1e300,
|
||||
1 => size * rng.uniform(0.0, 2.0),
|
||||
_ => -size,
|
||||
},
|
||||
}
|
||||
}
|
||||
11..=13 => OpIn::Grid {
|
||||
uv: gen_uv(rng, sd),
|
||||
iu: idx(rng, nu),
|
||||
iv: idx(rng, nv),
|
||||
},
|
||||
14..=17 => OpIn::Seams {
|
||||
uv: if rng.chance(0.7) {
|
||||
clip(gen_uv_edge(rng, sd), d)
|
||||
} else {
|
||||
gen_uv(rng, sd)
|
||||
},
|
||||
value: size * rng.uniform(0.0, 1.0),
|
||||
},
|
||||
18 => OpIn::Solve,
|
||||
19..=21 => {
|
||||
let uv0 = st_uv(rng);
|
||||
let uv1 = match rng.below(4) {
|
||||
0 => uv0,
|
||||
1 => [uv0[0] + 1e-13, uv0[1]],
|
||||
_ => gen_uv_any(rng, sd),
|
||||
};
|
||||
OpIn::Adjust {
|
||||
uv0,
|
||||
tight: tight(rng),
|
||||
uv1,
|
||||
hu: rng.range_i64(-MAX_HITS, MAX_HITS) as i32,
|
||||
hv: rng.range_i64(-MAX_HITS, MAX_HITS) as i32,
|
||||
}
|
||||
}
|
||||
22..=24 => OpIn::Norms {
|
||||
uv0: st_uv(rng),
|
||||
tight: tight(rng),
|
||||
},
|
||||
25..=26 => OpIn::Conv {
|
||||
uv0: st_uv(rng),
|
||||
tight: tight(rng),
|
||||
ctol: small_tol(rng).abs().max(1e-12),
|
||||
},
|
||||
27..=29 => OpIn::Push {
|
||||
uv0: st_uv(rng),
|
||||
tight: tight(rng),
|
||||
ptol: small_tol(rng),
|
||||
},
|
||||
30..=31 => OpIn::Snap {
|
||||
uv0: st_uv(rng),
|
||||
tight: tight(rng),
|
||||
stol: {
|
||||
let w = (sd[2] - sd[0]).min(sd[3] - sd[1]);
|
||||
w * 10f64.powf(rng.uniform(-10.0, -1.0))
|
||||
},
|
||||
},
|
||||
32..=35 => OpIn::Step {
|
||||
uv0: st_uv(rng),
|
||||
tight: tight(rng),
|
||||
plane: rng.chance(0.5),
|
||||
},
|
||||
36..=37 => OpIn::Line {
|
||||
uv0: st_uv(rng),
|
||||
tight: tight(rng),
|
||||
uv1: gen_uv(rng, d),
|
||||
},
|
||||
_ => OpIn::LineB {
|
||||
uv0: st_uv(rng),
|
||||
tight: tight(rng),
|
||||
uv1: gen_uv(rng, d),
|
||||
norm: rng.below(9) as u8,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn gen_case(rng: &mut Rng) -> Result<Inputs, HarnessError> {
|
||||
let surf = gen_surface(rng);
|
||||
let wrap = rng.chance(0.15);
|
||||
let d = surf_domain(&surf)?;
|
||||
let size = surf_size(&surf)?;
|
||||
let sd = if rng.chance(0.5) { d } else { gen_sub_dom(rng, d) };
|
||||
let solver = *rng.choose(&SOLVERS).unwrap_or(&3);
|
||||
let s = surface_in(&surf)?;
|
||||
let test = if solver == 10 {
|
||||
match rng.below(4) {
|
||||
0 => [1.0, 0.0, 0.0],
|
||||
1 => [0.0, -1.0, 0.0],
|
||||
_ => super::surface_surface_cache_p1::gvec(rng, 1.0),
|
||||
}
|
||||
} else {
|
||||
gen_test(rng, &s, sd, size)
|
||||
};
|
||||
let tol = match rng.below(4) {
|
||||
0 => 1e-6,
|
||||
1 => size * 1e-8,
|
||||
2 => size * rng.uniform(1e-5, 1e-2),
|
||||
_ => 1e-10,
|
||||
};
|
||||
let op = gen_op(rng, &surf, d, sd, size)?;
|
||||
let mut test = test;
|
||||
// converged / boundary tests: test points on the normal at the stepper point
|
||||
if solver != 10 && rng.chance(0.5) {
|
||||
if let OpIn::Conv { uv0, .. } | OpIn::Push { uv0, .. } | OpIn::Seams { uv: uv0, .. } = &op {
|
||||
test = test_on_normal(rng, &s, *uv0, size);
|
||||
}
|
||||
}
|
||||
Ok(Inputs {
|
||||
wrap,
|
||||
surf,
|
||||
dom: sd,
|
||||
solver,
|
||||
test,
|
||||
tol,
|
||||
op,
|
||||
})
|
||||
}
|
||||
|
||||
// ---- inline cases from the upstream tests ----------------------------------------------------
|
||||
|
||||
fn upstream_cases(ctx: &CaseContext, id: &str) -> Result<Vec<Case>, HarnessError> {
|
||||
let path = ctx.upstream_test_file("Surface.sms")?;
|
||||
let sms = SrfIn::File {
|
||||
sms: path.to_string_lossy().into_owned(),
|
||||
};
|
||||
let inline = srf(&sms)?;
|
||||
let surf = SurfIn::Bspline(BssIn {
|
||||
oob: false,
|
||||
form: super::surface_surface_cache_p1::default_form(),
|
||||
srf: sms,
|
||||
});
|
||||
let d = [
|
||||
inline.u.first().copied().unwrap_or(0.0),
|
||||
inline.v.first().copied().unwrap_or(0.0),
|
||||
inline.u.last().copied().unwrap_or(1.0),
|
||||
inline.v.last().copied().unwrap_or(1.0),
|
||||
];
|
||||
let (nu, nv) = (inline.net.len(), inline.net.first().map_or(0, Vec::len));
|
||||
let mid = [0.5 * (d[0] + d[2]), 0.5 * (d[1] + d[3])];
|
||||
let at = |fu: f64, fv: f64| [d[0] + (d[2] - d[0]) * fu, d[1] + (d[3] - d[1]) * fv];
|
||||
let mut out = Vec::new();
|
||||
for (pi, test) in SURFACE_SMS_POINTS.iter().enumerate() {
|
||||
let f = pi as f64 / 14.0;
|
||||
let ops = [
|
||||
OpIn::Rough,
|
||||
OpIn::Poly { iu: 0, iv: 0 },
|
||||
OpIn::Grid {
|
||||
uv: at(f, 1.0 - f),
|
||||
iu: pi % nu,
|
||||
iv: (pi * 3) % nv,
|
||||
},
|
||||
OpIn::Patch {
|
||||
sub: d,
|
||||
nu: 7,
|
||||
nv: 7,
|
||||
uv: mid,
|
||||
best: 1e300,
|
||||
},
|
||||
OpIn::Seams {
|
||||
uv: at(0.0, f),
|
||||
value: 0.0,
|
||||
},
|
||||
OpIn::Adjust {
|
||||
uv0: mid,
|
||||
tight: 1e-12,
|
||||
uv1: at(1.2, f),
|
||||
hu: 0,
|
||||
hv: 0,
|
||||
},
|
||||
OpIn::Norms {
|
||||
uv0: at(f, 0.5),
|
||||
tight: 1e-12,
|
||||
},
|
||||
OpIn::Conv {
|
||||
uv0: at(f, f),
|
||||
tight: 1e-12,
|
||||
ctol: 1e-6,
|
||||
},
|
||||
OpIn::Push {
|
||||
uv0: at(1.0, f),
|
||||
tight: 1e-12,
|
||||
ptol: 1e-9,
|
||||
},
|
||||
OpIn::Step {
|
||||
uv0: mid,
|
||||
tight: 1e-12,
|
||||
plane: false,
|
||||
},
|
||||
OpIn::Step {
|
||||
uv0: at(f, 0.25),
|
||||
tight: 1e-12,
|
||||
plane: true,
|
||||
},
|
||||
OpIn::Line {
|
||||
uv0: at(0.1, 0.1),
|
||||
tight: 1e-12,
|
||||
uv1: at(0.9, f),
|
||||
},
|
||||
];
|
||||
for (oi, op) in ops.into_iter().enumerate() {
|
||||
let inputs = Inputs {
|
||||
wrap: false,
|
||||
surf: surf.clone(),
|
||||
dom: d,
|
||||
solver: 3,
|
||||
test: *test,
|
||||
tol: 1e-6,
|
||||
op,
|
||||
};
|
||||
out.push(Case {
|
||||
unit: id.to_string(),
|
||||
name: format!("surface_sms_pt{:02}_op{oi}", pi + 1),
|
||||
source: CaseSource::Inline,
|
||||
inputs: serde_json::to_value(inputs).map_err(|e| harness(e.to_string()))?,
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
impl UnitDiff for Runner {
|
||||
fn id(&self) -> &'static str {
|
||||
"surface.surface_point_solve_p2"
|
||||
}
|
||||
|
||||
fn generate(&self, rng: &mut Rng) -> Option<Value> {
|
||||
for _ in 0..64 {
|
||||
let Ok(i) = gen_case(rng) else {
|
||||
continue;
|
||||
};
|
||||
let Ok((op, input)) = cpp_input(&i) else {
|
||||
continue;
|
||||
};
|
||||
if ffi::validate(op, &input).is_ok() {
|
||||
return serde_json::to_value(i).ok();
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn cases(&self, ctx: &CaseContext) -> Result<Vec<Case>, HarnessError> {
|
||||
let mut v = upstream_cases(ctx, self.id())?;
|
||||
let ctx = CaseContext {
|
||||
seeds: ctx.seeds.max(MIN_GENERATED),
|
||||
..ctx.clone()
|
||||
};
|
||||
v.extend(default_cases(self, &ctx)?);
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
fn run_cpp(&self, case: &Case) -> Result<Output, HarnessError> {
|
||||
cpp(&case.parse()?)
|
||||
}
|
||||
|
||||
fn run_rust(&self, case: &Case) -> Result<Output, HarnessError> {
|
||||
rust(&case.parse()?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::super::surface_surface_cache_p1::{default_form, InlineSrf};
|
||||
use super::super::surface_surface_point_solve_p1::gen_bilinear;
|
||||
|
||||
#[test]
|
||||
fn op_codes_round_trip() {
|
||||
let bil: InlineSrf = gen_bilinear(&mut Rng::new(1));
|
||||
let i = Inputs {
|
||||
wrap: false,
|
||||
surf: SurfIn::Bspline(BssIn {
|
||||
oob: false,
|
||||
form: default_form(),
|
||||
srf: SrfIn::Inline(bil),
|
||||
}),
|
||||
dom: [0.0, 0.0, 1.0, 1.0],
|
||||
solver: 3,
|
||||
test: [0.5, 0.5, 1.0],
|
||||
tol: 1e-6,
|
||||
op: OpIn::Patch {
|
||||
sub: [0.0, 0.0, 1.0, 1.0],
|
||||
nu: MAX_GRID as i32,
|
||||
nv: 1,
|
||||
uv: [0.5, 0.5],
|
||||
best: 1e300,
|
||||
},
|
||||
};
|
||||
let (op, input) = cpp_input(&i).unwrap();
|
||||
assert_eq!(op, Op::Patch);
|
||||
assert!(ffi::validate(op, &input).is_ok());
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ impl Op {
|
||||
pub const MAX_HIT_STEPS: usize = 64;
|
||||
|
||||
/// The solver operations the file's classes accept.
|
||||
const OPS: [usize; 5] = [0, 3, 4, 5, 10];
|
||||
pub(crate) const OPS: [usize; 5] = [0, 3, 4, 5, 10];
|
||||
|
||||
mod sys {
|
||||
extern "C" {
|
||||
@@ -124,13 +124,13 @@ mod sys {
|
||||
}
|
||||
|
||||
/// The problem `P`: the surface domain and the control-net counts (`2 x 2` for a plane).
|
||||
struct Problem {
|
||||
srf: Srf,
|
||||
num_u: usize,
|
||||
num_v: usize,
|
||||
pub(crate) struct Problem {
|
||||
pub(crate) srf: Srf,
|
||||
pub(crate) num_u: usize,
|
||||
pub(crate) num_v: usize,
|
||||
}
|
||||
|
||||
fn problem(rd: &mut Reader<'_>) -> Result<Problem, FfiError> {
|
||||
pub(crate) fn problem(rd: &mut Reader<'_>) -> Result<Problem, FfiError> {
|
||||
rd.flag()?;
|
||||
let at = rd.at;
|
||||
let (kind, srf, _) = sized_surface(rd)?;
|
||||
@@ -151,7 +151,7 @@ fn problem(rd: &mut Reader<'_>) -> Result<Problem, FfiError> {
|
||||
}
|
||||
|
||||
/// A point `uv` inside the surface domain.
|
||||
fn uv_in(rd: &mut Reader<'_>, srf: &Srf) -> Result<(), FfiError> {
|
||||
pub(crate) fn uv_in(rd: &mut Reader<'_>, srf: &Srf) -> Result<(), FfiError> {
|
||||
let u = rd.finite()?;
|
||||
let v = rd.finite()?;
|
||||
let d = srf.dom;
|
||||
@@ -162,7 +162,7 @@ fn uv_in(rd: &mut Reader<'_>, srf: &Srf) -> Result<(), FfiError> {
|
||||
}
|
||||
|
||||
/// A point `uv` within a domain size of the surface domain.
|
||||
fn uv_near(rd: &mut Reader<'_>, srf: &Srf) -> Result<(), FfiError> {
|
||||
pub(crate) fn uv_near(rd: &mut Reader<'_>, srf: &Srf) -> Result<(), FfiError> {
|
||||
let u = rd.finite()?;
|
||||
let v = rd.finite()?;
|
||||
let d = srf.dom;
|
||||
@@ -174,7 +174,7 @@ fn uv_near(rd: &mut Reader<'_>, srf: &Srf) -> Result<(), FfiError> {
|
||||
}
|
||||
|
||||
/// Control-net indices `i j` with `i + du < num_u`, `j + dv < num_v`.
|
||||
fn net_index(rd: &mut Reader<'_>, p: &Problem, d: usize) -> Result<(), FfiError> {
|
||||
pub(crate) fn net_index(rd: &mut Reader<'_>, p: &Problem, d: usize) -> Result<(), FfiError> {
|
||||
let i = rd.index(1 << 20)?;
|
||||
let j = rd.index(1 << 20)?;
|
||||
if i + d >= p.num_u || j + d >= p.num_v {
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
//! FFI wrappers for unit `surface.surface_point_solve_p2`.
|
||||
//!
|
||||
//! Shim: `harness/cpp/shim/surface_surface_point_solve_p2.cpp`
|
||||
//! (`sc_surface_surface_point_solve_p2_eval`, which compiles the upstream file with its
|
||||
//! file-local classes renamed). Upstream: `source/SMLib/src/SmSurfacePointSolve.cpp`.
|
||||
//!
|
||||
//! Notation as in [`super::surface_surface_point_solve_p1`]: every op starts with the
|
||||
//! problem `P = W S D op test(3) tol`; `uv` is a point `u v`, `D` a domain
|
||||
//! `umin vmin umax vmax`. The stepper ops continue with `uv0 tight`: the stepper
|
||||
//! `SmSurfPtSolveStepper(S, D, test, uv0, tight, tol, op)`.
|
||||
//!
|
||||
//! | [`Op`] | upstream | parameters |
|
||||
//! |------------|----------------------------------------------------------|------------------------|
|
||||
//! | `Rough` | `SmSurfPtGlobalSolver::FindRoughGuess` | `P` |
|
||||
//! | `Poly` | `FindPolygonGuess` (from best indices `iu iv`) | `P iu iv` |
|
||||
//! | `Refine` | `RefineGuess` | `P uv` |
|
||||
//! | `Patch` | `RefinePatchByGrid` | `P sub nu nv uv best` |
|
||||
//! | `Grid` | `RefineGuessByGrid` | `P uv iu iv` |
|
||||
//! | `Seams` | `CheckSeams` of the solution `(u, v)` with `value` | `P uv value` |
|
||||
//! | `Solve` | `Solve` | `P` |
|
||||
//! | `Adjust` | `AdjustStep` of a copy moved to `uv1` (after `hu`/`hv` boundary hits) | `P uv0 tight uv1 hu hv` |
|
||||
//! | `Norms` | the nine `ErrorNorm`s, `IsSingular` | `P uv0 tight` |
|
||||
//! | `Conv` | `Converged(ctol)`, `Converged(TRUE)`, `Converged(FALSE)` | `P uv0 tight ctol` |
|
||||
//! | `Push` | `PushingBoundary(ptol)`, `ConvergedOnBoundary(ptol)` | `P uv0 tight ptol` |
|
||||
//! | `Snap` | `SnapToBoundaries(stol)` | `P uv0 tight stol` |
|
||||
//! | `Step` | `Step` of a copy, Newton or plane | `P uv0 tight plane` |
|
||||
//! | `Line` | `LineSearch` of a copy moved to `uv1` | `P uv0 tight uv1` |
|
||||
//! | `LineB` | `LineSearchBracketted` of a copy moved to `uv1` | `P uv0 tight uv1 norm` |
|
||||
//!
|
||||
//! The output is the op status (`0` = `SM_SUCCESS`, else the `SmStatus`) and the op's
|
||||
//! outputs (see the shim). [`eval`] accepts only what upstream handles without undefined
|
||||
//! behaviour: the problems of [`super::surface_surface_point_solve_p1`], points inside the
|
||||
//! surface domain wherever a surface evaluator reads them (the step target of `Adjust`,
|
||||
//! which is only clamped, within a domain size of it), a grid sub-domain inside the
|
||||
//! surface domain with grid sizes up to [`MAX_GRID`], control-net indices inside the net,
|
||||
//! boundary-hit counts up to 3, an `SmSrfPtErrorNormType`, finite values.
|
||||
|
||||
use super::surface_surface_cache_p1::{bad, Reader};
|
||||
use super::surface_surface_point_solve_p1::{net_index, problem, uv_in, uv_near};
|
||||
use crate::{call_with_buffer, FfiError};
|
||||
|
||||
/// The upstream routine run by the shim.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Op {
|
||||
/// `FindRoughGuess`.
|
||||
Rough,
|
||||
/// `FindPolygonGuess`.
|
||||
Poly,
|
||||
/// `RefineGuess`.
|
||||
Refine,
|
||||
/// `RefinePatchByGrid`.
|
||||
Patch,
|
||||
/// `RefineGuessByGrid`.
|
||||
Grid,
|
||||
/// `CheckSeams`.
|
||||
Seams,
|
||||
/// `Solve`.
|
||||
Solve,
|
||||
/// `AdjustStep`.
|
||||
Adjust,
|
||||
/// `ErrorNorm`, `IsSingular`.
|
||||
Norms,
|
||||
/// `Converged`.
|
||||
Conv,
|
||||
/// `PushingBoundary`, `ConvergedOnBoundary`.
|
||||
Push,
|
||||
/// `SnapToBoundaries`.
|
||||
Snap,
|
||||
/// `Step`.
|
||||
Step,
|
||||
/// `LineSearch`.
|
||||
Line,
|
||||
/// `LineSearchBracketted`.
|
||||
LineB,
|
||||
}
|
||||
|
||||
impl Op {
|
||||
/// The shim's op code.
|
||||
#[must_use]
|
||||
pub fn code(self) -> i32 {
|
||||
match self {
|
||||
Op::Rough => 0,
|
||||
Op::Poly => 1,
|
||||
Op::Refine => 2,
|
||||
Op::Patch => 3,
|
||||
Op::Grid => 4,
|
||||
Op::Seams => 5,
|
||||
Op::Solve => 6,
|
||||
Op::Adjust => 7,
|
||||
Op::Norms => 8,
|
||||
Op::Conv => 9,
|
||||
Op::Push => 10,
|
||||
Op::Snap => 11,
|
||||
Op::Step => 12,
|
||||
Op::Line => 13,
|
||||
Op::LineB => 14,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Largest grid size of `Patch` (sizes below 2 count as 2 upstream).
|
||||
pub const MAX_GRID: i64 = 24;
|
||||
/// Largest boundary-hit count of `Adjust` (either sign).
|
||||
pub const MAX_HITS: i64 = 3;
|
||||
/// Largest `SmSrfPtErrorNormType` (`SM_SPN_GAP`).
|
||||
pub const MAX_NORM: usize = 8;
|
||||
|
||||
mod sys {
|
||||
extern "C" {
|
||||
pub fn sc_surface_surface_point_solve_p2_eval(
|
||||
op: i32,
|
||||
input: *const f64,
|
||||
n_in: i64,
|
||||
out: *mut f64,
|
||||
cap: i64,
|
||||
out_len: *mut i64,
|
||||
) -> i32;
|
||||
}
|
||||
}
|
||||
|
||||
/// An integer in `-max..=max`.
|
||||
fn small_int(rd: &mut Reader<'_>, max: i64, what: &str) -> Result<(), FfiError> {
|
||||
let v = rd.num()?;
|
||||
if v.fract() != 0.0 || v.abs() > max as f64 {
|
||||
return Err(bad(format!("{what}: bad integer {v}")));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_op(op: Op, rd: &mut Reader<'_>) -> Result<(), FfiError> {
|
||||
let p = problem(rd)?;
|
||||
match op {
|
||||
Op::Rough | Op::Solve => {}
|
||||
Op::Poly => net_index(rd, &p, 0)?,
|
||||
Op::Refine => uv_in(rd, &p.srf)?,
|
||||
Op::Patch => {
|
||||
rd.domain_in(&p.srf, false)?;
|
||||
small_int(rd, MAX_GRID, "grid size")?;
|
||||
small_int(rd, MAX_GRID, "grid size")?;
|
||||
rd.finite()?;
|
||||
rd.finite()?;
|
||||
if rd.num()?.is_nan() {
|
||||
return Err(bad("best value is NaN"));
|
||||
}
|
||||
}
|
||||
Op::Grid => {
|
||||
uv_in(rd, &p.srf)?;
|
||||
net_index(rd, &p, 0)?;
|
||||
}
|
||||
Op::Seams => {
|
||||
uv_in(rd, &p.srf)?;
|
||||
rd.finite()?;
|
||||
}
|
||||
Op::Adjust | Op::Norms | Op::Conv | Op::Push | Op::Snap | Op::Step | Op::Line | Op::LineB => {
|
||||
uv_in(rd, &p.srf)?;
|
||||
rd.finite()?;
|
||||
match op {
|
||||
Op::Adjust => {
|
||||
uv_near(rd, &p.srf)?;
|
||||
small_int(rd, MAX_HITS, "boundary hits")?;
|
||||
small_int(rd, MAX_HITS, "boundary hits")?;
|
||||
}
|
||||
Op::Conv | Op::Push | Op::Snap => {
|
||||
rd.finite()?;
|
||||
}
|
||||
Op::Step => {
|
||||
rd.flag()?;
|
||||
}
|
||||
Op::Line => uv_in(rd, &p.srf)?,
|
||||
Op::LineB => {
|
||||
uv_in(rd, &p.srf)?;
|
||||
rd.index(MAX_NORM)?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks `input` for `op` (layouts in the module docs).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// [`FfiError::InvalidArgument`] for input the shim must not see.
|
||||
pub fn validate(op: Op, input: &[f64]) -> Result<(), FfiError> {
|
||||
let mut rd = Reader { input, at: 0 };
|
||||
validate_op(op, &mut rd)?;
|
||||
rd.done()
|
||||
}
|
||||
|
||||
/// Runs `op` on `input` (layouts in the module docs); returns the shim's output record.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// [`FfiError::InvalidArgument`] for input the shim must not see; [`FfiError::Status`]
|
||||
/// for a shim failure.
|
||||
pub fn eval(op: Op, input: &[f64]) -> Result<Vec<f64>, FfiError> {
|
||||
validate(op, input)?;
|
||||
call_with_buffer(4096, |buf, len| {
|
||||
// SAFETY: input is valid for input.len() reads and holds exactly what the shim
|
||||
// reads for `op` (validated above: well-formed clamped surfaces, planes with
|
||||
// independent axes, a solver domain inside the surface domain, supported
|
||||
// operations, evaluated points and grid sub-domains inside the surface domain,
|
||||
// control-net indices inside the net, bounded grid sizes and hit counts, a valid
|
||||
// norm type); buf is valid for buf.len() writes and the shim honours the
|
||||
// capacity; len is valid.
|
||||
unsafe {
|
||||
sys::sc_surface_surface_point_solve_p2_eval(
|
||||
op.code(),
|
||||
input.as_ptr(),
|
||||
input.len() as i64,
|
||||
buf.as_mut_ptr(),
|
||||
buf.len() as i64,
|
||||
&raw mut *len,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -8,5 +8,6 @@
|
||||
//! `_p3`; each lives in its own submodule and adds methods to the classes of `p1`.
|
||||
|
||||
pub mod p1;
|
||||
pub mod p2;
|
||||
|
||||
pub use p1::{SurfPtGlobalSolver, SurfPtSolveStepper, SurfacePointSolver};
|
||||
|
||||
@@ -150,14 +150,7 @@ impl<'s> SurfacePointSolver<'s> {
|
||||
_solution_requested: SolutionRequested,
|
||||
) -> Result<Self> {
|
||||
// We need an SmBSplineSurface for some operations.
|
||||
// (An SmOffsetSurface would work from its base surface: not ported.)
|
||||
let b_spline = match surface.kind() {
|
||||
SurfaceKind::BSpline(b) => Some(b),
|
||||
SurfaceKind::Plane(p) => Some(p.base()),
|
||||
SurfaceKind::SurfOfRevolution(s) => Some(s.base()),
|
||||
SurfaceKind::CurveBounded(_) | SurfaceKind::Step(_) => None,
|
||||
}
|
||||
.filter(|b| b.gw_nurb().is_some());
|
||||
let b_spline = b_spline_part(surface);
|
||||
|
||||
let mut solver = Self {
|
||||
surface,
|
||||
@@ -255,6 +248,21 @@ impl<'s> SurfacePointSolver<'s> {
|
||||
self.b_spline
|
||||
}
|
||||
|
||||
/// The same problem over `surface` (the surface data re-read from it; every other
|
||||
/// field copied), for a solve on a copy of the surface.
|
||||
pub(super) fn with_surface<'t>(&self, surface: &'t Surface) -> SurfacePointSolver<'t> {
|
||||
SurfacePointSolver {
|
||||
surface,
|
||||
domain: self.domain,
|
||||
test_pt: self.test_pt,
|
||||
dir_vec: self.dir_vec,
|
||||
solver_op: self.solver_op,
|
||||
b_spline: b_spline_part(surface),
|
||||
tight_tol: self.tight_tol,
|
||||
loose_tol: self.loose_tol,
|
||||
}
|
||||
}
|
||||
|
||||
/// `crGap` reduced to its component along the direction for the directed
|
||||
/// operation (`dDot * m_sDirVec`), else unchanged.
|
||||
fn directed(&self, gap: Vec3) -> Vec3 {
|
||||
@@ -267,11 +275,23 @@ impl<'s> SurfacePointSolver<'s> {
|
||||
}
|
||||
}
|
||||
|
||||
/// `m_pBSplSrf`: the surface as an `SmBSplineSurface` with a NURB, if it is one (an
|
||||
/// `SmOffsetSurface` would work from its base surface: not ported).
|
||||
fn b_spline_part(surface: &Surface) -> Option<&BSplineSurface> {
|
||||
match surface.kind() {
|
||||
SurfaceKind::BSpline(b) => Some(b),
|
||||
SurfaceKind::Plane(p) => Some(p.base()),
|
||||
SurfaceKind::SurfOfRevolution(s) => Some(s.base()),
|
||||
SurfaceKind::CurveBounded(_) | SurfaceKind::Step(_) => None,
|
||||
}
|
||||
.filter(|b| b.gw_nurb().is_some())
|
||||
}
|
||||
|
||||
// ---- control-net helpers ------------------------------------------------------------------
|
||||
|
||||
/// The control point `(i, j)` of a net stored row by row with `num_v` points per row,
|
||||
/// divided by its weight when the net is rational.
|
||||
fn control_point(
|
||||
pub(super) fn control_point(
|
||||
pts: &[CPoint],
|
||||
num_v: usize,
|
||||
i: usize,
|
||||
@@ -498,6 +518,22 @@ impl<'s> SurfPtGlobalSolver<'s> {
|
||||
})
|
||||
}
|
||||
|
||||
/// The same solver over `surface` (see [`SurfacePointSolver::with_surface`]),
|
||||
/// keeping the closure computed so far.
|
||||
pub(super) fn with_surface<'t>(&self, surface: &'t Surface) -> SurfPtGlobalSolver<'t> {
|
||||
SurfPtGlobalSolver {
|
||||
base: self.base.with_surface(surface),
|
||||
closed_in_u: self.closed_in_u,
|
||||
closed_in_v: self.closed_in_v,
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes over the closure `other` has computed (the same surface geometry).
|
||||
pub(super) fn copy_closure_from(&mut self, other: &SurfPtGlobalSolver<'_>) {
|
||||
self.closed_in_u = other.closed_in_u;
|
||||
self.closed_in_v = other.closed_in_v;
|
||||
}
|
||||
|
||||
/// Whether the surface is closed over the domain in `which_dir` (`SM_SP_U` or
|
||||
/// `SM_SP_V`; `false` for any other value), computed once (`ClosedSurface`).
|
||||
///
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,516 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// difftest shim for unit `surface.surface_point_solve_p2`: the global solver's guesses,
|
||||
// refinements, seam solutions and Solve, and the local stepper's step adjustment, norms,
|
||||
// convergence tests, steps and line searches, of the file-local classes of upstream
|
||||
// source/SMLib/src/SmSurfacePointSolve.cpp.
|
||||
//
|
||||
// As the p1 shim (surface_surface_point_solve_p1.cpp), this translation unit compiles the
|
||||
// upstream source itself with the classes renamed (sc_spsp2_*), SmSurface::GlobalPointSolve_1
|
||||
// / LocalPointSolve_1 renamed before any header is read, and `private` / `protected` made
|
||||
// public for the class definitions only.
|
||||
//
|
||||
// One entry point, sc_surface_surface_point_solve_p2_eval(op, in, n_in, out, cap, out_len).
|
||||
// Layouts are documented in crates/solidcore-ffi/src/units/surface_surface_point_solve_p2.rs,
|
||||
// which validates every input.
|
||||
#define GlobalPointSolve_1 sc_spsp2_GlobalPointSolve_1
|
||||
#define LocalPointSolve_1 sc_spsp2_LocalPointSolve_1
|
||||
|
||||
#include "sc_shim.h"
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include <SmBSplineCurve.h>
|
||||
#include <SmBSplineSurface.h>
|
||||
#include <SmBrep.h>
|
||||
#include <SmContext.h>
|
||||
#include <SmCurveBoundedSurface.h>
|
||||
#include <SmExtent1d.h>
|
||||
#include <SmExtent2d.h>
|
||||
#include <SmExtent3d.h>
|
||||
#include <SmFace.h>
|
||||
#include <SmGeomUtility.h>
|
||||
#include <SmGraphicsExtern.h>
|
||||
#include <SmGraphicsOutput.h>
|
||||
#include <SmIsoCurve.h>
|
||||
#include <SmLocalSolve1d.h>
|
||||
#include <SmNurbsSrf.h>
|
||||
#include <SmOffsetSurface.h>
|
||||
#include <SmPlane.h>
|
||||
#include <SmSTEPSurface.h>
|
||||
#include <SmSolutionArray.h>
|
||||
#include <SmSurface.h>
|
||||
#include <SmSurfaceCache.h>
|
||||
#include <SmTArray.h>
|
||||
#include <SmVector2d.h>
|
||||
#include <SmVector3d.h>
|
||||
#include <nurbs.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#define SmSurfacePointSolver sc_spsp2_SmSurfacePointSolver
|
||||
#define SmSurfPtGlobalSolver sc_spsp2_SmSurfPtGlobalSolver
|
||||
#define SmSurfPtSolveStepper sc_spsp2_SmSurfPtSolveStepper
|
||||
#define SmSrfPtErrorNormType sc_spsp2_SmSrfPtErrorNormType
|
||||
#define GetTrianglesDist sc_spsp2_GetTrianglesDist
|
||||
#define WellBehavedControlNet sc_spsp2_WellBehavedControlNet
|
||||
#define private public
|
||||
#define protected public
|
||||
#include "../../../upstream/source/SMLib/src/SmSurfacePointSolve.cpp"
|
||||
#undef private
|
||||
#undef protected
|
||||
|
||||
namespace sc_spsp2
|
||||
{
|
||||
enum
|
||||
{
|
||||
OP_ROUGH = 0,
|
||||
OP_POLY = 1,
|
||||
OP_REFINE = 2,
|
||||
OP_PATCH = 3,
|
||||
OP_GRID = 4,
|
||||
OP_SEAMS = 5,
|
||||
OP_SOLVE = 6,
|
||||
OP_ADJUST = 7,
|
||||
OP_NORMS = 8,
|
||||
OP_CONV = 9,
|
||||
OP_PUSH = 10,
|
||||
OP_SNAP = 11,
|
||||
OP_STEP = 12,
|
||||
OP_LINE = 13,
|
||||
OP_LINE_B = 14,
|
||||
};
|
||||
|
||||
struct Rd
|
||||
{
|
||||
const double* in;
|
||||
int64_t at;
|
||||
double num() { return in[at++]; }
|
||||
int64_t idx() { return (int64_t)in[at++]; }
|
||||
bool flag() { return in[at++] != 0.0; }
|
||||
SmVector3d vec()
|
||||
{
|
||||
double x = num();
|
||||
double y = num();
|
||||
double z = num();
|
||||
return SmVector3d(x, y, z);
|
||||
}
|
||||
SmPoint2d uv()
|
||||
{
|
||||
double x = num();
|
||||
double y = num();
|
||||
return SmPoint2d(x, y);
|
||||
}
|
||||
SmExtent2d domain()
|
||||
{
|
||||
double a = num();
|
||||
double b = num();
|
||||
double c = num();
|
||||
double d = num();
|
||||
return SmExtent2d(a, b, c, d);
|
||||
}
|
||||
};
|
||||
|
||||
double st(SmStatus s)
|
||||
{
|
||||
return s == SM_SUCCESS ? 0.0 : (double)s;
|
||||
}
|
||||
|
||||
double flg(bool b)
|
||||
{
|
||||
return b ? 1.0 : 0.0;
|
||||
}
|
||||
|
||||
void put_uv(std::vector<double>& o, const SmVector2d& v)
|
||||
{
|
||||
o.push_back(v.x);
|
||||
o.push_back(v.y);
|
||||
}
|
||||
|
||||
gw_SURFACE* read_nurb(Rd& rd)
|
||||
{
|
||||
gw_INDEX n = (gw_INDEX)rd.idx();
|
||||
gw_INDEX m = (gw_INDEX)rd.idx();
|
||||
gw_DEGREE p = (gw_DEGREE)rd.idx();
|
||||
gw_DEGREE q = (gw_DEGREE)rd.idx();
|
||||
gw_INDEX r = (gw_INDEX)rd.idx();
|
||||
gw_INDEX s = (gw_INDEX)rd.idx();
|
||||
gw_SURFACE* sur = sm_AllocateNurbSurface(n, m, p, q, r, s);
|
||||
for (gw_INDEX i = 0; i <= n; i++)
|
||||
for (gw_INDEX j = 0; j <= m; j++)
|
||||
{
|
||||
sur->net->Pw[i][j].x = rd.num();
|
||||
sur->net->Pw[i][j].y = rd.num();
|
||||
sur->net->Pw[i][j].z = rd.num();
|
||||
sur->net->Pw[i][j].w = rd.num();
|
||||
}
|
||||
for (gw_INDEX i = 0; i <= r; i++)
|
||||
sur->knu->U[i] = rd.num();
|
||||
for (gw_INDEX i = 0; i <= s; i++)
|
||||
sur->knv->U[i] = rd.num();
|
||||
return sur;
|
||||
}
|
||||
|
||||
// `oob form nurb`.
|
||||
SmBSplineSurface* read_bspline(Rd& rd, const SmContext& ctx)
|
||||
{
|
||||
bool oob = rd.flag();
|
||||
SmBSplineSurfaceForm form = (SmBSplineSurfaceForm)rd.idx();
|
||||
SmBSplineSurface* s = new (ctx) SmBSplineSurface(read_nurb(rd), FALSE, NULL);
|
||||
s->GetOutOfBoundsEnabled() = oob ? TRUE : FALSE;
|
||||
s->SetBSplineSurfaceForm(form);
|
||||
return s;
|
||||
}
|
||||
|
||||
// A surface `kind ...`: 0 B-spline `oob form nurb`; 1 bounded plane `o x y sx sy D`;
|
||||
// 2 curve-bounded surface (implicit outer, no boundary curves) over a B-spline block.
|
||||
SmSurface* read_surface(Rd& rd, const SmContext& ctx)
|
||||
{
|
||||
int64_t kind = rd.idx();
|
||||
if (kind == 1)
|
||||
{
|
||||
SmPoint3d o = rd.vec();
|
||||
SmVector3d x = rd.vec();
|
||||
SmVector3d y = rd.vec();
|
||||
double sx = rd.num();
|
||||
double sy = rd.num();
|
||||
SmExtent2d d = rd.domain();
|
||||
return new (ctx) SmPlane(o, x, y, SmVector2d(sx, sy), d, &ctx);
|
||||
}
|
||||
SmBSplineSurface* b = read_bspline(rd, ctx);
|
||||
if (kind == 0)
|
||||
return b;
|
||||
// the curve-bounded surface owns (deletes) its basis
|
||||
SmTArray<SmCurve*> c3d(ctx), cuv(ctx);
|
||||
return new (ctx) SmCurveBoundedSurface(*b, &c3d, &cuv, TRUE);
|
||||
}
|
||||
|
||||
// `W S`: the surface, wrapped in an SmSTEPSurface owning it when W = 1.
|
||||
SmSurface* read_wrapped(Rd& rd, const SmContext& ctx)
|
||||
{
|
||||
bool wrap = rd.flag();
|
||||
SmSurface* s = read_surface(rd, ctx);
|
||||
if (!wrap)
|
||||
return s;
|
||||
return new (ctx) SmSTEPSurface(*s, TRUE);
|
||||
}
|
||||
|
||||
// The problem: `W S D op test tol`.
|
||||
struct Problem
|
||||
{
|
||||
SmSurface* srf;
|
||||
SmExtent2d dom;
|
||||
SmSolverOperationType op;
|
||||
SmPoint3d test;
|
||||
double tol;
|
||||
};
|
||||
|
||||
Problem read_problem(Rd& rd, const SmContext& ctx)
|
||||
{
|
||||
Problem p;
|
||||
p.srf = read_wrapped(rd, ctx);
|
||||
p.dom = rd.domain();
|
||||
p.op = (SmSolverOperationType)rd.idx();
|
||||
p.test = rd.vec();
|
||||
p.tol = rd.num();
|
||||
return p;
|
||||
}
|
||||
|
||||
// `(u v value nobj nvar obj0_is_srf)` of each solution, after their count.
|
||||
void put_solutions(std::vector<double>& o, SmSolutionArray& sols, const SmSurface* srf)
|
||||
{
|
||||
o.push_back((double)sols.GetSize());
|
||||
for (ULONG i = 0; i < sols.GetSize(); i++)
|
||||
{
|
||||
const SmSolution& s = sols[i];
|
||||
o.push_back(s.m_vStart.m_adParameters[0]);
|
||||
o.push_back(s.m_vStart.m_adParameters[1]);
|
||||
o.push_back(s.m_vStart.m_dSolutionValue);
|
||||
o.push_back((double)s.m_lNumObjects);
|
||||
o.push_back((double)s.m_lNumVariables);
|
||||
o.push_back(flg(s.m_apObjects[0] == (const SmObject*)srf));
|
||||
}
|
||||
}
|
||||
|
||||
// `P`: FindRoughGuess.
|
||||
void op_rough(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
||||
{
|
||||
Problem p = read_problem(rd, ctx);
|
||||
SmSurfPtGlobalSolver g(p.srf, p.dom, p.op, p.test, p.tol, NULL, SM_SR_SINGLE);
|
||||
SmPoint2d uv(0.0, 0.0);
|
||||
ULONG iu = 7, iv = 7;
|
||||
SmStatus s = g.FindRoughGuess(uv, iu, iv);
|
||||
o.push_back(st(s));
|
||||
if (s != SM_SUCCESS)
|
||||
return;
|
||||
put_uv(o, uv);
|
||||
o.push_back((double)iu);
|
||||
o.push_back((double)iv);
|
||||
}
|
||||
|
||||
// `P iu iv`: FindPolygonGuess from the given best indices.
|
||||
void op_poly(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
||||
{
|
||||
Problem p = read_problem(rd, ctx);
|
||||
ULONG iu = (ULONG)rd.idx();
|
||||
ULONG iv = (ULONG)rd.idx();
|
||||
SmSurfPtGlobalSolver g(p.srf, p.dom, p.op, p.test, p.tol, NULL, SM_SR_SINGLE);
|
||||
SmPoint2d uv(0.0, 0.0);
|
||||
SmBoolean susp = FALSE;
|
||||
SmStatus s = g.FindPolygonGuess(uv, iu, iv, susp);
|
||||
o.push_back(st(s));
|
||||
if (s != SM_SUCCESS)
|
||||
return;
|
||||
put_uv(o, uv);
|
||||
o.push_back((double)iu);
|
||||
o.push_back((double)iv);
|
||||
o.push_back(flg(susp != FALSE));
|
||||
}
|
||||
|
||||
// `P uv`: RefineGuess.
|
||||
void op_refine(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
||||
{
|
||||
Problem p = read_problem(rd, ctx);
|
||||
SmPoint2d uv = rd.uv();
|
||||
SmSurfPtGlobalSolver g(p.srf, p.dom, p.op, p.test, p.tol, NULL, SM_SR_SINGLE);
|
||||
ULONG iu = 0, iv = 0;
|
||||
SmStatus s = g.RefineGuess(uv, iu, iv);
|
||||
o.push_back(st(s));
|
||||
put_uv(o, uv);
|
||||
}
|
||||
|
||||
// `P sub nu nv uv best`: RefinePatchByGrid.
|
||||
void op_patch(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
||||
{
|
||||
Problem p = read_problem(rd, ctx);
|
||||
SmExtent2d sub = rd.domain();
|
||||
int nu = (int)rd.idx();
|
||||
int nv = (int)rd.idx();
|
||||
SmPoint2d uv = rd.uv();
|
||||
double best = rd.num();
|
||||
SmSurfPtGlobalSolver g(p.srf, p.dom, p.op, p.test, p.tol, NULL, SM_SR_SINGLE);
|
||||
SmStatus s = g.RefinePatchByGrid(sub, nu, nv, uv, best);
|
||||
o.push_back(st(s));
|
||||
put_uv(o, uv);
|
||||
o.push_back(best);
|
||||
}
|
||||
|
||||
// `P uv iu iv`: RefineGuessByGrid.
|
||||
void op_grid(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
||||
{
|
||||
Problem p = read_problem(rd, ctx);
|
||||
SmPoint2d uv = rd.uv();
|
||||
ULONG iu = (ULONG)rd.idx();
|
||||
ULONG iv = (ULONG)rd.idx();
|
||||
SmSurfPtGlobalSolver g(p.srf, p.dom, p.op, p.test, p.tol, NULL, SM_SR_SINGLE);
|
||||
SmStatus s = g.RefineGuessByGrid(uv, iu, iv);
|
||||
o.push_back(st(s));
|
||||
put_uv(o, uv);
|
||||
}
|
||||
|
||||
// `P uv value`: CheckSeams of the solution (u, v) on the surface with that value.
|
||||
void op_seams(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
||||
{
|
||||
Problem p = read_problem(rd, ctx);
|
||||
SmPoint2d uv = rd.uv();
|
||||
double value = rd.num();
|
||||
SmSurfPtGlobalSolver g(p.srf, p.dom, p.op, p.test, p.tol, NULL, SM_SR_SINGLE);
|
||||
SmSolution sol;
|
||||
sol.m_lNumVariables = 2;
|
||||
sol.m_lNumObjects = 1;
|
||||
sol.m_apObjects[0] = p.srf;
|
||||
sol.m_vStart[0] = uv.x;
|
||||
sol.m_vStart[1] = uv.y;
|
||||
sol.m_vStart.m_dSolutionValue = value;
|
||||
SmSolutionArray sols;
|
||||
SmStatus s = g.CheckSeams(g.m_dTightTol, sol, sols);
|
||||
o.push_back(st(s));
|
||||
put_solutions(o, sols, p.srf);
|
||||
}
|
||||
|
||||
// `P`: Solve.
|
||||
void op_solve(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
||||
{
|
||||
Problem p = read_problem(rd, ctx);
|
||||
SmSurfPtGlobalSolver g(p.srf, p.dom, p.op, p.test, p.tol, NULL, SM_SR_SINGLE);
|
||||
SmSolutionArray sols;
|
||||
SmStatus s = g.Solve(sols);
|
||||
o.push_back(st(s));
|
||||
if (s != SM_SUCCESS)
|
||||
return;
|
||||
put_solutions(o, sols, p.srf);
|
||||
}
|
||||
|
||||
// `HitBound(which, sign)` |n| times.
|
||||
void hits(SmSurfPtSolveStepper& s, SmSurfParamType which, int64_t n)
|
||||
{
|
||||
int sign = n < 0 ? -1 : 1;
|
||||
for (int64_t i = 0; i < (n < 0 ? -n : n); i++)
|
||||
s.HitBound(which, sign);
|
||||
}
|
||||
|
||||
// The stepper of `P uv0 tight`.
|
||||
SmSurfPtSolveStepper read_stepper(Rd& rd, const Problem& p)
|
||||
{
|
||||
SmPoint2d uv0 = rd.uv();
|
||||
double tight = rd.num();
|
||||
return SmSurfPtSolveStepper(p.srf, p.dom, p.test, uv0, tight, p.tol, p.op);
|
||||
}
|
||||
|
||||
// `UV BoundHitsU BoundHitsV NRStepSuspect IterCount` of a stepper.
|
||||
void put_state(std::vector<double>& o, SmSurfPtSolveStepper& s)
|
||||
{
|
||||
put_uv(o, s.UV());
|
||||
o.push_back((double)s.BoundHitsU());
|
||||
o.push_back((double)s.BoundHitsV());
|
||||
o.push_back(flg(s.NRStepSuspect() != FALSE));
|
||||
o.push_back((double)s.IterCount());
|
||||
}
|
||||
|
||||
// `P uv0 tight uv1 hu hv`: AdjustStep of a copy of the stepper at uv0 (after hu / hv
|
||||
// boundary hits) moved to uv1.
|
||||
void op_adjust(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
||||
{
|
||||
Problem p = read_problem(rd, ctx);
|
||||
SmSurfPtSolveStepper prev = read_stepper(rd, p);
|
||||
SmPoint2d uv1 = rd.uv();
|
||||
int64_t hu = rd.idx();
|
||||
int64_t hv = rd.idx();
|
||||
hits(prev, SM_SP_U, hu);
|
||||
hits(prev, SM_SP_V, hv);
|
||||
SmSurfPtSolveStepper next(prev);
|
||||
next.SetUV(uv1);
|
||||
SmBoolean adjusted = FALSE;
|
||||
double scale = -7.0;
|
||||
SmStatus s = next.AdjustStep(prev, adjusted, scale);
|
||||
o.push_back(st(s));
|
||||
o.push_back(flg(adjusted != FALSE));
|
||||
o.push_back(scale);
|
||||
put_state(o, next);
|
||||
}
|
||||
|
||||
// `P uv0 tight`: the nine ErrorNorms, IsSingular.
|
||||
void op_norms(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
||||
{
|
||||
Problem p = read_problem(rd, ctx);
|
||||
SmSurfPtSolveStepper s = read_stepper(rd, p);
|
||||
o.push_back(0.0);
|
||||
for (int n = 0; n <= (int)SM_SPN_GAP; n++)
|
||||
o.push_back(s.ErrorNorm((SmSrfPtErrorNormType)n));
|
||||
SmSurfParamType which = SM_SP_UNKNOWN;
|
||||
SmBoolean b = s.IsSingular(&which);
|
||||
o.push_back(flg(b != FALSE));
|
||||
o.push_back((double)which);
|
||||
}
|
||||
|
||||
// `P uv0 tight ctol`: Converged(ctol), Converged(TRUE), Converged(FALSE).
|
||||
void op_conv(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
||||
{
|
||||
Problem p = read_problem(rd, ctx);
|
||||
SmSurfPtSolveStepper s = read_stepper(rd, p);
|
||||
double tol = rd.num();
|
||||
o.push_back(0.0);
|
||||
o.push_back(flg(s.Converged(tol) != FALSE));
|
||||
o.push_back(flg(s.Converged((SmBoolean)TRUE) != FALSE));
|
||||
o.push_back(flg(s.Converged((SmBoolean)FALSE) != FALSE));
|
||||
}
|
||||
|
||||
// `P uv0 tight ptol`: PushingBoundary(ptol), ConvergedOnBoundary(ptol) (fresh steppers).
|
||||
void op_push(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
||||
{
|
||||
Problem p = read_problem(rd, ctx);
|
||||
SmSurfPtSolveStepper s = read_stepper(rd, p);
|
||||
double tol = rd.num();
|
||||
SmSurfPtSolveStepper c(s);
|
||||
o.push_back(0.0);
|
||||
SmSurfParamType which = SM_SP_NEITHER;
|
||||
SmBoolean b = s.PushingBoundary(tol, &which);
|
||||
o.push_back(flg(b != FALSE));
|
||||
o.push_back((double)which);
|
||||
which = SM_SP_NEITHER;
|
||||
b = c.ConvergedOnBoundary(tol, &which);
|
||||
o.push_back(flg(b != FALSE));
|
||||
o.push_back((double)which);
|
||||
}
|
||||
|
||||
// `P uv0 tight stol`: SnapToBoundaries(stol).
|
||||
void op_snap(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
||||
{
|
||||
Problem p = read_problem(rd, ctx);
|
||||
SmSurfPtSolveStepper s = read_stepper(rd, p);
|
||||
double tol = rd.num();
|
||||
SmBoolean b = s.SnapToBoundaries(tol);
|
||||
o.push_back(0.0);
|
||||
o.push_back(flg(b != FALSE));
|
||||
put_state(o, s);
|
||||
}
|
||||
|
||||
// `P uv0 tight plane`: Step of a copy of the stepper at uv0, from it.
|
||||
void op_step(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
||||
{
|
||||
Problem p = read_problem(rd, ctx);
|
||||
SmSurfPtSolveStepper prev = read_stepper(rd, p);
|
||||
bool plane = rd.flag();
|
||||
SmSurfPtSolveStepper next(prev);
|
||||
SmTerminationReasonType r = next.Step(prev, plane ? TRUE : FALSE);
|
||||
o.push_back(0.0);
|
||||
o.push_back((double)r);
|
||||
put_state(o, next);
|
||||
}
|
||||
|
||||
// `P uv0 tight uv1`: LineSearch of a copy of the stepper at uv0 moved to uv1, from it.
|
||||
void op_line(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
||||
{
|
||||
Problem p = read_problem(rd, ctx);
|
||||
SmSurfPtSolveStepper prev = read_stepper(rd, p);
|
||||
SmPoint2d uv1 = rd.uv();
|
||||
SmSurfPtSolveStepper next(prev);
|
||||
next.SetUV(uv1);
|
||||
SmStatus s = next.LineSearch(prev);
|
||||
o.push_back(st(s));
|
||||
put_state(o, next);
|
||||
}
|
||||
|
||||
// `P uv0 tight uv1 norm`: LineSearchBracketted as op_line.
|
||||
void op_line_b(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
||||
{
|
||||
Problem p = read_problem(rd, ctx);
|
||||
SmSurfPtSolveStepper prev = read_stepper(rd, p);
|
||||
SmPoint2d uv1 = rd.uv();
|
||||
SmSrfPtErrorNormType norm = (SmSrfPtErrorNormType)rd.idx();
|
||||
SmSurfPtSolveStepper next(prev);
|
||||
next.SetUV(uv1);
|
||||
SmStatus s = next.LineSearchBracketted(prev, norm);
|
||||
o.push_back(st(s));
|
||||
put_state(o, next);
|
||||
}
|
||||
} // namespace sc_spsp2
|
||||
|
||||
SC_EXPORT int32_t sc_surface_surface_point_solve_p2_eval(int32_t op, const double* in, int64_t n_in, double* out,
|
||||
int64_t cap, int64_t* out_len)
|
||||
{
|
||||
(void)n_in;
|
||||
SC_TRY
|
||||
SmContext ctx;
|
||||
std::vector<double> o;
|
||||
sc_spsp2::Rd rd{in, 0};
|
||||
switch (op)
|
||||
{
|
||||
case sc_spsp2::OP_ROUGH: sc_spsp2::op_rough(rd, ctx, o); break;
|
||||
case sc_spsp2::OP_POLY: sc_spsp2::op_poly(rd, ctx, o); break;
|
||||
case sc_spsp2::OP_REFINE: sc_spsp2::op_refine(rd, ctx, o); break;
|
||||
case sc_spsp2::OP_PATCH: sc_spsp2::op_patch(rd, ctx, o); break;
|
||||
case sc_spsp2::OP_GRID: sc_spsp2::op_grid(rd, ctx, o); break;
|
||||
case sc_spsp2::OP_SEAMS: sc_spsp2::op_seams(rd, ctx, o); break;
|
||||
case sc_spsp2::OP_SOLVE: sc_spsp2::op_solve(rd, ctx, o); break;
|
||||
case sc_spsp2::OP_ADJUST: sc_spsp2::op_adjust(rd, ctx, o); break;
|
||||
case sc_spsp2::OP_NORMS: sc_spsp2::op_norms(rd, ctx, o); break;
|
||||
case sc_spsp2::OP_CONV: sc_spsp2::op_conv(rd, ctx, o); break;
|
||||
case sc_spsp2::OP_PUSH: sc_spsp2::op_push(rd, ctx, o); break;
|
||||
case sc_spsp2::OP_SNAP: sc_spsp2::op_snap(rd, ctx, o); break;
|
||||
case sc_spsp2::OP_STEP: sc_spsp2::op_step(rd, ctx, o); break;
|
||||
case sc_spsp2::OP_LINE: sc_spsp2::op_line(rd, ctx, o); break;
|
||||
case sc_spsp2::OP_LINE_B: sc_spsp2::op_line_b(rd, ctx, o); break;
|
||||
default: return SC_ERR_ARG;
|
||||
}
|
||||
return sc_copy_out(o.data(), (int64_t)o.size(), out, cap, out_len);
|
||||
SC_CATCH
|
||||
}
|
||||
Reference in New Issue
Block a user