Skip to main content

geop_core_geometry/nurb_curve/
reverse.rs

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