geop_core_geometry/nurb_curve/
join.rs1use geop_core_math::{
4 geop_error::{GeopError, GeopResult},
5 vector::Vector,
6};
7
8use super::NurbCurve;
9use crate::knot_insertion::is_clamped_at;
10
11impl<const D: usize> NurbCurve<D> {
12 pub fn joined(&self, next: &Self) -> GeopResult<Self> {
24 let (mut a, mut b) = (self.clone(), next.clone());
25 while a.degree < b.degree {
26 a = a.elevate_degree()?;
27 }
28 while b.degree < a.degree {
29 b = b.elevate_degree()?;
30 }
31 let p = a.degree;
32 if !is_clamped_at(&a.knot_vector, p, false) || !is_clamped_at(&b.knot_vector, p, true) {
33 return Err(GeopError::new(format!(
34 "NurbCurve::joined: the knot vectors {:?} and {:?} are not clamped where the curves meet",
35 a.knot_vector, b.knot_vector
36 )));
37 }
38 let last = *a.control_points.last().expect("a curve has control points");
39 let first = b.control_points[0];
40 let scale = last[D - 1].div(first[D - 1])?;
41 let scaled: Vec<Vector<D>> = (b.control_points.iter())
42 .map(|cp| cp.prod_scalar(scale))
43 .collect();
44 if !scaled[0].could_be_equal(&last) {
45 return Err(GeopError::new(format!(
46 "NurbCurve::joined: the curves do not meet: one ends at {last:?}, the other starts at {:?}",
47 scaled[0]
48 )));
49 }
50 let meet = Vector::from_array(std::array::from_fn(|k| last[k].intersect(scaled[0][k])));
51 let (_, a1) = a.domain();
52 let (b0, _) = b.domain();
53 let shift = a1.sub(b0);
54 let mut knots: Vec<_> = a.knot_vector[..a.knot_vector.len() - 1].to_vec();
55 knots.extend(b.knot_vector[p + 1..].iter().map(|k| k.add(shift)));
56 let mut points = a.control_points;
57 *points.last_mut().expect("a curve has control points") = meet;
58 points.extend_from_slice(&scaled[1..]);
59 NurbCurve::try_new(p, points, knots)
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use geop_core_math::{scalars::Scalar, vector::Vector3};
66
67 use crate::nurb_curve::NurbCurve3D;
68
69 fn p(x: f64, y: f64, z: f64) -> Vector3 {
70 Vector3::from_array([x, y, z].map(Scalar::from_f64))
71 }
72
73 #[test]
76 fn joins_two_lines() {
77 let a = NurbCurve3D::line(p(0., 0., 0.), p(1., 0., 0.)).unwrap();
78 let b = NurbCurve3D::line(p(1., 0., 0.), p(1., 2., 0.)).unwrap();
79 let joined = a.joined(&b).unwrap();
80 let (t0, t1) = joined.domain();
81 assert!(joined.evaluate(t0).unwrap().could_be_equal(&p(0., 0., 0.)));
82 assert!(joined.evaluate(t1).unwrap().could_be_equal(&p(1., 2., 0.)));
83 let (_, a1) = a.domain();
84 assert!(joined.evaluate(a1).unwrap().could_be_equal(&p(1., 0., 0.)));
85 }
86
87 #[test]
89 fn refuses_curves_apart() {
90 let a = NurbCurve3D::line(p(0., 0., 0.), p(1., 0., 0.)).unwrap();
91 let b = NurbCurve3D::line(p(2., 0., 0.), p(3., 0., 0.)).unwrap();
92 assert!(a.joined(&b).is_err());
93 }
94}