Skip to main content

geop_core_geometry/nurb_curve/
sweep.rs

1use crate::nurb_surface::NurbSurface3D;
2use geop_core_math::{scalars::Scalar, vector::Vector3};
3
4use super::NurbCurve;
5
6impl<S: Scalar> NurbCurve<S, 4> {
7    /// The ruled surface swept out by translating this curve along
8    /// `offset`: `S(t, s) = self.evaluate(t) + s * offset`. Degree
9    /// `(self.degree, 1)` — same knot vector as `self` in `t`, `s ∈ [0,
10    /// 1]` — control points `[cp_i, cp_i + offset]` row-major per original
11    /// control point `cp_i`.
12    pub fn sweep(&self, offset: Vector3<S>) -> NurbSurface3D<S> {
13        let shifted = self.translate(offset);
14        let mut control_points = Vec::with_capacity(self.control_points.len() * 2);
15        for i in 0..self.control_points.len() {
16            control_points.push(self.control_points[i]);
17            control_points.push(shifted.control_points[i]);
18        }
19        NurbSurface3D::try_new(
20            self.degree,
21            1,
22            control_points,
23            self.knot_vector.clone(),
24            vec![S::ZERO, S::ZERO, S::ONE, S::ONE],
25        )
26        .expect("sweeping a valid curve always yields a valid ruled surface")
27    }
28}
29
30#[cfg(test)]
31mod tests {
32    use geop_core_math::for_all_scalars;
33    use geop_core_math::{scalars::Scalar, vector::Vector3};
34
35    use super::super::NurbCurve3D;
36
37    fn check_sweep_matches_curve_plus_offset<S: Scalar>() {
38        let curve = NurbCurve3D::try_new(
39            1,
40            vec![
41                geop_core_math::vector::Vector4::from_array([S::ZERO, S::ZERO, S::ZERO, S::ONE]),
42                geop_core_math::vector::Vector4::from_array([S::ONE, S::ZERO, S::ZERO, S::ONE]),
43            ],
44            vec![S::ZERO, S::ZERO, S::ONE, S::ONE],
45        )
46        .unwrap();
47        let offset = Vector3::from_array([S::ZERO, S::ZERO, S::from_f64(2.0)]);
48        let surface = curve.sweep(offset);
49
50        for t in [0.0, 0.4, 1.0] {
51            let a = surface.evaluate(S::from_f64(t), S::ZERO).unwrap();
52            let b = curve.evaluate(S::from_f64(t)).unwrap();
53            assert!(a.could_be_equal(&b));
54
55            let a_top = surface.evaluate(S::from_f64(t), S::ONE).unwrap();
56            let b_top = curve.evaluate(S::from_f64(t)).unwrap().add(&offset);
57            assert!(a_top.could_be_equal(&b_top));
58        }
59    }
60    #[test]
61    fn sweep_matches_curve_plus_offset() {
62        for_all_scalars!(check_sweep_matches_curve_plus_offset);
63    }
64}