1use crate::Point;
8
9use crate::scalar::{Scalar, SCALAR_NEARLY_ZERO};
10
11#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
12use crate::NoStdFloat;
13
14#[allow(missing_docs)]
20#[derive(Copy, Clone, PartialEq, Debug)]
21pub struct Transform {
22 pub sx: f32,
23 pub kx: f32,
24 pub ky: f32,
25 pub sy: f32,
26 pub tx: f32,
27 pub ty: f32,
28}
29
30impl Default for Transform {
31 fn default() -> Self {
32 Transform {
33 sx: 1.0,
34 kx: 0.0,
35 ky: 0.0,
36 sy: 1.0,
37 tx: 0.0,
38 ty: 0.0,
39 }
40 }
41}
42
43impl Transform {
44 pub fn identity() -> Self {
46 Transform::default()
47 }
48
49 pub fn from_row(sx: f32, ky: f32, kx: f32, sy: f32, tx: f32, ty: f32) -> Self {
53 Transform {
54 sx,
55 ky,
56 kx,
57 sy,
58 tx,
59 ty,
60 }
61 }
62
63 pub fn from_translate(tx: f32, ty: f32) -> Self {
65 Transform::from_row(1.0, 0.0, 0.0, 1.0, tx, ty)
66 }
67
68 pub fn from_scale(sx: f32, sy: f32) -> Self {
70 Transform::from_row(sx, 0.0, 0.0, sy, 0.0, 0.0)
71 }
72
73 pub fn from_skew(kx: f32, ky: f32) -> Self {
75 Transform::from_row(1.0, ky, kx, 1.0, 0.0, 0.0)
76 }
77
78 pub fn from_rotate(angle: f32) -> Self {
80 let v = angle.to_radians();
81 let a = v.cos();
82 let b = v.sin();
83 let c = -b;
84 let d = a;
85 Transform::from_row(a, b, c, d, 0.0, 0.0)
86 }
87
88 pub fn from_rotate_at(angle: f32, tx: f32, ty: f32) -> Self {
90 let mut ts = Self::default();
91 ts = ts.pre_translate(tx, ty);
92 ts = ts.pre_concat(Transform::from_rotate(angle));
93 ts = ts.pre_translate(-tx, -ty);
94 ts
95 }
96
97 pub fn is_finite(&self) -> bool {
99 self.sx.is_finite()
100 && self.ky.is_finite()
101 && self.kx.is_finite()
102 && self.sy.is_finite()
103 && self.tx.is_finite()
104 && self.ty.is_finite()
105 }
106
107 pub fn is_identity(&self) -> bool {
111 *self == Transform::default()
112 }
113
114 pub fn is_scale(&self) -> bool {
118 self.has_scale() && !self.has_skew() && !self.has_translate()
119 }
120
121 pub fn is_skew(&self) -> bool {
125 !self.has_scale() && self.has_skew() && !self.has_translate()
126 }
127
128 pub fn is_translate(&self) -> bool {
132 !self.has_scale() && !self.has_skew() && self.has_translate()
133 }
134
135 pub fn is_scale_translate(&self) -> bool {
139 (self.has_scale() || self.has_translate()) && !self.has_skew()
140 }
141
142 pub fn has_scale(&self) -> bool {
146 self.sx != 1.0 || self.sy != 1.0
147 }
148
149 pub fn has_skew(&self) -> bool {
153 self.kx != 0.0 || self.ky != 0.0
154 }
155
156 pub fn has_translate(&self) -> bool {
160 self.tx != 0.0 || self.ty != 0.0
161 }
162
163 #[must_use]
165 pub fn pre_scale(&self, sx: f32, sy: f32) -> Self {
166 self.pre_concat(Transform::from_scale(sx, sy))
167 }
168
169 #[must_use]
171 pub fn post_scale(&self, sx: f32, sy: f32) -> Self {
172 self.post_concat(Transform::from_scale(sx, sy))
173 }
174
175 #[must_use]
177 pub fn pre_translate(&self, tx: f32, ty: f32) -> Self {
178 self.pre_concat(Transform::from_translate(tx, ty))
179 }
180
181 #[must_use]
183 pub fn post_translate(&self, tx: f32, ty: f32) -> Self {
184 self.post_concat(Transform::from_translate(tx, ty))
185 }
186
187 #[must_use]
189 pub fn pre_concat(&self, other: Self) -> Self {
190 concat(*self, other)
191 }
192
193 #[must_use]
195 pub fn post_concat(&self, other: Self) -> Self {
196 concat(other, *self)
197 }
198
199 pub(crate) fn from_sin_cos(sin: f32, cos: f32) -> Self {
200 Transform::from_row(cos, sin, -sin, cos, 0.0, 0.0)
201 }
202
203 pub fn map_points(&self, points: &mut [Point]) {
205 if points.is_empty() {
206 return;
207 }
208
209 if self.is_identity() {
212 } else if self.is_translate() {
214 for p in points {
215 p.x += self.tx;
216 p.y += self.ty;
217 }
218 } else if self.is_scale_translate() {
219 for p in points {
220 p.x = p.x * self.sx + self.tx;
221 p.y = p.y * self.sy + self.ty;
222 }
223 } else {
224 for p in points {
225 let x = p.x * self.sx + p.y * self.kx + self.tx;
226 let y = p.x * self.ky + p.y * self.sy + self.ty;
227 p.x = x;
228 p.y = y;
229 }
230 }
231 }
232
233 pub fn invert(&self) -> Option<Self> {
235 if self.is_identity() {
237 return Some(*self);
238 }
239
240 invert(self)
241 }
242}
243
244fn invert(ts: &Transform) -> Option<Transform> {
245 debug_assert!(!ts.is_identity());
246
247 if ts.is_scale_translate() {
248 if ts.has_scale() {
249 let inv_x = ts.sx.invert();
250 let inv_y = ts.sy.invert();
251 Some(Transform::from_row(
252 inv_x,
253 0.0,
254 0.0,
255 inv_y,
256 -ts.tx * inv_x,
257 -ts.ty * inv_y,
258 ))
259 } else {
260 Some(Transform::from_translate(-ts.tx, -ts.ty))
262 }
263 } else {
264 let inv_det = inv_determinant(ts)?;
265 let inv_ts = compute_inv(ts, inv_det);
266
267 if inv_ts.is_finite() {
268 Some(inv_ts)
269 } else {
270 None
271 }
272 }
273}
274
275fn inv_determinant(ts: &Transform) -> Option<f64> {
276 let det = dcross(ts.sx as f64, ts.sy as f64, ts.kx as f64, ts.ky as f64);
277
278 let tolerance = SCALAR_NEARLY_ZERO * SCALAR_NEARLY_ZERO * SCALAR_NEARLY_ZERO;
282 if (det as f32).is_nearly_zero_within_tolerance(tolerance) {
283 None
284 } else {
285 Some(1.0 / det)
286 }
287}
288
289fn compute_inv(ts: &Transform, inv_det: f64) -> Transform {
290 Transform::from_row(
291 (ts.sy as f64 * inv_det) as f32,
292 (-ts.ky as f64 * inv_det) as f32,
293 (-ts.kx as f64 * inv_det) as f32,
294 (ts.sx as f64 * inv_det) as f32,
295 dcross_dscale(ts.kx, ts.ty, ts.sy, ts.tx, inv_det),
296 dcross_dscale(ts.ky, ts.tx, ts.sx, ts.ty, inv_det),
297 )
298}
299
300fn dcross(a: f64, b: f64, c: f64, d: f64) -> f64 {
301 a * b - c * d
302}
303
304fn dcross_dscale(a: f32, b: f32, c: f32, d: f32, scale: f64) -> f32 {
305 (dcross(a as f64, b as f64, c as f64, d as f64) * scale) as f32
306}
307
308fn concat(a: Transform, b: Transform) -> Transform {
309 if a.is_identity() {
310 b
311 } else if b.is_identity() {
312 a
313 } else if !a.has_skew() && !b.has_skew() {
314 Transform::from_row(
316 a.sx * b.sx,
317 0.0,
318 0.0,
319 a.sy * b.sy,
320 a.sx * b.tx + a.tx,
321 a.sy * b.ty + a.ty,
322 )
323 } else {
324 Transform::from_row(
325 mul_add_mul(a.sx, b.sx, a.kx, b.ky),
326 mul_add_mul(a.ky, b.sx, a.sy, b.ky),
327 mul_add_mul(a.sx, b.kx, a.kx, b.sy),
328 mul_add_mul(a.ky, b.kx, a.sy, b.sy),
329 mul_add_mul(a.sx, b.tx, a.kx, b.ty) + a.tx,
330 mul_add_mul(a.ky, b.tx, a.sy, b.ty) + a.ty,
331 )
332 }
333}
334
335fn mul_add_mul(a: f32, b: f32, c: f32, d: f32) -> f32 {
336 (f64::from(a) * f64::from(b) + f64::from(c) * f64::from(d)) as f32
337}
338
339#[cfg(test)]
340mod tests {
341 use super::*;
342
343 #[test]
344 fn transform() {
345 assert_eq!(
346 Transform::identity(),
347 Transform::from_row(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
348 );
349
350 assert_eq!(
351 Transform::from_scale(1.0, 2.0),
352 Transform::from_row(1.0, 0.0, 0.0, 2.0, 0.0, 0.0)
353 );
354
355 assert_eq!(
356 Transform::from_skew(2.0, 3.0),
357 Transform::from_row(1.0, 3.0, 2.0, 1.0, 0.0, 0.0)
358 );
359
360 assert_eq!(
361 Transform::from_translate(5.0, 6.0),
362 Transform::from_row(1.0, 0.0, 0.0, 1.0, 5.0, 6.0)
363 );
364
365 let ts = Transform::identity();
366 assert_eq!(ts.is_identity(), true);
367 assert_eq!(ts.is_scale(), false);
368 assert_eq!(ts.is_skew(), false);
369 assert_eq!(ts.is_translate(), false);
370 assert_eq!(ts.is_scale_translate(), false);
371 assert_eq!(ts.has_scale(), false);
372 assert_eq!(ts.has_skew(), false);
373 assert_eq!(ts.has_translate(), false);
374
375 let ts = Transform::from_scale(2.0, 3.0);
376 assert_eq!(ts.is_identity(), false);
377 assert_eq!(ts.is_scale(), true);
378 assert_eq!(ts.is_skew(), false);
379 assert_eq!(ts.is_translate(), false);
380 assert_eq!(ts.is_scale_translate(), true);
381 assert_eq!(ts.has_scale(), true);
382 assert_eq!(ts.has_skew(), false);
383 assert_eq!(ts.has_translate(), false);
384
385 let ts = Transform::from_skew(2.0, 3.0);
386 assert_eq!(ts.is_identity(), false);
387 assert_eq!(ts.is_scale(), false);
388 assert_eq!(ts.is_skew(), true);
389 assert_eq!(ts.is_translate(), false);
390 assert_eq!(ts.is_scale_translate(), false);
391 assert_eq!(ts.has_scale(), false);
392 assert_eq!(ts.has_skew(), true);
393 assert_eq!(ts.has_translate(), false);
394
395 let ts = Transform::from_translate(2.0, 3.0);
396 assert_eq!(ts.is_identity(), false);
397 assert_eq!(ts.is_scale(), false);
398 assert_eq!(ts.is_skew(), false);
399 assert_eq!(ts.is_translate(), true);
400 assert_eq!(ts.is_scale_translate(), true);
401 assert_eq!(ts.has_scale(), false);
402 assert_eq!(ts.has_skew(), false);
403 assert_eq!(ts.has_translate(), true);
404
405 let ts = Transform::from_row(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
406 assert_eq!(ts.is_identity(), false);
407 assert_eq!(ts.is_scale(), false);
408 assert_eq!(ts.is_skew(), false);
409 assert_eq!(ts.is_translate(), false);
410 assert_eq!(ts.is_scale_translate(), false);
411 assert_eq!(ts.has_scale(), true);
412 assert_eq!(ts.has_skew(), true);
413 assert_eq!(ts.has_translate(), true);
414
415 let ts = Transform::from_scale(1.0, 1.0);
416 assert_eq!(ts.has_scale(), false);
417
418 let ts = Transform::from_skew(0.0, 0.0);
419 assert_eq!(ts.has_skew(), false);
420
421 let ts = Transform::from_translate(0.0, 0.0);
422 assert_eq!(ts.has_translate(), false);
423 }
424
425 #[test]
426 fn concat() {
427 let mut ts = Transform::from_row(1.2, 3.4, -5.6, -7.8, 1.2, 3.4);
428 ts = ts.pre_scale(2.0, -4.0);
429 assert_eq!(ts, Transform::from_row(2.4, 6.8, 22.4, 31.2, 1.2, 3.4));
430
431 let mut ts = Transform::from_row(1.2, 3.4, -5.6, -7.8, 1.2, 3.4);
432 ts = ts.post_scale(2.0, -4.0);
433 assert_eq!(ts, Transform::from_row(2.4, -13.6, -11.2, 31.2, 2.4, -13.6));
434 }
435}