fj_math/
poly_chain.rs

1use crate::{Point, Segment};
2
3/// A polygonal chain
4///
5/// The dimensionality of the polygonal chain is defined by the const generic
6/// `D` parameter.
7#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
8#[repr(C)]
9pub struct PolyChain<const D: usize> {
10    points: Vec<Point<D>>,
11}
12
13impl<const D: usize> PolyChain<D> {
14    /// Create an empty `PolyChain`
15    pub fn new() -> Self {
16        Self::default()
17    }
18
19    /// Construct a polygonal chain from a number of points
20    pub fn from_points(
21        points: impl IntoIterator<Item = impl Into<Point<D>>>,
22    ) -> Self {
23        let points = points.into_iter().map(Into::into).collect::<Vec<_>>();
24
25        // Validate that we don't have any neighboring points that are the same.
26        // This doesn't ensure that the `PolyChain` is fully valid, but it's
27        // better than nothing.
28        for points in points.windows(2) {
29            // Can't panic, as we passed `2` to `windows`.
30            //
31            // Can be cleaned up, once `array_windows` is stable"
32            // https://doc.rust-lang.org/std/primitive.slice.html#method.array_windows
33            let [a, b] = [&points[0], &points[1]];
34
35            assert_ne!(a, b, "Polygonal chain has duplicate point");
36        }
37
38        Self { points }
39    }
40
41    /// Access the segments of the polygonal chain
42    pub fn segments(&self) -> Vec<Segment<D>> {
43        let mut segments = Vec::new();
44
45        for points in self.points.windows(2) {
46            // Can't panic, as we passed `2` to `windows`. Can be cleaned up,
47            // once `array_windows` is stable.
48            let points = [points[0], points[1]];
49
50            let segment = Segment::from_points(points);
51            segments.push(segment);
52        }
53
54        segments
55    }
56
57    /// Close the polygonal chain
58    ///
59    /// Adds the first point of the chain as the last, closing the chain. This
60    /// method does not check whether the `PolyChain` is already closed.
61    pub fn close(mut self) -> Self {
62        if let Some(&point) = self.points.first() {
63            self.points.push(point);
64        }
65
66        self
67    }
68
69    /// Reverse the order of points in the `PolyChain`
70    pub fn reverse(mut self) -> Self {
71        self.points.reverse();
72        self
73    }
74}
75
76impl<P, Ps, const D: usize> From<Ps> for PolyChain<D>
77where
78    P: Into<Point<D>>,
79    Ps: IntoIterator<Item = P>,
80{
81    fn from(points: Ps) -> Self {
82        Self::from_points(points)
83    }
84}