Skip to main content

ranim_core/traits/transform/
scale.rs

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