ranim_core/components/
mod.rs

1use std::fmt::Debug;
2
3use derive_more::{Deref, DerefMut, From};
4
5use crate::{
6    prelude::{Alignable, Interpolatable},
7    utils::{math::interpolate_usize, resize_preserving_order},
8};
9
10/// A Vec of Pointwise data
11#[derive(Debug, PartialEq, Eq, Deref, DerefMut, From)]
12pub struct PointVec<T>(Vec<T>);
13
14impl<T: Interpolatable> Interpolatable for PointVec<T> {
15    fn lerp(&self, target: &Self, t: f64) -> Self {
16        Self(self.0.lerp(&target.0, t))
17    }
18}
19
20impl<T: Clone> Clone for PointVec<T> {
21    fn clone(&self) -> Self {
22        Self(self.0.clone())
23    }
24}
25
26impl<T: Component + Interpolatable> PointVec<T> {
27    /// Get a partial PointVec within a specified range.
28    ///
29    /// This will interpolate the values at the start and end indices, and then
30    /// return a new PointVec containing the interpolated values.
31    pub fn get_partial(&self, range: std::ops::Range<f64>) -> Self {
32        let max_idx = self.len() - 2;
33
34        let (start_index, start_residue) = interpolate_usize(0, max_idx, range.start);
35        let (end_index, end_residue) = interpolate_usize(0, max_idx, range.end);
36        // trace!("max_idx: {max_idx}, range: {:?}, start: {} {}, end: {} {}", range, start_index, start_residue, end_index, end_residue);
37        if start_index == end_index {
38            let start_v = self
39                .get(start_index)
40                .unwrap()
41                .lerp(self.get(start_index + 1).unwrap(), start_residue);
42            let end_v = self
43                .get(end_index)
44                .unwrap()
45                .lerp(self.get(end_index + 1).unwrap(), end_residue);
46            vec![start_v, end_v]
47        } else {
48            let start_v = self
49                .get(start_index)
50                .unwrap()
51                .lerp(self.get(start_index + 1).unwrap(), start_residue);
52            let end_v = self
53                .get(end_index)
54                .unwrap()
55                .lerp(self.get(end_index + 1).unwrap(), end_residue);
56
57            let mut partial = Vec::with_capacity(end_index - start_index + 1 + 2);
58            partial.push(start_v);
59            partial.extend_from_slice(self.get(start_index + 1..=end_index).unwrap());
60            partial.push(end_v);
61            partial
62        }
63        .into()
64    }
65}
66
67/// Point
68pub mod point;
69/// Rgba
70pub mod rgba;
71/// Vpoint
72pub mod vpoint;
73/// Width
74pub mod width;
75
76/// An component
77pub trait Component: Debug + Default + Clone + PartialEq {}
78
79impl<T: Debug + Default + Clone + PartialEq> Component for T {}
80
81/// Vec resizing utils
82pub trait VecResizeTrait {
83    /// Resize with default value
84    fn resize_with_default(&mut self, new_len: usize);
85    /// Resize with last element
86    fn resize_with_last(&mut self, new_len: usize);
87    /// Resize preserved order
88    fn resize_preserving_order(&mut self, new_len: usize);
89}
90
91impl<T: Component> VecResizeTrait for Vec<T> {
92    /// Resize with default value
93    fn resize_with_default(&mut self, new_len: usize) {
94        self.resize(new_len, Default::default());
95    }
96    /// Resize with last element
97    fn resize_with_last(&mut self, new_len: usize) {
98        let last = self.last().cloned().unwrap_or_default();
99        self.resize(new_len, last);
100    }
101    /// Resize preserved order
102    fn resize_preserving_order(&mut self, new_len: usize) {
103        *self = resize_preserving_order(self, new_len);
104    }
105}
106
107impl<T: Component> Alignable for PointVec<T> {
108    fn is_aligned(&self, other: &Self) -> bool {
109        self.len() == other.len()
110    }
111    fn align_with(&mut self, other: &mut Self) {
112        if self.len() == other.len() {
113            return;
114        }
115        if self.len() < other.len() {
116            self.resize_with_last(other.len());
117        } else {
118            other.resize_with_last(self.len());
119        }
120    }
121}
122
123// MARK: Test
124#[cfg(test)]
125mod test {
126    use glam::{DVec3, dvec3};
127
128    use crate::{
129        anchor::{Aabb, AabbPoint, Locate},
130        components::vpoint::VPointVec,
131        traits::{ScaleTransform, ShiftTransformExt},
132    };
133
134    #[test]
135    fn test_bounding_box() {
136        // 5 points = 2 bezier segments: [P0,P1,P2] and [P2,P3,P4]
137        // P3 == P2 signals a subpath break, so 2nd segment is a line from P2 to P4.
138        let points: VPointVec = VPointVec(vec![
139            dvec3(-100.0, -100.0, 0.0),
140            dvec3(-100.0, 100.0, 0.0),
141            dvec3(100.0, 100.0, 0.0),
142            dvec3(100.0, 100.0, 0.0),
143            dvec3(100.0, -200.0, 0.0),
144        ]);
145        assert_eq!(
146            points.aabb(),
147            [dvec3(-100.0, -200.0, 0.0), dvec3(100.0, 100.0, 0.0)]
148        );
149        assert_eq!(
150            dvec3(0.0, -50.0, 0.0),
151            AabbPoint(dvec3(0.0, 0.0, 0.0)).locate(&points)
152        );
153        assert_eq!(
154            dvec3(-100.0, -200.0, 0.0),
155            AabbPoint(dvec3(-1.0, -1.0, 0.0)).locate(&points)
156        );
157        assert_eq!(
158            dvec3(-100.0, 100.0, 0.0),
159            AabbPoint(dvec3(-1.0, 1.0, 0.0)).locate(&points)
160        );
161        assert_eq!(
162            dvec3(100.0, -200.0, 0.0),
163            AabbPoint(dvec3(1.0, -1.0, 0.0)).locate(&points)
164        );
165        assert_eq!(
166            dvec3(100.0, 100.0, 0.0),
167            AabbPoint(dvec3(1.0, 1.0, 0.0)).locate(&points)
168        );
169    }
170
171    #[test]
172    fn test_transform() {
173        let square = vec![
174            dvec3(-1.0, -1.0, 0.0),
175            dvec3(2.0, -2.0, 0.0),
176            dvec3(0.5, 1.0, 0.0),
177            dvec3(-3.0, 3.0, 0.0),
178            dvec3(4.0, 4.0, 0.0),
179        ];
180        let mut scale_origin = VPointVec(square.clone());
181        // Bezier-aware AABB center: ((-1+4)/2, (-1.25+4)/2, 0) = (1.5, 1.375, 0)
182        assert_eq!(
183            AabbPoint(DVec3::ZERO).locate(&scale_origin),
184            dvec3(1.5, 1.375, 0.0)
185        );
186        scale_origin.with_origin(AabbPoint::CENTER, |x| {
187            x.scale(DVec3::splat(3.0));
188        });
189
190        let ans = VPointVec(vec![
191            dvec3(-6.0, -5.75, 0.0),
192            dvec3(3.0, -8.75, 0.0),
193            dvec3(-1.5, 0.25, 0.0),
194            dvec3(-12.0, 6.25, 0.0),
195            dvec3(9.0, 9.25, 0.0),
196        ]);
197        assert_eq!(scale_origin, ans);
198    }
199}