brep: Curve2 pcurve type and the truck pcurve entry point

The shared base for the schema 4 types and the truck pcurves: 2D B-spline
pcurves as SMLib keeps its UV trim curves, and the truck adapter's pcurve
function, not supported yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QWhMwdqJYQKQq8RCLSL1LT
This commit is contained in:
jyviko
2026-10-20 22:41:19 -04:00
parent 92d433ca96
commit ee5deffaa4
4 changed files with 86 additions and 0 deletions
+2
View File
@@ -3,7 +3,9 @@
//! Validation checks structure and resource budgets, not geometric equivalence or trust.
use serde::{Deserialize, Serialize};
mod curve2;
pub mod envelope;
pub use curve2::Curve2;
pub const VERSION: u32 = 3;
pub const MAX_ITEMS: usize = 1_000_000;
+67
View File
@@ -0,0 +1,67 @@
//! Pcurves: an edge in its face's surface parameters, (u, v). 2D B-splines only, as SMLib keeps
//! its UV trim curves (`SmBSplineCurve`); 2D lines and arcs are exact as degree-1 and rational
//! quadratic splines.
use serde::{Deserialize, Serialize};
use super::{Budget, Result, knots, numbers, require};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
pub enum Curve2 {
Spline {
knots: Vec<f64>,
points: Vec<[f64; 2]>,
},
/// Homogeneous `[u*w, v*w, w]` coefficients.
RationalSpline {
knots: Vec<f64>,
homogeneous: Vec<[f64; 3]>,
},
}
impl Curve2 {
/// The active parameter interval `[knots[degree], knots[count]]`.
pub fn interval(&self) -> Option<[f64; 2]> {
let (k, count) = match self {
Curve2::Spline { knots, points } => (knots, points.len()),
Curve2::RationalSpline { knots, homogeneous } => (knots, homogeneous.len()),
};
let degree = k.len().checked_sub(count.checked_add(1)?)?;
Some([*k.get(degree)?, *k.get(count)?])
}
}
#[allow(dead_code)] // called by the schema 4 validator
pub(super) fn curve2(c: &Curve2, budget: &mut Budget) -> Result<()> {
budget.add(1)?;
match c {
Curve2::Spline { knots: k, points } => {
knots(k, points.len(), budget)?;
budget.add(points.len())?;
numbers(points.iter().flatten().copied())
}
Curve2::RationalSpline { knots: k, homogeneous } => {
knots(k, homogeneous.len(), budget)?;
budget.add(homogeneous.len())?;
numbers(homogeneous.iter().flatten().copied())?;
require(homogeneous.iter().all(|p| p[2] >= 1e-12), "non-positive rational weight")
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pcurves_validate_and_round_trip() {
let line = Curve2::Spline { knots: vec![0., 0., 1., 1.], points: vec![[0., 0.], [1., 2.]] };
curve2(&line, &mut Budget(0)).unwrap();
assert_eq!(line.interval(), Some([0., 1.]));
assert_eq!(serde_json::from_str::<Curve2>(&serde_json::to_string(&line).unwrap()).unwrap(), line);
let bad = Curve2::RationalSpline { knots: vec![0., 0., 1., 1.], homogeneous: vec![[0., 0., 1.], [1., 2., 0.]] };
assert!(curve2(&bad, &mut Budget(0)).is_err());
let nan = Curve2::Spline { knots: vec![0., 0., 1., 1.], points: vec![[0., f64::NAN], [1., 2.]] };
assert!(curve2(&nan, &mut Budget(0)).is_err());
}
}
+2
View File
@@ -8,6 +8,8 @@ use std::sync::Arc;
mod adapter;
mod binary;
mod metadata;
#[allow(dead_code)]
mod pcurve;
mod preview;
pub use preview::native_preview_mesh;
+15
View File
@@ -0,0 +1,15 @@
//! Pcurves for truck faces, which keep none: each edge use's curve in its face's surface
//! parameters, on the parameter of the edge's own curve (as the adapter writes it), running in
//! that curve's direction. Exact where the edge lies on its surface in closed form, otherwise a
//! 2D B-spline fitted within the tolerance.
use super::Result;
use solidcraft_geom::brep as b;
use truck_modeling as mt;
/// For each of `face`'s boundary wires, in order, the pcurve of each of its edges, in order, in
/// the (u, v) of the face's surface as truck evaluates it (`ParametricSurface::subs`), within
/// `tolerance` mm when lifted onto the surface.
pub(super) fn pcurves(face: &mt::Face, tolerance: f64) -> Result<Vec<Vec<b::Curve2>>> {
let _ = (face, tolerance);
Err(crate::KernelError::Failed("not supported yet: pcurves of truck faces".into()))
}