ferroui_base/media/transformation/
transform_operation.rs1use crate::media::transformation::InterpolationUtilities;
2use crate::Matrix;
3
4#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
6#[repr(i32)]
7pub enum OperationType {
8 #[default]
9 Translate = 0,
10 Rotate = 1,
11 Scale = 2,
12 Skew = 3,
13 Matrix = 4,
14 Identity = 5,
15}
16
17#[derive(Clone, Copy, Debug, Default, PartialEq)]
19pub struct SkewLayout {
20 pub x: f64,
21 pub y: f64,
22}
23
24#[derive(Clone, Copy, Debug, Default, PartialEq)]
26pub struct ScaleLayout {
27 pub x: f64,
28 pub y: f64,
29}
30
31#[derive(Clone, Copy, Debug, Default, PartialEq)]
33pub struct TranslateLayout {
34 pub x: f64,
35 pub y: f64,
36}
37
38#[derive(Clone, Copy, Debug, Default, PartialEq)]
40pub struct RotateLayout {
41 pub angle: f64,
42}
43
44#[derive(Clone, Copy, Debug, Default, PartialEq)]
49pub struct DataLayout {
50 first: f64,
51 second: f64,
52}
53
54impl DataLayout {
55 #[inline]
56 pub fn skew(&self) -> SkewLayout {
57 SkewLayout { x: self.first, y: self.second }
58 }
59
60 #[inline]
61 pub fn set_skew(&mut self, x: f64, y: f64) {
62 self.first = x;
63 self.second = y;
64 }
65
66 #[inline]
67 pub fn scale(&self) -> ScaleLayout {
68 ScaleLayout { x: self.first, y: self.second }
69 }
70
71 #[inline]
72 pub fn set_scale(&mut self, x: f64, y: f64) {
73 self.first = x;
74 self.second = y;
75 }
76
77 #[inline]
78 pub fn translate(&self) -> TranslateLayout {
79 TranslateLayout { x: self.first, y: self.second }
80 }
81
82 #[inline]
83 pub fn set_translate(&mut self, x: f64, y: f64) {
84 self.first = x;
85 self.second = y;
86 }
87
88 #[inline]
89 pub fn rotate(&self) -> RotateLayout {
90 RotateLayout { angle: self.first }
91 }
92
93 #[inline]
94 pub fn set_rotate(&mut self, angle: f64) {
95 self.first = angle;
96 }
97}
98
99#[derive(Clone, Copy, Debug, Default, PartialEq)]
105pub struct TransformOperation {
106 pub type_: OperationType,
107 pub matrix: Matrix,
108 pub data: DataLayout,
109}
110
111impl TransformOperation {
112 #[inline]
114 pub fn is_identity(&self) -> bool {
115 self.matrix.is_identity()
116 }
117
118 pub fn bake(&mut self) {
120 self.matrix = Matrix::IDENTITY;
121
122 match self.type_ {
123 OperationType::Translate => {
124 self.matrix = Matrix::create_translation(self.data.translate().x, self.data.translate().y);
125 }
126 OperationType::Rotate => {
127 self.matrix = Matrix::create_rotation(self.data.rotate().angle);
128 }
129 OperationType::Scale => {
130 self.matrix = Matrix::create_scale(self.data.scale().x, self.data.scale().y);
131 }
132 OperationType::Skew => {
133 self.matrix = Matrix::create_skew(self.data.skew().x, self.data.skew().y);
134 }
135 OperationType::Matrix | OperationType::Identity => {}
136 }
137 }
138
139 pub fn identity() -> TransformOperation {
141 TransformOperation { matrix: Matrix::IDENTITY, type_: OperationType::Identity, data: DataLayout::default() }
142 }
143
144 pub fn try_interpolate(
147 from: Option<TransformOperation>,
148 to: Option<TransformOperation>,
149 progress: f64,
150 result: &mut TransformOperation,
151 ) -> bool {
152 let from_identity = Self::is_operation_identity(&from);
153 let to_identity = Self::is_operation_identity(&to);
154
155 if from_identity && to_identity {
156 result.matrix = Matrix::IDENTITY;
157 return true;
158 }
159
160 let from_value = match from {
161 Some(from) if !from_identity => from,
162 _ => Self::identity(),
163 };
164 let to_value = match to {
165 Some(to) if !to_identity => to,
166 _ => Self::identity(),
167 };
168
169 let interpolation_type = if to_identity { from_value.type_ } else { to_value.type_ };
170 result.type_ = interpolation_type;
171
172 match interpolation_type {
173 OperationType::Translate => {
174 let from_x = if from_identity { 0.0 } else { from_value.data.translate().x };
175 let from_y = if from_identity { 0.0 } else { from_value.data.translate().y };
176 let to_x = if to_identity { 0.0 } else { to_value.data.translate().x };
177 let to_y = if to_identity { 0.0 } else { to_value.data.translate().y };
178
179 result.data.set_translate(
180 InterpolationUtilities::interpolate_scalars(from_x, to_x, progress),
181 InterpolationUtilities::interpolate_scalars(from_y, to_y, progress),
182 );
183 result.bake();
184 }
185 OperationType::Rotate => {
186 let from_angle = if from_identity { 0.0 } else { from_value.data.rotate().angle };
187 let to_angle = if to_identity { 0.0 } else { to_value.data.rotate().angle };
188
189 result.data.set_rotate(InterpolationUtilities::interpolate_scalars(from_angle, to_angle, progress));
190 result.bake();
191 }
192 OperationType::Scale => {
193 let from_x = if from_identity { 1.0 } else { from_value.data.scale().x };
194 let from_y = if from_identity { 1.0 } else { from_value.data.scale().y };
195 let to_x = if to_identity { 1.0 } else { to_value.data.scale().x };
196 let to_y = if to_identity { 1.0 } else { to_value.data.scale().y };
197
198 result.data.set_scale(
199 InterpolationUtilities::interpolate_scalars(from_x, to_x, progress),
200 InterpolationUtilities::interpolate_scalars(from_y, to_y, progress),
201 );
202 result.bake();
203 }
204 OperationType::Skew => {
205 let from_x = if from_identity { 0.0 } else { from_value.data.skew().x };
206 let from_y = if from_identity { 0.0 } else { from_value.data.skew().y };
207 let to_x = if to_identity { 0.0 } else { to_value.data.skew().x };
208 let to_y = if to_identity { 0.0 } else { to_value.data.skew().y };
209
210 result.data.set_skew(
211 InterpolationUtilities::interpolate_scalars(from_x, to_x, progress),
212 InterpolationUtilities::interpolate_scalars(from_y, to_y, progress),
213 );
214 result.bake();
215 }
216 OperationType::Matrix => {
217 let from_matrix = if from_identity { Matrix::IDENTITY } else { from_value.matrix };
218 let to_matrix = if to_identity { Matrix::IDENTITY } else { to_value.matrix };
219
220 let (Some(from_decomposed), Some(to_decomposed)) =
221 (Matrix::try_decompose_transform(from_matrix), Matrix::try_decompose_transform(to_matrix))
222 else {
223 return false;
224 };
225
226 let interpolated = InterpolationUtilities::interpolate_decomposed_transforms(
227 &from_decomposed,
228 &to_decomposed,
229 progress,
230 );
231 result.matrix = InterpolationUtilities::compose_transform(interpolated);
232 }
233 OperationType::Identity => {
234 result.matrix = Matrix::IDENTITY;
235 }
236 }
237
238 true
239 }
240
241 fn is_operation_identity(operation: &Option<TransformOperation>) -> bool {
242 match operation {
243 Some(operation) => operation.is_identity(),
244 None => true,
245 }
246 }
247}