ranim_core/traits/transform/
scale.rs

1use std::cmp::Ordering;
2
3use glam::{DVec3, dvec3};
4use itertools::Itertools;
5
6use crate::{anchor::Aabb, traits::StrokeWidth};
7
8/// A hint for scaling the mobject.
9#[derive(Debug, Clone, Copy)]
10pub enum ScaleHint {
11    /// Scale the mobject's X axe
12    X(f64),
13    /// Scale the mobject's Y axe
14    Y(f64),
15    /// Scale the mobject's Z axe
16    Z(f64),
17    /// Scale the mobject's X axe, while other axes are scaled accordingly.
18    PorportionalX(f64),
19    /// Scale the mobject's Y axe, while other axes are scaled accordingly.
20    PorportionalY(f64),
21    /// Scale the mobject's Z axe, while other axes are scaled accordingly.
22    PorportionalZ(f64),
23}
24
25/// Scaling operations.
26///
27/// This trait is automatically implemented for [`DVec3`] and `[T]` where `T: ScaleTransform`.
28pub trait ScaleTransform {
29    /// Scale at the origin.
30    fn scale(&mut self, scale: DVec3) -> &mut Self;
31}
32
33impl ScaleTransform for DVec3 {
34    fn scale(&mut self, scale: DVec3) -> &mut Self {
35        *self *= scale;
36        self
37    }
38}
39
40impl<T: ScaleTransform> ScaleTransform for [T] {
41    fn scale(&mut self, scale: DVec3) -> &mut Self {
42        self.iter_mut().for_each(|x| {
43            x.scale(scale);
44        });
45        self
46    }
47}
48
49impl<T: ScaleTransform> ScaleTransform for Vec<T> {
50    fn scale(&mut self, scale: DVec3) -> &mut Self {
51        self.as_mut_slice().scale(scale);
52        self
53    }
54}
55
56/// Useful extensions for scaling operations.
57///
58/// This trait is implemented automatically for types that implement [`ScaleTransform`], you should not implement it yourself.
59pub trait ScaleTransformExt: ScaleTransform {
60    /// Calculate the scale ratio for a given hint.
61    ///
62    /// See [`ScaleHint`] for more details.
63    fn calc_scale_ratio(&self, hint: ScaleHint) -> DVec3
64    where
65        Self: Aabb,
66    {
67        let aabb_size = self.aabb_size();
68        match hint {
69            ScaleHint::X(v) => dvec3(v / (aabb_size.x), 1.0, 1.0),
70            ScaleHint::Y(v) => dvec3(1.0, v / (aabb_size.y), 1.0),
71            ScaleHint::Z(v) => dvec3(1.0, 1.0, v / aabb_size.z),
72            ScaleHint::PorportionalX(v) => DVec3::splat(v / aabb_size.x),
73            ScaleHint::PorportionalY(v) => DVec3::splat(v / aabb_size.y),
74            ScaleHint::PorportionalZ(v) => DVec3::splat(v / aabb_size.z),
75        }
76    }
77    /// Scale the item to a given hint (at origin).
78    ///
79    /// See [`ScaleHint`] for more details.
80    fn scale_to(&mut self, hint: ScaleHint) -> &mut Self
81    where
82        Self: Aabb,
83    {
84        self.scale(self.calc_scale_ratio(hint));
85        self
86    }
87    /// Scale the item to the minimum scale ratio of each axis from the given hints.
88    ///
89    /// See [`ScaleHint`] for more details.
90    fn scale_to_min(&mut self, hints: &[ScaleHint]) -> &mut Self
91    where
92        Self: Aabb,
93    {
94        let scale = hints
95            .iter()
96            .map(|hint| self.calc_scale_ratio(*hint))
97            .reduce(|a, b| a.min(b))
98            .unwrap_or(DVec3::ONE);
99        self.scale(scale);
100        self
101    }
102    /// Scale the item to the maximum scale ratio of each axis from the given hints.
103    ///
104    /// See [`ScaleHint`] for more details.
105    fn scale_to_max(&mut self, hints: &[ScaleHint]) -> &mut Self
106    where
107        Self: Aabb,
108    {
109        let scale = hints
110            .iter()
111            .map(|hint| self.calc_scale_ratio(*hint))
112            .reduce(|a, b| a.max(b))
113            .unwrap_or(DVec3::ONE);
114        self.scale(scale);
115        self
116    }
117}
118
119impl<T: ScaleTransform + ?Sized> ScaleTransformExt for T {}
120
121/// A trait for scaling operations with stroke width.
122pub trait ScaleTransformStrokeExt: ScaleTransform + StrokeWidth {
123    /// Scale the item with stroke width (at origin).
124    fn scale_with_stroke(&mut self, scale: DVec3) -> &mut Self {
125        self.scale(scale);
126
127        let scales = [scale.x, scale.y, scale.z];
128        let idx = scales
129            .iter()
130            .map(|x: &f64| if *x > 1.0 { *x } else { 1.0 / *x })
131            .position_max_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal))
132            .unwrap_or(0);
133        let scale = scales[idx];
134        self.apply_stroke_func(|widths| widths.iter_mut().for_each(|w| w.0 *= scale as f32));
135        self
136    }
137    /// Scale the item to a given hint with stroke width.
138    ///
139    /// See [`ScaleHint`] for more details.
140    fn scale_to_with_stroke(&mut self, hint: ScaleHint) -> &mut Self
141    where
142        Self: Aabb,
143    {
144        let scale = self.calc_scale_ratio(hint);
145        self.scale_with_stroke(scale)
146    }
147}
148
149impl<T: ScaleTransform + StrokeWidth + ?Sized> ScaleTransformStrokeExt for T {}