batbox_lapp/
chain.rs

1use super::*;
2
3/// A polygonal chain connecting a vector of points in space
4#[derive(Debug, Clone)]
5pub struct Chain<T> {
6    /// List of points
7    pub vertices: Vec<vec2<T>>,
8}
9
10impl<T: Float> Chain<T> {
11    /// Construct a new chain
12    pub fn new(vertices: Vec<vec2<T>>) -> Self {
13        Self { vertices }
14    }
15
16    /// Returns the total length of the chain
17    pub fn length(&self) -> T
18    where
19        T: std::iter::Sum<T>,
20    {
21        self.vertices
22            .iter()
23            .zip(self.vertices.iter().skip(1))
24            .map(|(&a, &b)| (a - b).len())
25            .sum()
26    }
27
28    /// Returns a part of the chain. The full chain's range is `0.0..=1.0`.
29    ///
30    /// # Examples
31    /// ```
32    /// # use batbox_la::*;
33    /// # use batbox_lapp::*;
34    /// let chain = Chain::new(vec![vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 1.0)]);
35    /// assert_eq!(chain.clone().take_range_ratio(0.0..=1.0).vertices, chain.vertices);
36    /// ```
37    pub fn take_range_ratio(self, range: RangeInclusive<T>) -> Self
38    where
39        T: std::iter::Sum<T>,
40    {
41        let len = self.length();
42        let (start, end) = range.into_inner();
43        self.take_range_length(start * len..=end * len)
44    }
45
46    /// Returns a part of the chain. The full chain's range is `0.0..=chain.length()`.
47    ///
48    /// # Examples
49    /// ```
50    /// # use batbox_la::*;
51    /// # use batbox_lapp::*;
52    /// let chain = Chain::new(vec![vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 1.0)]);
53    /// assert_eq!(chain.clone().take_range_ratio(0.0..=chain.length()).vertices, chain.vertices);
54    /// ```
55    pub fn take_range_length(self, range: RangeInclusive<T>) -> Self {
56        let &(mut start_len) = range.start();
57        let &(mut end_len) = range.end();
58
59        let segments = self.vertices.iter().zip(self.vertices.iter().skip(1));
60
61        let mut start = self.vertices[0];
62        let mut start_i = 1;
63        for (i, (&a, &b)) in segments.enumerate() {
64            let len = (a - b).len();
65            start_len -= len;
66
67            if start_len < T::ZERO {
68                start = if len.approx_eq(&T::ZERO) {
69                    b
70                } else {
71                    b + (a - b) * (-start_len / len)
72                };
73                start_i = i + 1;
74                break;
75            }
76
77            end_len -= len;
78        }
79
80        let mut vertices = vec![start];
81
82        for i in start_i..self.vertices.len() {
83            let a = self.vertices[i - 1];
84            let b = self.vertices[i];
85            let len = (a - b).len();
86            end_len -= len;
87
88            if end_len <= T::ZERO {
89                let end = if len.approx_eq(&T::ZERO) {
90                    b
91                } else {
92                    b + (a - b) * (-end_len / len)
93                };
94                vertices.push(end);
95                break;
96            }
97
98            vertices.push(b);
99        }
100
101        Self { vertices }
102    }
103
104    /// Converts a chain into a vector of segments.
105    pub fn segments(&self) -> Vec<Segment<T>> {
106        let length = self.vertices.len();
107        if length < 2 {
108            return vec![];
109        }
110
111        let mut segments = Vec::with_capacity(length - 1);
112        let mut prev = self.vertices[0];
113        for &vertex in self.vertices.iter().skip(1) {
114            segments.push(Segment(prev, vertex));
115            prev = vertex;
116        }
117        segments
118    }
119}