mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-04 02:55:54 +00:00
solidcraft-smlib: ssi hands out SMLib's intersection curves exactly, as schema 4 B-splines
ssi::intersect took a sample count and gave each intersection curve as samples: its 3D points, its UV points on each surface and the surfaces at them. The fork's 947eedc hands out the curves SMLib's intersectors return, exactly (smlib_ssi_curve_nurbs: degree, knots, control points, weights), and drops the samples; SMLIB_ABI_VERSION 4. SsiCurve now holds the 3D curve as a schema 4 Curve::Spline or Curve::RationalSpline (rational where a weight is not 1) and each UV curve the method returned as a Curve2, with SMLib's type, deviation estimate and the 3D curve's natural interval; ssi::intersect loses its sample count. A curve SMLib does not hold as a B-spline is Error::Unsupported (it is never approximated); a B-spline whose knot count is not points + degree + 1 is an argument error. The SSI corpus scorer (tests/ssi_corpus.rs) samples the exact curves itself where the corpus runner samples them: 4001 parameters spread evenly over the 3D curve's natural interval and over each UV curve's interval, the surfaces evaluated at the UV samples, samples that cannot be evaluated dropped. The unit test plane_through_sphere_is_a_circle evaluates the exact curve. Nothing else used the samples. SMLIB_REV: the fork at 947eedc. clippy -p solidcraft-smlib --all-targets, with and without native: clean. The corpus gate (smlib_global_intersect_at_1e_6) is not run here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
This commit is contained in:
@@ -1 +1 @@
|
||||
4a81dd9ca9a7bbd41c5125e4cc7b026f6abae5e5
|
||||
f6d2c5e00e63b0923351566714f161decf64373d
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
use std::ffi::{c_char, c_void};
|
||||
|
||||
/// `SMLIB_ABI_VERSION` this crate was written against.
|
||||
pub const ABI_VERSION: i32 = 3;
|
||||
pub const ABI_VERSION: i32 = 4;
|
||||
|
||||
pub const OK: i32 = 0;
|
||||
pub const ERR_EXCEPTION: i32 = -1000;
|
||||
|
||||
+55
-45
@@ -70,27 +70,22 @@ unsafe extern "C" {
|
||||
fn smlib_surface_free(s: *mut RawSurface);
|
||||
fn smlib_surface_domain(s: *const RawSurface, out: *mut f64) -> i32;
|
||||
fn smlib_surface_evaluate(s: *const RawSurface, u: f64, v: f64, out: *mut f64) -> i32;
|
||||
fn smlib_ssi(a: *const RawSurface, b: *const RawSurface, method: i32, approx_tol: f64, samples_per_curve: i32, out: *mut *mut RawResult) -> i32;
|
||||
fn smlib_ssi(a: *const RawSurface, b: *const RawSurface, method: i32, approx_tol: f64, out: *mut *mut RawResult) -> i32;
|
||||
fn smlib_ssi_result_free(r: *mut RawResult);
|
||||
fn smlib_ssi_curve_count(r: *const RawResult, out: *mut i32) -> i32;
|
||||
fn smlib_ssi_curve_info(
|
||||
fn smlib_ssi_curve_info(r: *const RawResult, i: i32, ty: *mut i32, flags: *mut i32, deviation: *mut f64, interval: *mut f64) -> i32;
|
||||
fn smlib_ssi_curve_nurbs(
|
||||
r: *const RawResult,
|
||||
i: i32,
|
||||
ty: *mut i32,
|
||||
flags: *mut i32,
|
||||
samples: *mut i32,
|
||||
deviation: *mut f64,
|
||||
interval: *mut f64,
|
||||
) -> i32;
|
||||
fn smlib_ssi_curve_samples(
|
||||
r: *const RawResult,
|
||||
i: i32,
|
||||
cap: i64,
|
||||
xyz: *mut f64,
|
||||
uv_a: *mut f64,
|
||||
uv_b: *mut f64,
|
||||
a_at_uv: *mut f64,
|
||||
b_at_uv: *mut f64,
|
||||
which: i32,
|
||||
degree: *mut i32,
|
||||
n_points: *mut i64,
|
||||
n_knots: *mut i64,
|
||||
cap_points: i64,
|
||||
points: *mut f64,
|
||||
weights: *mut f64,
|
||||
cap_knots: i64,
|
||||
knots: *mut f64,
|
||||
) -> i32;
|
||||
fn smlib_ssi_point_count(r: *const RawResult, out: *mut i32) -> i32;
|
||||
fn smlib_ssi_point(r: *const RawResult, i: i32, xyz: *mut f64, uv_a: *mut f64, uv_b: *mut f64) -> i32;
|
||||
@@ -139,20 +134,22 @@ impl Drop for ResultHandle {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct CurveInfo {
|
||||
pub ty: i32,
|
||||
pub flags: i32,
|
||||
pub samples: i32,
|
||||
pub deviation: f64,
|
||||
pub interval: [f64; 2],
|
||||
}
|
||||
|
||||
/// One curve's samples, flat: 3 (xyz, a_at_uv, b_at_uv) or 2 (uv_a, uv_b) values per sample.
|
||||
/// `smlib_ssi_curve_nurbs` `which`: the 3D curve, the UV curve on a, on b.
|
||||
pub const CURVE_3D: i32 = 0;
|
||||
pub const CURVE_UV_A: i32 = 1;
|
||||
pub const CURVE_UV_B: i32 = 2;
|
||||
|
||||
/// One B-spline as `smlib_ssi_curve_nurbs` gives it: cartesian control points, `dim` values each,
|
||||
/// one weight each, and every knot (as many as points + degree + 1).
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Samples {
|
||||
pub xyz: Vec<f64>,
|
||||
pub uv_a: Vec<f64>,
|
||||
pub uv_b: Vec<f64>,
|
||||
pub a_at_uv: Vec<f64>,
|
||||
pub b_at_uv: Vec<f64>,
|
||||
pub struct Nurbs {
|
||||
pub points: Vec<f64>,
|
||||
pub weights: Vec<f64>,
|
||||
pub knots: Vec<f64>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "native")]
|
||||
@@ -258,9 +255,9 @@ mod calls {
|
||||
st => Err(st),
|
||||
}
|
||||
}
|
||||
pub fn ssi(a: &SurfaceHandle, b: &SurfaceHandle, method: i32, approx_tol: f64, samples: i32) -> Result<ResultHandle, i32> {
|
||||
pub fn ssi(a: &SurfaceHandle, b: &SurfaceHandle, method: i32, approx_tol: f64) -> Result<ResultHandle, i32> {
|
||||
let mut out: *mut RawResult = std::ptr::null_mut();
|
||||
match (unsafe { smlib_ssi(a.0.as_ptr(), b.0.as_ptr(), method, approx_tol, samples, &mut out) }, std::ptr::NonNull::new(out)) {
|
||||
match (unsafe { smlib_ssi(a.0.as_ptr(), b.0.as_ptr(), method, approx_tol, &mut out) }, std::ptr::NonNull::new(out)) {
|
||||
(OK, Some(p)) => Ok(ResultHandle(p)),
|
||||
(OK, None) => Err(ERR_EMPTY),
|
||||
(s, _) => Err(s),
|
||||
@@ -274,30 +271,43 @@ mod calls {
|
||||
}
|
||||
}
|
||||
pub fn curve_info(r: &ResultHandle, i: i32) -> Result<CurveInfo, i32> {
|
||||
let mut c = CurveInfo { ty: 0, flags: 0, samples: 0, deviation: 0.0, interval: [0.0; 2] };
|
||||
match unsafe { smlib_ssi_curve_info(r.0.as_ptr(), i, &mut c.ty, &mut c.flags, &mut c.samples, &mut c.deviation, c.interval.as_mut_ptr()) } {
|
||||
let mut c = CurveInfo { ty: 0, deviation: 0.0, interval: [0.0; 2] };
|
||||
match unsafe { smlib_ssi_curve_info(r.0.as_ptr(), i, &mut c.ty, std::ptr::null_mut(), &mut c.deviation, c.interval.as_mut_ptr()) } {
|
||||
OK => Ok(c),
|
||||
s => Err(s),
|
||||
}
|
||||
}
|
||||
pub fn curve_samples(r: &ResultHandle, i: i32, n: usize) -> Result<Samples, i32> {
|
||||
let cap = i64::try_from(n).map_err(|_| ERR_ARG)?;
|
||||
let three = n.checked_mul(3).ok_or(ERR_ARG)?;
|
||||
let mut s =
|
||||
Samples { xyz: vec![0.0; three], uv_a: vec![0.0; 2 * n], uv_b: vec![0.0; 2 * n], a_at_uv: vec![0.0; three], b_at_uv: vec![0.0; three] };
|
||||
/// Curve `i`'s `which` B-spline, `dim` coordinates per control point: the counts first, then
|
||||
/// the arrays.
|
||||
pub fn curve_nurbs(r: &ResultHandle, i: i32, which: i32, dim: usize) -> Result<Nurbs, i32> {
|
||||
let (mut degree, mut np, mut nk) = (0i32, 0i64, 0i64);
|
||||
let null = std::ptr::null_mut();
|
||||
let st = unsafe { smlib_ssi_curve_nurbs(r.0.as_ptr(), i, which, &mut degree, &mut np, &mut nk, 0, null, null, 0, null) };
|
||||
if st != OK {
|
||||
return Err(st);
|
||||
}
|
||||
let (n, k) = (usize::try_from(np).map_err(|_| ERR_ARG)?, usize::try_from(nk).map_err(|_| ERR_ARG)?);
|
||||
let degree = usize::try_from(degree).map_err(|_| ERR_ARG)?;
|
||||
if !(n > degree && k == n + degree + 1) {
|
||||
return Err(ERR_ARG);
|
||||
}
|
||||
let mut c = Nurbs { points: vec![0.0; n.checked_mul(dim).ok_or(ERR_ARG)?], weights: vec![0.0; n], knots: vec![0.0; k] };
|
||||
let st = unsafe {
|
||||
smlib_ssi_curve_samples(
|
||||
smlib_ssi_curve_nurbs(
|
||||
r.0.as_ptr(),
|
||||
i,
|
||||
cap,
|
||||
s.xyz.as_mut_ptr(),
|
||||
s.uv_a.as_mut_ptr(),
|
||||
s.uv_b.as_mut_ptr(),
|
||||
s.a_at_uv.as_mut_ptr(),
|
||||
s.b_at_uv.as_mut_ptr(),
|
||||
which,
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
np,
|
||||
c.points.as_mut_ptr(),
|
||||
c.weights.as_mut_ptr(),
|
||||
nk,
|
||||
c.knots.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
if st == OK { Ok(s) } else { Err(st) }
|
||||
if st == OK { Ok(c) } else { Err(st) }
|
||||
}
|
||||
pub fn point_count(r: &ResultHandle) -> Result<i32, i32> {
|
||||
let mut n = 0;
|
||||
@@ -345,7 +355,7 @@ mod calls {
|
||||
pub fn evaluate(_: &SurfaceHandle, _: f64, _: f64) -> Result<[f64; 3], i32> {
|
||||
Err(NOT_BUILT)
|
||||
}
|
||||
pub fn ssi(_: &SurfaceHandle, _: &SurfaceHandle, _: i32, _: f64, _: i32) -> Result<ResultHandle, i32> {
|
||||
pub fn ssi(_: &SurfaceHandle, _: &SurfaceHandle, _: i32, _: f64) -> Result<ResultHandle, i32> {
|
||||
Err(NOT_BUILT)
|
||||
}
|
||||
pub fn curve_count(_: &ResultHandle) -> Result<i32, i32> {
|
||||
@@ -354,7 +364,7 @@ mod calls {
|
||||
pub fn curve_info(_: &ResultHandle, _: i32) -> Result<CurveInfo, i32> {
|
||||
Err(NOT_BUILT)
|
||||
}
|
||||
pub fn curve_samples(_: &ResultHandle, _: i32, _: usize) -> Result<Samples, i32> {
|
||||
pub fn curve_nurbs(_: &ResultHandle, _: i32, _: i32, _: usize) -> Result<Nurbs, i32> {
|
||||
Err(NOT_BUILT)
|
||||
}
|
||||
pub fn point_count(_: &ResultHandle) -> Result<i32, i32> {
|
||||
|
||||
+55
-42
@@ -1,15 +1,14 @@
|
||||
//! Surface/surface intersection (`capi/include/smlib.h`, "Surface/surface intersection"): two
|
||||
//! surfaces described in plain numbers, intersected by one of SMLib's SSI entry points, each
|
||||
//! intersection curve sampled uniformly in its parameters.
|
||||
//! intersection curve exactly as SMLib holds it: schema 4 B-splines (`solidcraft_geom::brep`).
|
||||
//!
|
||||
//! The constructors take exactly the fields of an SSI corpus case (`tests/ssi-corpus`), angles in
|
||||
//! degrees. Each surface's UV domain is SMLib's natural one ([`Surface::domain`]): (u, v) itself
|
||||
//! for planes, the parameterisation of SMLib's internal NURBS form for the other analytic kinds.
|
||||
|
||||
use crate::{Error, Result, error, ffi, init};
|
||||
use solidcraft_geom::brep as b;
|
||||
|
||||
/// `SMLIB_SSI_MAX_SAMPLES`: most samples per curve.
|
||||
pub const MAX_SAMPLES: usize = 100_000;
|
||||
use crate::{Error, Result, error, ffi, init};
|
||||
|
||||
/// A surface with its UV domain, owned on the C side.
|
||||
pub struct Surface(ffi::ssi::SurfaceHandle);
|
||||
@@ -209,27 +208,25 @@ impl CurveType {
|
||||
}
|
||||
}
|
||||
|
||||
/// One intersection curve, sampled at the same count of parameters on the 3D curve and on each
|
||||
/// UV curve. A value SMLib could not evaluate is NaN, and so is every UV value (with the surface
|
||||
/// at it) of a UV curve the method did not return ([`SsiCurve::has_uv_a`]).
|
||||
/// One intersection curve as SMLib returned it: its 3D curve and, where the method returned
|
||||
/// them, its curves in each surface's UV, each the (rational) B-spline SMLib holds (its lines,
|
||||
/// circles and ellipses are B-splines too; an intersection curve one SMLib fitted within the
|
||||
/// approximation tolerance).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SsiCurve {
|
||||
pub curve_type: CurveType,
|
||||
pub has_uv_a: bool,
|
||||
pub has_uv_b: bool,
|
||||
/// SMLib's deviation estimate (NaN: none).
|
||||
pub deviation: f64,
|
||||
/// The 3D curve's natural parameter interval, which `xyz` samples uniformly.
|
||||
/// The 3D curve's natural parameter interval.
|
||||
pub interval: [f64; 2],
|
||||
pub xyz: Vec<[f64; 3]>,
|
||||
pub uv_a: Vec<[f64; 2]>,
|
||||
pub uv_b: Vec<[f64; 2]>,
|
||||
/// Surface a at `uv_a`, b at `uv_b`.
|
||||
pub a_at_uv: Vec<[f64; 3]>,
|
||||
pub b_at_uv: Vec<[f64; 3]>,
|
||||
pub curve: b::Curve,
|
||||
/// On surface a, in a's UV.
|
||||
pub uv_a: Option<b::Curve2>,
|
||||
/// On surface b, in b's UV.
|
||||
pub uv_b: Option<b::Curve2>,
|
||||
}
|
||||
|
||||
/// An isolated point: a [`CurveType::Touching`] curve's first sample.
|
||||
/// An isolated point: where a [`CurveType::Touching`] curve starts.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct SsiPoint {
|
||||
pub xyz: [f64; 3],
|
||||
@@ -244,39 +241,54 @@ pub struct SsiResult {
|
||||
pub points: Vec<SsiPoint>,
|
||||
}
|
||||
|
||||
fn triples(v: &[f64]) -> Vec<[f64; 3]> {
|
||||
v.as_chunks::<3>().0.to_vec()
|
||||
/// The 3D B-spline: polynomial where every weight is 1.
|
||||
fn curve3(c: ffi::ssi::Nurbs) -> b::Curve {
|
||||
let points = c.points.as_chunks::<3>().0;
|
||||
if c.weights.iter().all(|w| *w == 1.0) {
|
||||
b::Curve::Spline { knots: c.knots, points: points.to_vec() }
|
||||
} else {
|
||||
b::Curve::RationalSpline {
|
||||
knots: c.knots,
|
||||
homogeneous: points.iter().zip(&c.weights).map(|(p, w)| [p[0] * w, p[1] * w, p[2] * w, *w]).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pairs(v: &[f64]) -> Vec<[f64; 2]> {
|
||||
v.as_chunks::<2>().0.to_vec()
|
||||
/// A UV B-spline, as [`curve3`].
|
||||
fn curve2(c: ffi::ssi::Nurbs) -> b::Curve2 {
|
||||
let points = c.points.as_chunks::<2>().0;
|
||||
if c.weights.iter().all(|w| *w == 1.0) {
|
||||
b::Curve2::Spline { knots: c.knots, points: points.to_vec() }
|
||||
} else {
|
||||
b::Curve2::RationalSpline { knots: c.knots, homogeneous: points.iter().zip(&c.weights).map(|(p, w)| [p[0] * w, p[1] * w, *w]).collect() }
|
||||
}
|
||||
}
|
||||
|
||||
/// A UV curve the method may not have returned (`SMLIB_ERR_EMPTY`).
|
||||
fn uv(r: &ffi::ssi::ResultHandle, i: i32, which: i32) -> Result<Option<b::Curve2>> {
|
||||
match ffi::ssi::curve_nurbs(r, i, which, 2) {
|
||||
Ok(c) => Ok(Some(curve2(c))),
|
||||
Err(ffi::ERR_EMPTY) => Ok(None),
|
||||
Err(s) => Err(error(s)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Intersects `a` and `b` within their domains with `method` at approximation tolerance
|
||||
/// `approx_tol` (<= 0: SMLib's default, 1/1000 of the surfaces' size), sampling each curve at
|
||||
/// `samples` parameters (2..=[`MAX_SAMPLES`]). An intersector failure is SMLib's status.
|
||||
pub fn intersect(a: &Surface, b: &Surface, method: Method, approx_tol: f64, samples: usize) -> Result<SsiResult> {
|
||||
if !(2..=MAX_SAMPLES).contains(&samples) {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
let r = ffi::ssi::ssi(&a.0, &b.0, method.code(), approx_tol, int(samples)?).map_err(error)?;
|
||||
/// `approx_tol` (<= 0: SMLib's default, 1/1000 of the surfaces' size). An intersector failure is
|
||||
/// SMLib's status; a curve SMLib does not hold as a B-spline is [`Error::Unsupported`].
|
||||
pub fn intersect(a: &Surface, b: &Surface, method: Method, approx_tol: f64) -> Result<SsiResult> {
|
||||
let r = ffi::ssi::ssi(&a.0, &b.0, method.code(), approx_tol).map_err(error)?;
|
||||
let n = ffi::ssi::curve_count(&r).map_err(error)?;
|
||||
let mut curves = Vec::new();
|
||||
for i in 0..n {
|
||||
let info = ffi::ssi::curve_info(&r, i).map_err(error)?;
|
||||
let count = usize::try_from(info.samples).map_err(|_| Error::Argument)?;
|
||||
let s = ffi::ssi::curve_samples(&r, i, count).map_err(error)?;
|
||||
curves.push(SsiCurve {
|
||||
curve_type: CurveType::from_code(info.ty),
|
||||
has_uv_a: info.flags & 1 != 0,
|
||||
has_uv_b: info.flags & 2 != 0,
|
||||
deviation: info.deviation,
|
||||
interval: info.interval,
|
||||
xyz: triples(&s.xyz),
|
||||
uv_a: pairs(&s.uv_a),
|
||||
uv_b: pairs(&s.uv_b),
|
||||
a_at_uv: triples(&s.a_at_uv),
|
||||
b_at_uv: triples(&s.b_at_uv),
|
||||
curve: curve3(ffi::ssi::curve_nurbs(&r, i, ffi::ssi::CURVE_3D, 3).map_err(error)?),
|
||||
uv_a: uv(&r, i, ffi::ssi::CURVE_UV_A)?,
|
||||
uv_b: uv(&r, i, ffi::ssi::CURVE_UV_B)?,
|
||||
});
|
||||
}
|
||||
let n = ffi::ssi::point_count(&r).map_err(error)?;
|
||||
@@ -304,15 +316,16 @@ mod tests {
|
||||
(Ok(p), Ok(s)) => (p, s),
|
||||
(p, s) => panic!("{p:?} {s:?}"),
|
||||
};
|
||||
let r = intersect(&plane, &sphere, Method::Global, 1e-6, 101).expect("intersect");
|
||||
let r = intersect(&plane, &sphere, Method::Global, 1e-6).expect("intersect");
|
||||
assert!(!r.curves.is_empty());
|
||||
for c in &r.curves {
|
||||
assert_eq!(c.xyz.len(), 101);
|
||||
for p in &c.xyz {
|
||||
assert!((p[0].hypot(p[1]) - 1.0).abs() < 1e-5 && p[2].abs() < 1e-5, "{p:?}");
|
||||
let eval = b::eval::CurveEval::new(&c.curve).expect("a curve");
|
||||
let [t0, t1] = c.interval;
|
||||
for k in 0..=100 {
|
||||
let p = eval.point(t0 + (t1 - t0) * f64::from(k) / 100.0).expect("evaluates");
|
||||
assert!((p.x.hypot(p.y) - 1.0).abs() < 1e-5 && p.z.abs() < 1e-5, "{p:?}");
|
||||
}
|
||||
}
|
||||
assert_eq!(intersect(&plane, &sphere, Method::Global, 1e-6, 1).err(), Some(Error::Argument));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -530,12 +530,20 @@ fn surface(s: &Value) -> Result<solidcraft_smlib::ssi::Surface, String> {
|
||||
r.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// SMLib's answer as the corpus runner writes it for `gsi`: samples SMLib could not evaluate
|
||||
/// dropped, empty UV arrays for a UV curve the method did not return, the runner's type names,
|
||||
/// and no isolated points (the runner only reports those in face mode).
|
||||
/// SMLib's answer as the corpus runner writes it for `gsi`: each exact curve sampled where the
|
||||
/// runner samples it ([`RUNNER_SAMPLES`] parameters spread evenly over the 3D curve's natural
|
||||
/// interval, each UV curve over its own, the surfaces at the UV samples), samples that cannot be
|
||||
/// evaluated dropped, empty UV arrays for a UV curve the method did not return, the runner's type
|
||||
/// names, and no isolated points (the runner only reports those in face mode).
|
||||
#[cfg(feature = "native")]
|
||||
fn runner_output(r: solidcraft_smlib::Result<solidcraft_smlib::ssi::SsiResult>, ms: f64) -> Outcome {
|
||||
use solidcraft_smlib::ssi::CurveType;
|
||||
fn runner_output(
|
||||
r: solidcraft_smlib::Result<solidcraft_smlib::ssi::SsiResult>,
|
||||
a: &solidcraft_smlib::ssi::Surface,
|
||||
b: &solidcraft_smlib::ssi::Surface,
|
||||
ms: f64,
|
||||
) -> Outcome {
|
||||
use solidcraft_geom::brep::{Curve2, eval::CurveEval};
|
||||
use solidcraft_smlib::ssi::{CurveType, Surface};
|
||||
let r = match r {
|
||||
Ok(r) => r,
|
||||
Err(solidcraft_smlib::Error::Status(s)) => {
|
||||
@@ -543,33 +551,33 @@ fn runner_output(r: solidcraft_smlib::Result<solidcraft_smlib::ssi::SsiResult>,
|
||||
}
|
||||
Err(e) => return Outcome::Crashed(e.to_string()),
|
||||
};
|
||||
let curves = r
|
||||
.curves
|
||||
.into_iter()
|
||||
.map(|c| {
|
||||
let curve_type = match c.curve_type {
|
||||
CurveType::None => "n/a",
|
||||
CurveType::Unknown => "UNKNOWN",
|
||||
CurveType::Touching => "TOUCHING",
|
||||
CurveType::Crossing => "CROSSING",
|
||||
CurveType::Tangent => "TANGENT",
|
||||
CurveType::Coincident => "COINCIDENT",
|
||||
CurveType::NearTangent => "NEAR_TANGENT",
|
||||
CurveType::RegionBoundary => "REGION_BOUNDARY",
|
||||
CurveType::Other(_) => "?",
|
||||
};
|
||||
let evaluated = |p: &[f64]| p.iter().any(|x| !x.is_nan());
|
||||
let on = |has: bool, uv: Vec<[f64; 2]>, at: Vec<V3>| -> (Vec<[f64; 2]>, Vec<V3>) {
|
||||
if !has {
|
||||
return (Vec::new(), Vec::new());
|
||||
}
|
||||
uv.into_iter().zip(at).filter(|(q, _)| evaluated(q)).unzip()
|
||||
};
|
||||
let (uv1, s1_at_uv) = on(c.has_uv_a, c.uv_a, c.a_at_uv);
|
||||
let (uv2, s2_at_uv) = on(c.has_uv_b, c.uv_b, c.b_at_uv);
|
||||
EngineCurve { curve_type: curve_type.into(), xyz: c.xyz.into_iter().filter(|p| evaluated(p)).collect(), uv1, uv2, s1_at_uv, s2_at_uv }
|
||||
})
|
||||
.collect();
|
||||
let params = |[t0, t1]: [f64; 2]| (0..RUNNER_SAMPLES).map(move |i| t0 + (t1 - t0) * (i as f64 / (RUNNER_SAMPLES - 1) as f64));
|
||||
let on = |c: &Option<Curve2>, s: &Surface| -> (Vec<[f64; 2]>, Vec<V3>) {
|
||||
let Some(c) = c else { return (Vec::new(), Vec::new()) };
|
||||
let Some(ivl) = c.interval() else { return (Vec::new(), Vec::new()) };
|
||||
params(ivl).filter_map(|t| c.eval(t)).filter_map(|q| s.evaluate(q[0], q[1]).ok().map(|p| (q, p))).unzip()
|
||||
};
|
||||
let mut curves = Vec::new();
|
||||
for c in r.curves {
|
||||
let curve_type = match c.curve_type {
|
||||
CurveType::None => "n/a",
|
||||
CurveType::Unknown => "UNKNOWN",
|
||||
CurveType::Touching => "TOUCHING",
|
||||
CurveType::Crossing => "CROSSING",
|
||||
CurveType::Tangent => "TANGENT",
|
||||
CurveType::Coincident => "COINCIDENT",
|
||||
CurveType::NearTangent => "NEAR_TANGENT",
|
||||
CurveType::RegionBoundary => "REGION_BOUNDARY",
|
||||
CurveType::Other(_) => "?",
|
||||
};
|
||||
let xyz = match CurveEval::new(&c.curve) {
|
||||
Ok(e) => params(c.interval).filter_map(|t| e.point(t)).map(|p| [p.x, p.y, p.z]).collect(),
|
||||
Err(e) => return Outcome::Crashed(format!("an intersection curve: {e}")),
|
||||
};
|
||||
let (uv1, s1_at_uv) = on(&c.uv_a, a);
|
||||
let (uv2, s2_at_uv) = on(&c.uv_b, b);
|
||||
curves.push(EngineCurve { curve_type: curve_type.into(), xyz, uv1, uv2, s1_at_uv, s2_at_uv });
|
||||
}
|
||||
Outcome::Ran(EngineResult { status: ssi_score::SM_SUCCESS, status_name: None, time_ms: ms, curves, points: Vec::new() })
|
||||
}
|
||||
|
||||
@@ -588,8 +596,8 @@ fn smlib_global_intersect_at_1e_6() {
|
||||
let a = surface(&case.surfaces[0]).unwrap_or_else(|e| panic!("{} surface 1: {e}", case.id));
|
||||
let b = surface(&case.surfaces[1]).unwrap_or_else(|e| panic!("{} surface 2: {e}", case.id));
|
||||
let t = std::time::Instant::now();
|
||||
let r = intersect(&a, &b, Method::Global, 1e-6, RUNNER_SAMPLES);
|
||||
runner_output(r, t.elapsed().as_secs_f64() * 1e3)
|
||||
let r = intersect(&a, &b, Method::Global, 1e-6);
|
||||
runner_output(r, &a, &b, t.elapsed().as_secs_f64() * 1e3)
|
||||
})
|
||||
.collect();
|
||||
let ran = t0.elapsed();
|
||||
|
||||
Reference in New Issue
Block a user