Skip to main content

geop_core_geometry/nurb_curve/
reverse.rs

1use geop_core_math::scalars::Scalar;
2
3use super::NurbCurve;
4
5impl<S: Scalar, const D: usize> NurbCurve<S, D> {
6    /// This curve traversed in the opposite direction: same domain, same
7    /// shape, but `reverse().evaluate(t) == evaluate(t_min + t_max - t)`.
8    /// Reverses the control points and complements the knot vector
9    /// (`u -> t_min + t_max - u`, then reversed) — the standard B-spline
10    /// reversal construction.
11    pub fn reverse(&self) -> Self {
12        let (t_min, t_max) = self.domain();
13        let sum = t_min.add(t_max);
14        let control_points = self.control_points.iter().rev().copied().collect();
15        let knot_vector = self.knot_vector.iter().rev().map(|&u| sum.sub(u)).collect();
16        Self {
17            degree: self.degree,
18            control_points,
19            knot_vector,
20            // Same control points, just reordered — the bounding box they
21            // enclose is unchanged.
22            aabb: self.aabb,
23        }
24    }
25}
26
27impl<S: Scalar> NurbCurve<S, 3> {
28    /// This 2-D curve mirrored across the diagonal `x = y`: every point
29    /// `(x, y)` becomes `(y, x)`, with the same parametrization. Mirroring
30    /// flips orientation, so a counter-clockwise loop becomes clockwise.
31    pub fn swap_xy(&self) -> Self {
32        let control_points: Vec<_> = self
33            .control_points
34            .iter()
35            .map(|cp| geop_core_math::vector::Vector3::from_array([cp[1], cp[0], cp[2]]))
36            .collect();
37        let aabb = crate::aabb::compute_aabb(&control_points);
38        Self {
39            degree: self.degree,
40            control_points,
41            knot_vector: self.knot_vector.clone(),
42            aabb,
43        }
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use geop_core_math::for_all_scalars;
50    use geop_core_math::{scalars::Scalar, vector::Vector2};
51
52    use super::super::NurbCurve2D;
53
54    fn check_reverse_of_line_evaluates_swapped<S: Scalar>() {
55        let p0 = Vector2::from_array([S::ZERO, S::ZERO]);
56        let p1 = Vector2::from_array([S::ONE, S::from_f64(2.0)]);
57        let curve = NurbCurve2D::try_new(
58            1,
59            vec![
60                geop_core_math::vector::Vector3::from_array([p0[0], p0[1], S::ONE]),
61                geop_core_math::vector::Vector3::from_array([p1[0], p1[1], S::ONE]),
62            ],
63            vec![S::ZERO, S::ZERO, S::ONE, S::ONE],
64        )
65        .unwrap();
66        let reversed = curve.reverse();
67        let (r0, r1) = reversed.domain();
68        let (c0, c1) = curve.domain();
69        assert!(r0.could_be_equal(c0) && r1.could_be_equal(c1));
70        let a = reversed.evaluate(S::ZERO).unwrap();
71        let b = curve.evaluate(S::ONE).unwrap();
72        assert!(a.could_be_equal(&b));
73        let a = reversed.evaluate(S::ONE).unwrap();
74        let b = curve.evaluate(S::ZERO).unwrap();
75        assert!(a.could_be_equal(&b));
76    }
77    #[test]
78    fn reverse_of_line_evaluates_swapped() {
79        for_all_scalars!(check_reverse_of_line_evaluates_swapped);
80    }
81}