1use crate::basics::is_equal_eps;
8use crate::trans_affine::{TransAffine, AFFINE_EPSILON};
9
10#[derive(Clone, Copy)]
27pub struct TransPerspective {
28 pub sx: f64,
29 pub shy: f64,
30 pub w0: f64,
31 pub shx: f64,
32 pub sy: f64,
33 pub w1: f64,
34 pub tx: f64,
35 pub ty: f64,
36 pub w2: f64,
37}
38
39impl TransPerspective {
40 pub fn new() -> Self {
42 Self {
43 sx: 1.0,
44 shy: 0.0,
45 w0: 0.0,
46 shx: 0.0,
47 sy: 1.0,
48 w1: 0.0,
49 tx: 0.0,
50 ty: 0.0,
51 w2: 1.0,
52 }
53 }
54
55 #[allow(clippy::too_many_arguments)]
57 pub fn new_from_values(
58 v0: f64,
59 v1: f64,
60 v2: f64,
61 v3: f64,
62 v4: f64,
63 v5: f64,
64 v6: f64,
65 v7: f64,
66 v8: f64,
67 ) -> Self {
68 Self {
69 sx: v0,
70 shy: v1,
71 w0: v2,
72 shx: v3,
73 sy: v4,
74 w1: v5,
75 tx: v6,
76 ty: v7,
77 w2: v8,
78 }
79 }
80
81 pub fn new_from_array(m: &[f64; 9]) -> Self {
83 Self {
84 sx: m[0],
85 shy: m[1],
86 w0: m[2],
87 shx: m[3],
88 sy: m[4],
89 w1: m[5],
90 tx: m[6],
91 ty: m[7],
92 w2: m[8],
93 }
94 }
95
96 pub fn new_from_affine(a: &TransAffine) -> Self {
98 Self {
99 sx: a.sx,
100 shy: a.shy,
101 w0: 0.0,
102 shx: a.shx,
103 sy: a.sy,
104 w1: 0.0,
105 tx: a.tx,
106 ty: a.ty,
107 w2: 1.0,
108 }
109 }
110
111 pub fn square_to_quad(&mut self, q: &[f64; 8]) -> bool {
117 let dx = q[0] - q[2] + q[4] - q[6];
118 let dy = q[1] - q[3] + q[5] - q[7];
119
120 if dx == 0.0 && dy == 0.0 {
121 self.sx = q[2] - q[0];
123 self.shy = q[3] - q[1];
124 self.w0 = 0.0;
125 self.shx = q[4] - q[2];
126 self.sy = q[5] - q[3];
127 self.w1 = 0.0;
128 self.tx = q[0];
129 self.ty = q[1];
130 self.w2 = 1.0;
131 } else {
132 let dx1 = q[2] - q[4];
133 let dy1 = q[3] - q[5];
134 let dx2 = q[6] - q[4];
135 let dy2 = q[7] - q[5];
136 let den = dx1 * dy2 - dx2 * dy1;
137 if den == 0.0 {
138 self.sx = 0.0;
140 self.shy = 0.0;
141 self.w0 = 0.0;
142 self.shx = 0.0;
143 self.sy = 0.0;
144 self.w1 = 0.0;
145 self.tx = 0.0;
146 self.ty = 0.0;
147 self.w2 = 0.0;
148 return false;
149 }
150 let u = (dx * dy2 - dy * dx2) / den;
152 let v = (dy * dx1 - dx * dy1) / den;
153 self.sx = q[2] - q[0] + u * q[2];
154 self.shy = q[3] - q[1] + u * q[3];
155 self.w0 = u;
156 self.shx = q[6] - q[0] + v * q[6];
157 self.sy = q[7] - q[1] + v * q[7];
158 self.w1 = v;
159 self.tx = q[0];
160 self.ty = q[1];
161 self.w2 = 1.0;
162 }
163 true
164 }
165
166 pub fn quad_to_square(&mut self, q: &[f64; 8]) -> bool {
168 if !self.square_to_quad(q) {
169 return false;
170 }
171 self.invert()
172 }
173
174 pub fn quad_to_quad(&mut self, qs: &[f64; 8], qd: &[f64; 8]) -> bool {
176 let mut p = TransPerspective::new();
177 if !self.quad_to_square(qs) {
178 return false;
179 }
180 if !p.square_to_quad(qd) {
181 return false;
182 }
183 self.multiply(&p);
184 true
185 }
186
187 pub fn rect_to_quad(&mut self, x1: f64, y1: f64, x2: f64, y2: f64, q: &[f64; 8]) -> bool {
189 let r = [x1, y1, x2, y1, x2, y2, x1, y2];
190 self.quad_to_quad(&r, q)
191 }
192
193 pub fn quad_to_rect(&mut self, q: &[f64; 8], x1: f64, y1: f64, x2: f64, y2: f64) -> bool {
195 let r = [x1, y1, x2, y1, x2, y2, x1, y2];
196 self.quad_to_quad(q, &r)
197 }
198
199 pub fn reset(&mut self) {
205 self.sx = 1.0;
206 self.shy = 0.0;
207 self.w0 = 0.0;
208 self.shx = 0.0;
209 self.sy = 1.0;
210 self.w1 = 0.0;
211 self.tx = 0.0;
212 self.ty = 0.0;
213 self.w2 = 1.0;
214 }
215
216 pub fn invert(&mut self) -> bool {
218 let d0 = self.sy * self.w2 - self.w1 * self.ty;
219 let d1 = self.w0 * self.ty - self.shy * self.w2;
220 let d2 = self.shy * self.w1 - self.w0 * self.sy;
221 let d = self.sx * d0 + self.shx * d1 + self.tx * d2;
222 if d == 0.0 {
223 self.sx = 0.0;
224 self.shy = 0.0;
225 self.w0 = 0.0;
226 self.shx = 0.0;
227 self.sy = 0.0;
228 self.w1 = 0.0;
229 self.tx = 0.0;
230 self.ty = 0.0;
231 self.w2 = 0.0;
232 return false;
233 }
234 let d = 1.0 / d;
235 let a = *self;
236 self.sx = d * d0;
237 self.shy = d * d1;
238 self.w0 = d * d2;
239 self.shx = d * (a.w1 * a.tx - a.shx * a.w2);
240 self.sy = d * (a.sx * a.w2 - a.w0 * a.tx);
241 self.w1 = d * (a.w0 * a.shx - a.sx * a.w1);
242 self.tx = d * (a.shx * a.ty - a.sy * a.tx);
243 self.ty = d * (a.shy * a.tx - a.sx * a.ty);
244 self.w2 = d * (a.sx * a.sy - a.shy * a.shx);
245 true
246 }
247
248 pub fn translate(&mut self, x: f64, y: f64) {
250 self.tx += x;
251 self.ty += y;
252 }
253
254 pub fn rotate(&mut self, a: f64) {
256 self.multiply_affine(&TransAffine::new_rotation(a));
257 }
258
259 pub fn scale_uniform(&mut self, s: f64) {
261 self.multiply_affine(&TransAffine::new_scaling_uniform(s));
262 }
263
264 pub fn scale_xy(&mut self, x: f64, y: f64) {
266 self.multiply_affine(&TransAffine::new_scaling(x, y));
267 }
268
269 pub fn multiply(&mut self, a: &TransPerspective) {
271 let b = *self;
272 self.sx = a.sx * b.sx + a.shx * b.shy + a.tx * b.w0;
273 self.shx = a.sx * b.shx + a.shx * b.sy + a.tx * b.w1;
274 self.tx = a.sx * b.tx + a.shx * b.ty + a.tx * b.w2;
275 self.shy = a.shy * b.sx + a.sy * b.shy + a.ty * b.w0;
276 self.sy = a.shy * b.shx + a.sy * b.sy + a.ty * b.w1;
277 self.ty = a.shy * b.tx + a.sy * b.ty + a.ty * b.w2;
278 self.w0 = a.w0 * b.sx + a.w1 * b.shy + a.w2 * b.w0;
279 self.w1 = a.w0 * b.shx + a.w1 * b.sy + a.w2 * b.w1;
280 self.w2 = a.w0 * b.tx + a.w1 * b.ty + a.w2 * b.w2;
281 }
282
283 pub fn premultiply(&mut self, b: &TransPerspective) {
285 let a = *self;
286 self.sx = a.sx * b.sx + a.shx * b.shy + a.tx * b.w0;
287 self.shx = a.sx * b.shx + a.shx * b.sy + a.tx * b.w1;
288 self.tx = a.sx * b.tx + a.shx * b.ty + a.tx * b.w2;
289 self.shy = a.shy * b.sx + a.sy * b.shy + a.ty * b.w0;
290 self.sy = a.shy * b.shx + a.sy * b.sy + a.ty * b.w1;
291 self.ty = a.shy * b.tx + a.sy * b.ty + a.ty * b.w2;
292 self.w0 = a.w0 * b.sx + a.w1 * b.shy + a.w2 * b.w0;
293 self.w1 = a.w0 * b.shx + a.w1 * b.sy + a.w2 * b.w1;
294 self.w2 = a.w0 * b.tx + a.w1 * b.ty + a.w2 * b.w2;
295 }
296
297 pub fn multiply_affine(&mut self, a: &TransAffine) {
299 let b = *self;
300 self.sx = a.sx * b.sx + a.shx * b.shy + a.tx * b.w0;
301 self.shx = a.sx * b.shx + a.shx * b.sy + a.tx * b.w1;
302 self.tx = a.sx * b.tx + a.shx * b.ty + a.tx * b.w2;
303 self.shy = a.shy * b.sx + a.sy * b.shy + a.ty * b.w0;
304 self.sy = a.shy * b.shx + a.sy * b.sy + a.ty * b.w1;
305 self.ty = a.shy * b.tx + a.sy * b.ty + a.ty * b.w2;
306 }
307
308 pub fn premultiply_affine(&mut self, b: &TransAffine) {
310 let a = *self;
311 self.sx = a.sx * b.sx + a.shx * b.shy;
312 self.shx = a.sx * b.shx + a.shx * b.sy;
313 self.tx = a.sx * b.tx + a.shx * b.ty + a.tx;
314 self.shy = a.shy * b.sx + a.sy * b.shy;
315 self.sy = a.shy * b.shx + a.sy * b.sy;
316 self.ty = a.shy * b.tx + a.sy * b.ty + a.ty;
317 self.w0 = a.w0 * b.sx + a.w1 * b.shy;
318 self.w1 = a.w0 * b.shx + a.w1 * b.sy;
319 self.w2 = a.w0 * b.tx + a.w1 * b.ty + a.w2;
320 }
321
322 pub fn multiply_inv(&mut self, m: &TransPerspective) {
324 let mut t = *m;
325 t.invert();
326 self.multiply(&t);
327 }
328
329 pub fn premultiply_inv(&mut self, m: &TransPerspective) {
331 let mut t = *m;
332 t.invert();
333 *self = t;
334 }
348
349 pub fn multiply_inv_affine(&mut self, m: &TransAffine) {
351 let mut t = *m;
352 t.invert();
353 self.multiply_affine(&t);
354 }
355
356 pub fn premultiply_inv_affine(&mut self, m: &TransAffine) {
358 let mut t = TransPerspective::new_from_affine(m);
359 t.invert();
360 let old_self = *self;
361 *self = t;
362 self.multiply(&old_self);
363 }
364
365 pub fn transform(&self, x: &mut f64, y: &mut f64) {
371 let tx = *x;
372 let ty = *y;
373 let m = 1.0 / (tx * self.w0 + ty * self.w1 + self.w2);
374 *x = m * (tx * self.sx + ty * self.shx + self.tx);
375 *y = m * (tx * self.shy + ty * self.sy + self.ty);
376 }
377
378 pub fn transform_affine(&self, x: &mut f64, y: &mut f64) {
380 let tmp = *x;
381 *x = tmp * self.sx + *y * self.shx + self.tx;
382 *y = tmp * self.shy + *y * self.sy + self.ty;
383 }
384
385 pub fn transform_2x2(&self, x: &mut f64, y: &mut f64) {
387 let tmp = *x;
388 *x = tmp * self.sx + *y * self.shx;
389 *y = tmp * self.shy + *y * self.sy;
390 }
391
392 pub fn inverse_transform(&self, x: &mut f64, y: &mut f64) {
394 let mut t = *self;
395 if t.invert() {
396 t.transform(x, y);
397 }
398 }
399
400 pub fn store_to(&self, m: &mut [f64; 9]) {
406 m[0] = self.sx;
407 m[1] = self.shy;
408 m[2] = self.w0;
409 m[3] = self.shx;
410 m[4] = self.sy;
411 m[5] = self.w1;
412 m[6] = self.tx;
413 m[7] = self.ty;
414 m[8] = self.w2;
415 }
416
417 pub fn load_from(&mut self, m: &[f64; 9]) {
419 self.sx = m[0];
420 self.shy = m[1];
421 self.w0 = m[2];
422 self.shx = m[3];
423 self.sy = m[4];
424 self.w1 = m[5];
425 self.tx = m[6];
426 self.ty = m[7];
427 self.w2 = m[8];
428 }
429
430 pub fn from_affine(&mut self, a: &TransAffine) {
432 self.sx = a.sx;
433 self.shy = a.shy;
434 self.w0 = 0.0;
435 self.shx = a.shx;
436 self.sy = a.sy;
437 self.w1 = 0.0;
438 self.tx = a.tx;
439 self.ty = a.ty;
440 self.w2 = 1.0;
441 }
442
443 pub fn determinant(&self) -> f64 {
449 self.sx * (self.sy * self.w2 - self.ty * self.w1)
450 + self.shx * (self.ty * self.w0 - self.shy * self.w2)
451 + self.tx * (self.shy * self.w1 - self.sy * self.w0)
452 }
453
454 pub fn determinant_reciprocal(&self) -> f64 {
456 1.0 / self.determinant()
457 }
458
459 pub fn is_valid(&self) -> bool {
461 self.is_valid_eps(AFFINE_EPSILON)
462 }
463
464 pub fn is_valid_eps(&self, epsilon: f64) -> bool {
466 self.sx.abs() > epsilon && self.sy.abs() > epsilon && self.w2.abs() > epsilon
467 }
468
469 pub fn is_identity(&self) -> bool {
471 self.is_identity_eps(AFFINE_EPSILON)
472 }
473
474 pub fn is_identity_eps(&self, epsilon: f64) -> bool {
476 is_equal_eps(self.sx, 1.0, epsilon)
477 && is_equal_eps(self.shy, 0.0, epsilon)
478 && is_equal_eps(self.w0, 0.0, epsilon)
479 && is_equal_eps(self.shx, 0.0, epsilon)
480 && is_equal_eps(self.sy, 1.0, epsilon)
481 && is_equal_eps(self.w1, 0.0, epsilon)
482 && is_equal_eps(self.tx, 0.0, epsilon)
483 && is_equal_eps(self.ty, 0.0, epsilon)
484 && is_equal_eps(self.w2, 1.0, epsilon)
485 }
486
487 pub fn is_equal(&self, m: &TransPerspective) -> bool {
489 self.is_equal_eps(m, AFFINE_EPSILON)
490 }
491
492 pub fn is_equal_eps(&self, m: &TransPerspective, epsilon: f64) -> bool {
494 is_equal_eps(self.sx, m.sx, epsilon)
495 && is_equal_eps(self.shy, m.shy, epsilon)
496 && is_equal_eps(self.w0, m.w0, epsilon)
497 && is_equal_eps(self.shx, m.shx, epsilon)
498 && is_equal_eps(self.sy, m.sy, epsilon)
499 && is_equal_eps(self.w1, m.w1, epsilon)
500 && is_equal_eps(self.tx, m.tx, epsilon)
501 && is_equal_eps(self.ty, m.ty, epsilon)
502 && is_equal_eps(self.w2, m.w2, epsilon)
503 }
504
505 pub fn scale(&self) -> f64 {
507 let x =
508 std::f64::consts::FRAC_1_SQRT_2 * self.sx + std::f64::consts::FRAC_1_SQRT_2 * self.shx;
509 let y =
510 std::f64::consts::FRAC_1_SQRT_2 * self.shy + std::f64::consts::FRAC_1_SQRT_2 * self.sy;
511 (x * x + y * y).sqrt()
512 }
513
514 pub fn rotation(&self) -> f64 {
516 let mut x1 = 0.0;
517 let mut y1 = 0.0;
518 let mut x2 = 1.0;
519 let mut y2 = 0.0;
520 self.transform(&mut x1, &mut y1);
521 self.transform(&mut x2, &mut y2);
522 (y2 - y1).atan2(x2 - x1)
523 }
524
525 pub fn translation(&self) -> (f64, f64) {
527 (self.tx, self.ty)
528 }
529
530 pub fn scaling(&self) -> (f64, f64) {
532 let mut x1 = 0.0;
533 let mut y1 = 0.0;
534 let mut x2 = 1.0;
535 let mut y2 = 1.0;
536 let mut t = *self;
537 t.multiply_affine(&TransAffine::new_rotation(-self.rotation()));
538 t.transform(&mut x1, &mut y1);
539 t.transform(&mut x2, &mut y2);
540 (x2 - x1, y2 - y1)
541 }
542
543 pub fn scaling_abs(&self) -> (f64, f64) {
545 (
546 (self.sx * self.sx + self.shx * self.shx).sqrt(),
547 (self.shy * self.shy + self.sy * self.sy).sqrt(),
548 )
549 }
550
551 pub fn begin(&self, x: f64, y: f64, step: f64) -> PerspectiveIteratorX {
557 PerspectiveIteratorX::new(x, y, step, self)
558 }
559}
560
561impl Default for TransPerspective {
562 fn default() -> Self {
563 Self::new()
564 }
565}
566
567pub struct PerspectiveIteratorX {
578 den: f64,
579 den_step: f64,
580 nom_x: f64,
581 nom_x_step: f64,
582 nom_y: f64,
583 nom_y_step: f64,
584 pub x: f64,
585 pub y: f64,
586}
587
588impl PerspectiveIteratorX {
589 pub fn default_new() -> Self {
591 Self {
592 den: 1.0,
593 den_step: 0.0,
594 nom_x: 0.0,
595 nom_x_step: 0.0,
596 nom_y: 0.0,
597 nom_y_step: 0.0,
598 x: 0.0,
599 y: 0.0,
600 }
601 }
602
603 fn new(px: f64, py: f64, step: f64, m: &TransPerspective) -> Self {
604 let den = px * m.w0 + py * m.w1 + m.w2;
605 let nom_x = px * m.sx + py * m.shx + m.tx;
606 let nom_y = px * m.shy + py * m.sy + m.ty;
607 Self {
608 den,
609 den_step: m.w0 * step,
610 nom_x,
611 nom_x_step: step * m.sx,
612 nom_y,
613 nom_y_step: step * m.shy,
614 x: nom_x / den,
615 y: nom_y / den,
616 }
617 }
618
619 pub fn next(&mut self) {
621 self.den += self.den_step;
622 self.nom_x += self.nom_x_step;
623 self.nom_y += self.nom_y_step;
624 let d = 1.0 / self.den;
625 self.x = self.nom_x * d;
626 self.y = self.nom_y * d;
627 }
628}
629
630#[cfg(test)]
635mod tests {
636 use super::*;
637
638 #[test]
639 fn test_identity() {
640 let t = TransPerspective::new();
641 assert!(t.is_identity());
642 assert!(t.is_valid());
643
644 let mut x = 5.0;
645 let mut y = 10.0;
646 t.transform(&mut x, &mut y);
647 assert!((x - 5.0).abs() < 1e-10);
648 assert!((y - 10.0).abs() < 1e-10);
649 }
650
651 #[test]
652 fn test_translate() {
653 let mut t = TransPerspective::new();
654 t.translate(10.0, 20.0);
655 let mut x = 0.0;
656 let mut y = 0.0;
657 t.transform(&mut x, &mut y);
658 assert!((x - 10.0).abs() < 1e-10);
659 assert!((y - 20.0).abs() < 1e-10);
660 }
661
662 #[test]
663 fn test_from_affine() {
664 let a = TransAffine::new_scaling(2.0, 3.0);
665 let t = TransPerspective::new_from_affine(&a);
666 let mut x = 5.0;
667 let mut y = 10.0;
668 t.transform(&mut x, &mut y);
669 assert!((x - 10.0).abs() < 1e-10);
670 assert!((y - 30.0).abs() < 1e-10);
671 }
672
673 #[test]
674 fn test_invert() {
675 let mut t = TransPerspective::new();
676 t.translate(10.0, 20.0);
677 assert!(t.invert());
678
679 let mut x = 10.0;
680 let mut y = 20.0;
681 t.transform(&mut x, &mut y);
682 assert!((x - 0.0).abs() < 1e-10);
683 assert!((y - 0.0).abs() < 1e-10);
684 }
685
686 #[test]
687 fn test_square_to_quad_parallelogram() {
688 let mut t = TransPerspective::new();
689 let q = [0.0, 0.0, 10.0, 0.0, 10.0, 10.0, 0.0, 10.0];
691 assert!(t.square_to_quad(&q));
692 assert_eq!(t.w0, 0.0);
693 assert_eq!(t.w1, 0.0);
694
695 let mut x = 0.5;
696 let mut y = 0.5;
697 t.transform(&mut x, &mut y);
698 assert!((x - 5.0).abs() < 1e-10);
699 assert!((y - 5.0).abs() < 1e-10);
700 }
701
702 #[test]
703 fn test_quad_to_quad() {
704 let mut t = TransPerspective::new();
705 let src = [0.0, 0.0, 10.0, 0.0, 10.0, 10.0, 0.0, 10.0];
706 let dst = [0.0, 0.0, 20.0, 0.0, 20.0, 20.0, 0.0, 20.0];
707 assert!(t.quad_to_quad(&src, &dst));
708
709 let mut x = 5.0;
710 let mut y = 5.0;
711 t.transform(&mut x, &mut y);
712 assert!((x - 10.0).abs() < 1e-8);
713 assert!((y - 10.0).abs() < 1e-8);
714 }
715
716 #[test]
717 fn test_rect_to_quad() {
718 let mut t = TransPerspective::new();
719 let q = [0.0, 0.0, 20.0, 0.0, 20.0, 20.0, 0.0, 20.0];
720 assert!(t.rect_to_quad(0.0, 0.0, 10.0, 10.0, &q));
721
722 let mut x = 5.0;
723 let mut y = 5.0;
724 t.transform(&mut x, &mut y);
725 assert!((x - 10.0).abs() < 1e-8);
726 assert!((y - 10.0).abs() < 1e-8);
727 }
728
729 #[test]
730 fn test_transform_inverse_round_trip() {
731 let mut t = TransPerspective::new();
732 t.translate(5.0, 10.0);
733 t.rotate(0.3);
734 t.scale_uniform(2.0);
735
736 let mut x = 3.0;
737 let mut y = 7.0;
738 t.transform(&mut x, &mut y);
739 t.inverse_transform(&mut x, &mut y);
740 assert!((x - 3.0).abs() < 1e-8);
741 assert!((y - 7.0).abs() < 1e-8);
742 }
743
744 #[test]
745 fn test_store_load() {
746 let t = TransPerspective::new_from_values(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
747 let mut arr = [0.0; 9];
748 t.store_to(&mut arr);
749 assert_eq!(arr, [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]);
750
751 let mut t2 = TransPerspective::new();
752 t2.load_from(&arr);
753 assert!(t.is_equal(&t2));
754 }
755
756 #[test]
757 fn test_determinant() {
758 let t = TransPerspective::new();
759 assert!((t.determinant() - 1.0).abs() < 1e-10);
760 }
761
762 #[test]
763 fn test_scale_query() {
764 let mut t = TransPerspective::new();
765 t.scale_uniform(3.0);
766 let s = t.scale();
767 assert!((s - 3.0).abs() < 1e-8);
768 }
769
770 #[test]
771 fn test_rotation_query() {
772 let mut t = TransPerspective::new();
773 t.rotate(0.5);
774 assert!((t.rotation() - 0.5).abs() < 1e-8);
775 }
776
777 #[test]
778 fn test_iterator() {
779 let mut t = TransPerspective::new();
780 t.scale_xy(2.0, 3.0);
781
782 let mut it = t.begin(0.0, 0.0, 1.0);
783 assert!((it.x - 0.0).abs() < 1e-10);
784 assert!((it.y - 0.0).abs() < 1e-10);
785
786 it.next();
787 assert!((it.x - 2.0).abs() < 1e-10);
788 assert!((it.y - 0.0).abs() < 1e-10);
789 }
790
791 #[test]
792 fn test_perspective_quad() {
793 let mut t = TransPerspective::new();
795 let q = [0.0, 0.0, 10.0, 1.0, 9.0, 10.0, 1.0, 9.0];
796 assert!(t.square_to_quad(&q));
797 assert!(t.w0 != 0.0 || t.w1 != 0.0);
799 }
800
801 #[test]
802 fn test_scaling_abs() {
803 let t = TransPerspective::new_from_affine(&TransAffine::new_scaling(3.0, 5.0));
804 let (sx, sy) = t.scaling_abs();
805 assert!((sx - 3.0).abs() < 1e-10);
806 assert!((sy - 5.0).abs() < 1e-10);
807 }
808}