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}