1use super::brep::{cross, dot, lin, norm, scale, sub, Frame, P3};
11use super::tagged::Tree;
12
13const MAX_KNOTS: usize = 10_000;
15
16#[derive(Debug, Clone)]
18pub struct Spline {
19 pub t: Vec<f64>,
20 pub p: Vec<P3>,
21 pub d: Vec<P3>,
23}
24
25fn spline_derivs(t: &[f64], p: &[P3], d0: P3, d1: P3) -> Vec<P3> {
28 let n = t.len();
29 match n {
30 0 => return Vec::new(),
31 1 => return vec![d1],
32 2 => return vec![d0, d1],
33 _ => {}
34 }
35 let h: Vec<f64> = t.iter().zip(t.iter().skip(1)).map(|(a, b)| b - a).collect();
36 let m = n - 2;
37 let hk = |i: usize| h.get(i).copied().unwrap_or(1.0);
38 let pk = |i: usize| p.get(i).copied().unwrap_or_default();
39 let mut rows: Vec<(f64, f64, f64, P3)> = Vec::with_capacity(m);
42 for k in 0..m {
43 let i = k + 1;
44 let (h0, h1) = (hk(i - 1), hk(i));
45 let mut rhs = scale(
46 3.0,
47 lin(
48 scale(h1 / h0, sub(pk(i), pk(i - 1))),
49 h0 / h1,
50 sub(pk(i + 1), pk(i)),
51 ),
52 );
53 if k == 0 {
55 rhs = lin(rhs, -h1, d0);
56 }
57 if k + 1 == m {
58 rhs = lin(rhs, -h0, d1);
59 }
60 let lower = if k > 0 { h1 } else { 0.0 };
61 let upper = if k + 1 < m { h0 } else { 0.0 };
62 rows.push((lower, 2.0 * (h0 + h1), upper, rhs));
63 }
64 let mut fwd: Vec<(f64, P3)> = Vec::with_capacity(m);
66 let (mut c_prev, mut r_prev) = (0.0, [0.0; 3]);
67 for &(lower, diag, upper, rhs) in &rows {
68 let mut den = diag - lower * c_prev;
69 if den.abs() < 1e-300 {
70 den = 1e-300;
71 }
72 c_prev = upper / den;
73 r_prev = scale(1.0 / den, lin(rhs, -lower, r_prev));
74 fwd.push((c_prev, r_prev));
75 }
76 let mut x = vec![[0.0; 3]; m];
78 let mut next = [0.0; 3];
79 for (xk, &(ck, rk)) in x.iter_mut().zip(&fwd).rev() {
80 *xk = lin(rk, -ck, next);
81 next = *xk;
82 }
83 let mut d = Vec::with_capacity(n);
84 d.push(d0);
85 d.extend(x);
86 d.push(d1);
87 d
88}
89
90fn increasing(t: &[f64]) -> bool {
92 t.iter().zip(t.iter().skip(1)).all(|(a, b)| b > a)
93}
94
95fn hermite(s: f64) -> [f64; 4] {
96 let s2 = s * s;
97 let s3 = s2 * s;
98 [
99 2.0 * s3 - 3.0 * s2 + 1.0,
100 s3 - 2.0 * s2 + s,
101 -2.0 * s3 + 3.0 * s2,
102 s3 - s2,
103 ]
104}
105
106fn segment(t: &[f64], x: f64) -> usize {
108 if t.len() < 2 {
109 return 0;
110 }
111 let mut i = t.partition_point(|&k| k <= x);
112 i = i.saturating_sub(1);
113 i.min(t.len() - 2)
114}
115
116impl Spline {
117 pub fn new(t: Vec<f64>, p: Vec<P3>, d0: P3, d1: P3) -> Option<Spline> {
118 if t.len() != p.len() || t.is_empty() || t.len() > MAX_KNOTS {
119 return None;
120 }
121 if t.iter().chain(p.iter().flatten()).any(|x| !x.is_finite()) {
122 return None;
123 }
124 if !increasing(&t) {
125 return None;
126 }
127 let d = spline_derivs(&t, &p, d0, d1);
128 Some(Spline { t, p, d })
129 }
130
131 pub fn at(&self, x: f64) -> P3 {
132 let i = segment(&self.t, x);
133 let (t0, t1) = (
134 self.t.get(i).copied().unwrap_or(0.0),
135 self.t.get(i + 1).copied().unwrap_or(1.0),
136 );
137 let h = t1 - t0;
138 if h.abs() < 1e-300 || self.t.len() < 2 {
139 return self.p.first().copied().unwrap_or_default();
140 }
141 let s = (x - t0) / h;
142 let b = hermite(s);
143 let (p0, p1) = (
144 self.p.get(i).copied().unwrap_or_default(),
145 self.p.get(i + 1).copied().unwrap_or_default(),
146 );
147 let (d0, d1) = (
148 self.d.get(i).copied().unwrap_or_default(),
149 self.d.get(i + 1).copied().unwrap_or_default(),
150 );
151 let out = lin(scale(b[0], p0), b[1] * h, d0);
152 lin(lin(out, b[2], p1), b[3] * h, d1)
153 }
154
155 pub fn first_knot(&self) -> f64 {
156 self.t.first().copied().unwrap_or(0.0)
157 }
158
159 pub fn last_knot(&self) -> f64 {
160 self.t.last().copied().unwrap_or(1.0)
161 }
162}
163
164#[derive(Debug, Clone)]
167pub struct Grid {
168 tu: Vec<f64>,
169 tv: Vec<f64>,
170 nv: usize,
171 g: Vec<P3>,
172 pu: Vec<P3>,
173 pv: Vec<P3>,
174 puv: Vec<P3>,
175}
176
177impl Grid {
178 fn idx(&self, i: usize, j: usize) -> usize {
179 i * self.nv + j
180 }
181
182 pub fn eval(&self, u: f64, v: f64) -> P3 {
183 let i = segment(&self.tu, u);
184 let j = segment(&self.tv, v);
185 let (u0, u1) = (
186 self.tu.get(i).copied().unwrap_or(0.0),
187 self.tu.get(i + 1).copied().unwrap_or(1.0),
188 );
189 let (v0, v1) = (
190 self.tv.get(j).copied().unwrap_or(0.0),
191 self.tv.get(j + 1).copied().unwrap_or(1.0),
192 );
193 let (hu, hv) = (u1 - u0, v1 - v0);
194 if hu.abs() < 1e-300 || hv.abs() < 1e-300 {
195 return self.g.get(self.idx(i, j)).copied().unwrap_or_default();
196 }
197 let bu = hermite((u - u0) / hu);
198 let bv = hermite((v - v0) / hv);
199 let mut out = [0.0; 3];
200 let corners = [(0, 0), (0, 1), (1, 0), (1, 1)];
202 for (wa, (di, du)) in bu.into_iter().zip(corners) {
203 for (wb, (dj, dv)) in bv.into_iter().zip(corners) {
204 let k = self.idx(i + di, j + dj);
205 let q = match (du, dv) {
206 (0, 0) => self.g.get(k).copied().unwrap_or_default(),
207 (1, 0) => self.pu.get(k).copied().unwrap_or_default().map(|x| x * hu),
208 (0, 1) => self.pv.get(k).copied().unwrap_or_default().map(|x| x * hv),
209 _ => self
210 .puv
211 .get(k)
212 .copied()
213 .unwrap_or_default()
214 .map(|x| x * hu * hv),
215 };
216 out = lin(out, wa * wb, q);
217 }
218 }
219 out
220 }
221}
222
223#[derive(Debug, Clone)]
225pub enum Freeform {
226 Spline(Grid),
227 Fillet {
230 pnt: Spline,
231 ctr: Spline,
232 tan: Spline,
233 v0: f64,
234 v1: f64,
235 },
236 Ruled {
238 c1: Curve,
239 c2: Curve,
240 },
241}
242
243#[derive(Debug, Clone)]
245pub enum Curve {
246 Conic {
248 frame: Frame,
249 c1: f64,
250 c2: f64,
251 t0: f64,
252 t1: f64,
253 },
254 Spline(Spline),
255}
256
257impl Curve {
258 fn at(&self, u: f64) -> P3 {
259 match self {
260 Curve::Conic {
261 frame,
262 c1,
263 c2,
264 t0,
265 t1,
266 } => {
267 let t = t0 + u * (t1 - t0);
268 lin(lin(frame.o, c1 * t.cos(), frame.x), c2 * t.sin(), frame.y)
269 }
270 Curve::Spline(s) => s.at(s.first_knot() + u * (s.last_knot() - s.first_knot())),
271 }
272 }
273}
274
275impl Freeform {
276 pub fn eval(&self, u: f64, v: f64) -> P3 {
277 match self {
278 Freeform::Spline(g) => g.eval(u, v),
279 Freeform::Fillet {
280 pnt,
281 ctr,
282 tan,
283 v0,
284 v1,
285 } => {
286 let s = v0 + v * (v1 - v0);
287 let c = ctr.at(s);
288 let a = sub(pnt.at(s), c);
289 let r2 = dot(a, a);
290 let mut t = tan.at(s);
291 if r2 > 1e-300 {
292 t = lin(t, -dot(t, a) / r2, a);
293 }
294 let lt = norm(t);
295 if lt > 1e-300 {
296 t = t.map(|x| x / lt);
297 }
298 let b = cross(t, a);
299 let (cu, su) = (u.cos(), u.sin());
300 lin(lin(c, cu, a), su, b)
301 }
302 Freeform::Ruled { c1, c2 } => {
303 let a = c1.at(u);
304 let b = c2.at(u);
305 lin(a, v, sub(b, a))
306 }
307 }
308 }
309}
310
311fn p3s(tree: &Tree, node: usize) -> Vec<P3> {
312 tree.rows(node)
313 .into_iter()
314 .filter_map(|r| <P3>::try_from(r.as_slice()).ok())
315 .collect()
316}
317
318fn spline_of(tree: &Tree, node: usize) -> Option<Spline> {
319 let t = tree.dbls(tree.child(node, "params")?);
320 let p = p3s(tree, tree.child(node, "i_pnts")?);
321 let e = p3s(tree, tree.child(node, "end_tangts")?);
322 Spline::new(
323 t,
324 p,
325 e.first().copied().unwrap_or_default(),
326 e.get(1).copied().unwrap_or_default(),
327 )
328}
329
330pub fn read(tree: &Tree, kind: &str, prim: usize) -> Option<Freeform> {
332 match kind {
333 "splsrf" => {
334 let tu = tree.dbls(tree.child(prim, "u_params")?);
335 let tv = tree.dbls(tree.child(prim, "v_params")?);
336 let (nu, nv) = (tu.len(), tv.len());
337 if nu < 2
338 || nv < 2
339 || nu > MAX_KNOTS
340 || nv > MAX_KNOTS
341 || nu.checked_mul(nv)? > 4_000_000
342 {
343 return None;
344 }
345 let g = p3s(tree, tree.child(prim, "i_points")?);
346 let eu = p3s(tree, tree.child(prim, "end_u_tangts")?);
347 let ev = p3s(tree, tree.child(prim, "end_v_tangts")?);
348 let tw = p3s(tree, tree.child(prim, "end_uv_deriv")?);
349 if g.len() != nu * nv || eu.len() != 2 * nv || ev.len() != 2 * nu || tw.len() != 4 {
350 return None;
351 }
352 if !increasing(&tu) || !increasing(&tv) {
353 return None;
354 }
355 if g.iter()
356 .chain(eu.iter())
357 .chain(ev.iter())
358 .chain(tw.iter())
359 .flatten()
360 .any(|x| !x.is_finite())
361 {
362 return None;
363 }
364 let at = |v: &[P3], i: usize, j: usize| v.get(i * nv + j).copied().unwrap_or_default();
366 let get = |v: &[P3], k: usize| v.get(k).copied().unwrap_or_default();
367 let put = |v: &mut Vec<P3>, i: usize, j: usize, x: P3| {
368 if let Some(slot) = v.get_mut(i * nv + j) {
369 *slot = x;
370 }
371 };
372 let mut pu = vec![[0.0; 3]; nu * nv];
373 let mut pv = vec![[0.0; 3]; nu * nv];
374 let mut puv = vec![[0.0; 3]; nu * nv];
375 for j in 0..nv {
376 let col: Vec<P3> = (0..nu).map(|i| at(&g, i, j)).collect();
377 let d = spline_derivs(&tu, &col, get(&eu, j), get(&eu, nv + j));
378 for (i, x) in d.into_iter().enumerate() {
379 put(&mut pu, i, j, x);
380 }
381 }
382 for i in 0..nu {
383 let row: Vec<P3> = (0..nv).map(|j| at(&g, i, j)).collect();
384 let d = spline_derivs(&tv, &row, get(&ev, i), get(&ev, nu + i));
385 for (j, x) in d.into_iter().enumerate() {
386 put(&mut pv, i, j, x);
387 }
388 }
389 for (e, i) in [(0usize, 0usize), (1, nu - 1)] {
391 let row: Vec<P3> = (0..nv).map(|j| at(&pu, i, j)).collect();
392 let d = spline_derivs(&tv, &row, get(&tw, e * 2), get(&tw, e * 2 + 1));
393 for (j, x) in d.into_iter().enumerate() {
394 put(&mut puv, i, j, x);
395 }
396 }
397 for j in 0..nv {
398 let col: Vec<P3> = (0..nu).map(|i| at(&pv, i, j)).collect();
399 let d = spline_derivs(&tu, &col, at(&puv, 0, j), at(&puv, nu - 1, j));
400 for (i, x) in d.into_iter().enumerate() {
401 put(&mut puv, i, j, x);
402 }
403 }
404 Some(Freeform::Spline(Grid {
405 tu,
406 tv,
407 nv,
408 g,
409 pu,
410 pv,
411 puv,
412 }))
413 }
414 "fillet_srf" => {
415 let pnt = spline_of(tree, tree.child(prim, "pnt_spline")?)?;
416 let ctr = spline_of(tree, tree.child(prim, "ctr_spline")?)?;
417 let tan = spline_of(tree, tree.child(prim, "tan_spline")?)?;
418 let v0 = tree.dbl_at(prim, "par_v_0")?;
419 let v1 = tree.dbl_at(prim, "par_v_1")?;
420 if !(v0.is_finite() && v1.is_finite()) {
421 return None;
422 }
423 Some(Freeform::Fillet {
424 pnt,
425 ctr,
426 tan,
427 v0,
428 v1,
429 })
430 }
431 "ruled_srf" => {
432 let c1 = ruled_curve(tree, prim, "curve_1")?;
433 let c2 = ruled_curve(tree, prim, "curve_2")?;
434 Some(Freeform::Ruled { c1, c2 })
435 }
436 _ => None,
437 }
438}
439
440fn ruled_curve(tree: &Tree, prim: usize, key: &str) -> Option<Curve> {
441 let (name, node) = tree
442 .children_with_prefix(prim, &format!("{key}("))
443 .into_iter()
444 .next()?;
445 let ty = name
446 .trim_start_matches(key)
447 .trim_start_matches('(')
448 .trim_end_matches(')');
449 match ty {
450 "conic" => {
451 let rows = tree.rows(tree.child(node, "local_sys")?);
452 let frame = super::brep::frame_of(&rows)?;
453 let c1 = tree.dbl_at(node, "c1")?;
454 let c2 = tree.dbl_at(node, "c2")?;
455 let t0 = tree.dbl_at(node, "t0")?;
456 let t1 = tree.dbl_at(node, "t1")?;
457 if ![c1, c2, t0, t1].iter().all(|x| x.is_finite()) {
458 return None;
459 }
460 Some(Curve::Conic {
461 frame,
462 c1,
463 c2,
464 t0,
465 t1,
466 })
467 }
468 "spline" => Some(Curve::Spline(spline_of(tree, node)?)),
469 _ => None,
470 }
471}
472
473#[cfg(test)]
474mod tests {
475 use super::*;
476
477 #[test]
478 fn a_clamped_spline_interpolates_its_points_and_end_tangents() {
479 let t = vec![0.0, 1.0, 2.0, 3.0];
480 let p: Vec<P3> = t.iter().map(|&x| [x, x * x, 0.0]).collect();
481 let s = Spline::new(t.clone(), p.clone(), [1.0, 0.0, 0.0], [1.0, 6.0, 0.0]).unwrap();
482 for (x, q) in t.iter().zip(&p) {
483 let r = s.at(*x);
484 assert!(norm(sub(r, *q)) < 1e-12);
485 }
486 let r = s.at(1.5);
488 assert!((r[1] - 2.25).abs() < 1e-9, "{r:?}");
489 assert_eq!(segment(&t, -1.0), 0);
490 assert_eq!(segment(&t, 9.0), 2);
491 }
492}