1#[derive(Debug, Clone, Copy, PartialEq)]
9pub struct Point {
10 pub x: f64,
12 pub y: f64,
14}
15
16impl Point {
17 pub fn new(x: f64, y: f64) -> Self {
19 Self { x, y }
20 }
21
22 pub const ORIGIN: Self = Self { x: 0.0, y: 0.0 };
24}
25
26#[derive(Debug, Clone, Copy, PartialEq)]
28pub struct Size {
29 pub width: f64,
31 pub height: f64,
33}
34
35impl Size {
36 pub fn new(width: f64, height: f64) -> Self {
38 Self { width, height }
39 }
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Default)]
53pub struct Vector2D {
54 pub x: f64,
56 pub y: f64,
58}
59
60impl Vector2D {
61 pub fn new(x: f64, y: f64) -> Self {
63 Self { x, y }
64 }
65
66 pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
68
69 pub fn between(from: Point, to: Point) -> Self {
74 Self {
75 x: to.x - from.x,
76 y: to.y - from.y,
77 }
78 }
79
80 pub fn dot(self, other: Self) -> f64 {
84 self.x * other.x + self.y * other.y
85 }
86
87 pub fn length_squared(self) -> f64 {
92 self.x * self.x + self.y * self.y
93 }
94
95 pub fn length(self) -> f64 {
97 self.length_squared().sqrt()
98 }
99
100 pub fn normalize(self) -> Self {
104 let len = self.length();
105 if len == 0.0 {
106 Self::ZERO
107 } else {
108 Self {
109 x: self.x / len,
110 y: self.y / len,
111 }
112 }
113 }
114}
115
116impl From<(f64, f64)> for Vector2D {
117 fn from((x, y): (f64, f64)) -> Self {
118 Self { x, y }
119 }
120}
121
122impl From<Point> for Vector2D {
123 fn from(p: Point) -> Self {
125 Self { x: p.x, y: p.y }
126 }
127}
128
129impl std::ops::Add for Vector2D {
130 type Output = Self;
131 fn add(self, rhs: Self) -> Self {
132 Self {
133 x: self.x + rhs.x,
134 y: self.y + rhs.y,
135 }
136 }
137}
138
139impl std::ops::Sub for Vector2D {
140 type Output = Self;
141 fn sub(self, rhs: Self) -> Self {
142 Self {
143 x: self.x - rhs.x,
144 y: self.y - rhs.y,
145 }
146 }
147}
148
149impl std::ops::Neg for Vector2D {
150 type Output = Self;
151 fn neg(self) -> Self {
152 Self {
153 x: -self.x,
154 y: -self.y,
155 }
156 }
157}
158
159impl std::ops::Mul<f64> for Vector2D {
160 type Output = Self;
161 fn mul(self, scalar: f64) -> Self {
162 Self {
163 x: self.x * scalar,
164 y: self.y * scalar,
165 }
166 }
167}
168
169impl std::ops::Mul<Vector2D> for f64 {
170 type Output = Vector2D;
171 fn mul(self, v: Vector2D) -> Vector2D {
172 Vector2D {
173 x: self * v.x,
174 y: self * v.y,
175 }
176 }
177}
178
179impl std::ops::MulAssign<f64> for Vector2D {
180 fn mul_assign(&mut self, scalar: f64) {
181 self.x *= scalar;
182 self.y *= scalar;
183 }
184}
185
186impl std::ops::AddAssign for Vector2D {
187 fn add_assign(&mut self, rhs: Self) {
188 self.x += rhs.x;
189 self.y += rhs.y;
190 }
191}
192
193impl std::ops::SubAssign for Vector2D {
194 fn sub_assign(&mut self, rhs: Self) {
195 self.x -= rhs.x;
196 self.y -= rhs.y;
197 }
198}
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
205pub struct RectI {
206 pub left: i32,
207 pub bottom: i32,
208 pub right: i32,
209 pub top: i32,
210}
211
212impl RectI {
213 pub fn new(left: i32, bottom: i32, right: i32, top: i32) -> Self {
214 Self {
215 left,
216 bottom,
217 right,
218 top,
219 }
220 }
221
222 pub fn width(&self) -> i32 {
223 self.right - self.left
224 }
225
226 pub fn height(&self) -> i32 {
227 self.top - self.bottom
228 }
229
230 pub fn normalize(&self) -> Self {
232 RectI {
233 left: self.left.min(self.right),
234 bottom: self.bottom.min(self.top),
235 right: self.left.max(self.right),
236 top: self.bottom.max(self.top),
237 }
238 }
239
240 pub fn intersect(&self, other: &RectI) -> Option<RectI> {
242 let left = self.left.max(other.left);
243 let bottom = self.bottom.max(other.bottom);
244 let right = self.right.min(other.right);
245 let top = self.top.min(other.top);
246 if left < right && bottom < top {
247 Some(RectI {
248 left,
249 bottom,
250 right,
251 top,
252 })
253 } else {
254 None
255 }
256 }
257
258 pub fn contains_point(&self, x: i32, y: i32) -> bool {
260 x >= self.left && x < self.right && y >= self.bottom && y < self.top
261 }
262
263 #[inline]
267 pub fn contains(&self, x: i32, y: i32) -> bool {
268 self.contains_point(x, y)
269 }
270
271 pub fn is_empty(&self) -> bool {
275 self.right <= self.left || self.top <= self.bottom
276 }
277
278 pub fn valid(&self) -> bool {
286 (self.right as i64 - self.left as i64).abs() <= i32::MAX as i64
287 && (self.top as i64 - self.bottom as i64).abs() <= i32::MAX as i64
288 }
289
290 pub fn offset(&self, dx: i32, dy: i32) -> Self {
292 RectI {
293 left: self.left + dx,
294 bottom: self.bottom + dy,
295 right: self.right + dx,
296 top: self.top + dy,
297 }
298 }
299}
300
301#[derive(Debug, Clone, Copy, PartialEq)]
307pub struct Rect {
308 pub left: f64,
310 pub bottom: f64,
312 pub right: f64,
314 pub top: f64,
316}
317
318impl Rect {
319 pub fn new(left: f64, bottom: f64, right: f64, top: f64) -> Self {
321 Self {
322 left,
323 bottom,
324 right,
325 top,
326 }
327 }
328
329 pub fn width(&self) -> f64 {
331 self.right - self.left
332 }
333
334 pub fn height(&self) -> f64 {
336 self.top - self.bottom
337 }
338
339 pub fn size(&self) -> Size {
341 Size::new(self.width(), self.height())
342 }
343
344 pub fn is_empty(&self) -> bool {
351 self.left >= self.right || self.bottom >= self.top
352 }
353
354 pub fn contains(&self, point: Point) -> bool {
359 let left = self.left.min(self.right);
360 let right = self.left.max(self.right);
361 let bottom = self.bottom.min(self.top);
362 let top = self.bottom.max(self.top);
363 point.x >= left && point.x <= right && point.y >= bottom && point.y <= top
364 }
365
366 pub fn contains_rect(&self, other: &Rect) -> bool {
371 let n1 = self.normalize();
372 let n2 = other.normalize();
373 n2.left >= n1.left && n2.right <= n1.right && n2.bottom >= n1.bottom && n2.top <= n1.top
374 }
375
376 pub fn normalize(&self) -> Self {
378 Self {
379 left: self.left.min(self.right),
380 bottom: self.bottom.min(self.top),
381 right: self.left.max(self.right),
382 top: self.bottom.max(self.top),
383 }
384 }
385
386 pub fn intersect(&self, other: &Rect) -> Option<Rect> {
390 let left = self.left.max(other.left);
391 let bottom = self.bottom.max(other.bottom);
392 let right = self.right.min(other.right);
393 let top = self.top.min(other.top);
394 if left < right && bottom < top {
395 Some(Rect {
396 left,
397 bottom,
398 right,
399 top,
400 })
401 } else {
402 None
403 }
404 }
405
406 pub fn union(&self, other: &Rect) -> Rect {
410 Rect {
411 left: self.left.min(other.left),
412 bottom: self.bottom.min(other.bottom),
413 right: self.right.max(other.right),
414 top: self.top.max(other.top),
415 }
416 }
417
418 pub fn inflate(&self, x: f64, y: f64) -> Rect {
422 Rect {
423 left: self.left - x,
424 bottom: self.bottom - y,
425 right: self.right + x,
426 top: self.top + y,
427 }
428 }
429
430 pub fn inflate_sides(&self, left: f64, bottom: f64, right: f64, top: f64) -> Rect {
434 Rect {
435 left: self.left - left,
436 bottom: self.bottom - bottom,
437 right: self.right + right,
438 top: self.top + top,
439 }
440 }
441
442 pub fn deflate_sides(&self, left: f64, bottom: f64, right: f64, top: f64) -> Rect {
446 Rect {
447 left: self.left + left,
448 bottom: self.bottom + bottom,
449 right: self.right - right,
450 top: self.top - top,
451 }
452 }
453
454 pub fn deflate(&self, x: f64, y: f64) -> Rect {
458 Rect {
459 left: self.left + x,
460 bottom: self.bottom + y,
461 right: self.right - x,
462 top: self.top - y,
463 }
464 }
465
466 pub fn from_points(points: &[Point]) -> Option<Rect> {
470 let mut iter = points.iter();
471 let first = iter.next()?;
472 let mut left = first.x;
473 let mut bottom = first.y;
474 let mut right = first.x;
475 let mut top = first.y;
476 for p in iter {
477 left = left.min(p.x);
478 bottom = bottom.min(p.y);
479 right = right.max(p.x);
480 top = top.max(p.y);
481 }
482 Some(Rect {
483 left,
484 bottom,
485 right,
486 top,
487 })
488 }
489
490 pub fn translate(&self, dx: f64, dy: f64) -> Rect {
494 Rect {
495 left: self.left + dx,
496 bottom: self.bottom + dy,
497 right: self.right + dx,
498 top: self.top + dy,
499 }
500 }
501
502 pub fn scale(&self, factor: f64) -> Rect {
506 Rect {
507 left: self.left * factor,
508 bottom: self.bottom * factor,
509 right: self.right * factor,
510 top: self.top * factor,
511 }
512 }
513
514 pub fn outer_rect(&self) -> RectI {
519 RectI::new(
520 self.left.floor() as i32,
521 self.bottom.floor() as i32,
522 self.right.ceil() as i32,
523 self.top.ceil() as i32,
524 )
525 }
526
527 #[inline]
531 pub fn get_outer_rect(&self) -> RectI {
532 self.outer_rect()
533 }
534
535 pub fn inner_rect(&self) -> RectI {
540 RectI::new(
541 self.left.ceil() as i32,
542 self.bottom.ceil() as i32,
543 self.right.floor() as i32,
544 self.top.floor() as i32,
545 )
546 }
547
548 #[inline]
552 pub fn get_inner_rect(&self) -> RectI {
553 self.inner_rect()
554 }
555
556 pub fn closest_rect(&self) -> RectI {
562 fn match_float_range(f1: f64, f2: f64) -> (i32, i32) {
563 let length = (f2 - f1).ceil();
564 let f1_floor = f1.floor();
565 let f1_ceil = f1.ceil();
566 let err1 = f1 - f1_floor + (f2 - f1_floor - length).abs();
567 let err2 = f1_ceil - f1 + (f2 - f1_ceil - length).abs();
568 let start = if err1 > err2 { f1_ceil } else { f1_floor };
569 let clamp = |f: f64| f.clamp(i32::MIN as f64, i32::MAX as f64) as i32;
572 (clamp(start), clamp(start + length))
573 }
574 let (left, right) = match_float_range(self.left, self.right);
575 let (bottom, top) = match_float_range(self.bottom, self.top);
576 RectI::new(left, bottom, right, top)
577 }
578
579 #[inline]
583 pub fn get_closest_rect(&self) -> RectI {
584 self.closest_rect()
585 }
586
587 pub fn to_rect_i(&self) -> RectI {
591 RectI::new(
592 self.left as i32,
593 self.bottom as i32,
594 self.right as i32,
595 self.top as i32,
596 )
597 }
598
599 #[inline]
603 pub fn to_fx_rect(&self) -> RectI {
604 self.to_rect_i()
605 }
606
607 pub fn center(&self) -> Point {
611 Point {
612 x: (self.left + self.right) / 2.0,
613 y: (self.bottom + self.top) / 2.0,
614 }
615 }
616
617 pub fn scale_from_center_point(&self, factor: f64) -> Self {
621 let cx = (self.left + self.right) / 2.0;
622 let cy = (self.bottom + self.top) / 2.0;
623 let half_w = (self.right - self.left) / 2.0 * factor;
624 let half_h = (self.top - self.bottom) / 2.0 * factor;
625 Rect {
626 left: cx - half_w,
627 bottom: cy - half_h,
628 right: cx + half_w,
629 top: cy + half_h,
630 }
631 }
632
633 pub fn center_square(&self) -> Self {
638 let cx = (self.left + self.right) / 2.0;
639 let cy = (self.bottom + self.top) / 2.0;
640 let half = ((self.right - self.left).min(self.top - self.bottom)) / 2.0;
641 Rect {
642 left: cx - half,
643 bottom: cy - half,
644 right: cx + half,
645 top: cy + half,
646 }
647 }
648
649 #[inline]
653 pub fn get_center_square(&self) -> Self {
654 self.center_square()
655 }
656
657 pub fn to_rounded_rect_i(&self) -> RectI {
661 RectI::new(
662 self.left.round() as i32,
663 self.bottom.round() as i32,
664 self.right.round() as i32,
665 self.top.round() as i32,
666 )
667 }
668
669 #[inline]
673 pub fn to_rounded_fx_rect(&self) -> RectI {
674 self.to_rounded_rect_i()
675 }
676
677 #[inline]
681 pub fn get_deflated(&self, x: f64, y: f64) -> Self {
682 self.deflate(x, y)
683 }
684
685 pub fn update_rect(&self, point: Point) -> Self {
690 Rect {
691 left: self.left.min(point.x),
692 bottom: self.bottom.min(point.y),
693 right: self.right.max(point.x),
694 top: self.top.max(point.y),
695 }
696 }
697
698 #[inline]
703 pub fn get_bbox(points: &[Point]) -> Option<Self> {
704 Self::from_points(points)
705 }
706}
707
708#[derive(Debug, Clone, Copy, PartialEq)]
727pub struct Matrix {
728 pub a: f64,
730 pub b: f64,
732 pub c: f64,
734 pub d: f64,
736 pub e: f64,
738 pub f: f64,
740}
741
742impl Matrix {
743 pub fn new(a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) -> Self {
745 Self { a, b, c, d, e, f }
746 }
747
748 pub fn identity() -> Self {
750 Self {
751 a: 1.0,
752 b: 0.0,
753 c: 0.0,
754 d: 1.0,
755 e: 0.0,
756 f: 0.0,
757 }
758 }
759
760 pub fn from_translation(tx: f64, ty: f64) -> Self {
766 Self {
767 a: 1.0,
768 b: 0.0,
769 c: 0.0,
770 d: 1.0,
771 e: tx,
772 f: ty,
773 }
774 }
775
776 pub fn from_scale(sx: f64, sy: f64) -> Self {
782 Self {
783 a: sx,
784 b: 0.0,
785 c: 0.0,
786 d: sy,
787 e: 0.0,
788 f: 0.0,
789 }
790 }
791
792 pub fn from_rotation(angle_radians: f64) -> Self {
799 let cos = angle_radians.cos();
800 let sin = angle_radians.sin();
801 Self {
802 a: cos,
803 b: sin,
804 c: -sin,
805 d: cos,
806 e: 0.0,
807 f: 0.0,
808 }
809 }
810
811 pub fn pre_concat(&self, other: &Matrix) -> Self {
821 Self {
824 a: other.a * self.a + other.b * self.c,
825 b: other.a * self.b + other.b * self.d,
826 c: other.c * self.a + other.d * self.c,
827 d: other.c * self.b + other.d * self.d,
828 e: other.e * self.a + other.f * self.c + self.e,
829 f: other.e * self.b + other.f * self.d + self.f,
830 }
831 }
832
833 pub fn transform_point(&self, point: Point) -> Point {
837 Point {
838 x: self.a * point.x + self.c * point.y + self.e,
839 y: self.b * point.x + self.d * point.y + self.f,
840 }
841 }
842
843 pub fn transform_vector(&self, v: Vector2D) -> Vector2D {
851 Vector2D {
852 x: self.a * v.x + self.c * v.y,
853 y: self.b * v.x + self.d * v.y,
854 }
855 }
856
857 pub fn transform_rect(&self, rect: Rect) -> Rect {
862 let corners = [
863 self.transform_point(Point::new(rect.left, rect.bottom)),
864 self.transform_point(Point::new(rect.right, rect.bottom)),
865 self.transform_point(Point::new(rect.left, rect.top)),
866 self.transform_point(Point::new(rect.right, rect.top)),
867 ];
868
869 let min_x = corners.iter().map(|p| p.x).fold(f64::INFINITY, f64::min);
870 let max_x = corners
871 .iter()
872 .map(|p| p.x)
873 .fold(f64::NEG_INFINITY, f64::max);
874 let min_y = corners.iter().map(|p| p.y).fold(f64::INFINITY, f64::min);
875 let max_y = corners
876 .iter()
877 .map(|p| p.y)
878 .fold(f64::NEG_INFINITY, f64::max);
879
880 Rect::new(min_x, min_y, max_x, max_y)
881 }
882
883 pub fn determinant(&self) -> f64 {
885 self.a * self.d - self.b * self.c
886 }
887
888 pub fn inverse(&self) -> Option<Self> {
893 let det = self.determinant();
894 if det.abs() < f64::EPSILON {
895 return None;
896 }
897 let inv_det = 1.0 / det;
898 Some(Self {
899 a: self.d * inv_det,
900 b: -self.b * inv_det,
901 c: -self.c * inv_det,
902 d: self.a * inv_det,
903 e: (self.c * self.f - self.d * self.e) * inv_det,
904 f: (self.b * self.e - self.a * self.f) * inv_det,
905 })
906 }
907
908 #[inline]
912 pub fn get_inverse(&self) -> Option<Self> {
913 self.inverse()
914 }
915
916 pub fn inverse_or_identity(&self) -> Self {
921 self.inverse().unwrap_or_else(Self::identity)
922 }
923
924 #[allow(clippy::float_cmp)]
929 pub fn is_identity(&self) -> bool {
930 self.a == 1.0
931 && self.b == 0.0
932 && self.c == 0.0
933 && self.d == 1.0
934 && self.e == 0.0
935 && self.f == 0.0
936 }
937
938 pub fn will_scale(&self) -> bool {
942 (self.a - 1.0).abs() >= f64::EPSILON
943 || self.b.abs() >= f64::EPSILON
944 || self.c.abs() >= f64::EPSILON
945 || (self.d - 1.0).abs() >= f64::EPSILON
946 }
947
948 pub fn is_90_rotated(&self) -> bool {
952 self.a.abs() * 1000.0 < self.b.abs() && self.d.abs() * 1000.0 < self.c.abs()
953 }
954
955 pub fn x_unit(&self) -> f64 {
959 (self.a * self.a + self.b * self.b).sqrt()
960 }
961
962 #[inline]
966 pub fn get_x_unit(&self) -> f64 {
967 self.x_unit()
968 }
969
970 pub fn y_unit(&self) -> f64 {
974 (self.c * self.c + self.d * self.d).sqrt()
975 }
976
977 #[inline]
981 pub fn get_y_unit(&self) -> f64 {
982 self.y_unit()
983 }
984
985 pub fn is_scaled(&self) -> bool {
991 (self.b * 1000.0).abs() < self.a.abs() && (self.c * 1000.0).abs() < self.d.abs()
992 }
993
994 pub fn concat(&mut self, right: &Matrix) {
1001 *self = Matrix {
1002 a: self.a * right.a + self.b * right.c,
1003 b: self.a * right.b + self.b * right.d,
1004 c: self.c * right.a + self.d * right.c,
1005 d: self.c * right.b + self.d * right.d,
1006 e: self.e * right.a + self.f * right.c + right.e,
1007 f: self.e * right.b + self.f * right.d + right.f,
1008 };
1009 }
1010
1011 #[deprecated(note = "use `concat()` instead")]
1015 #[inline]
1016 pub fn concat_assign(&mut self, right: &Matrix) {
1017 self.concat(right);
1018 }
1019
1020 pub fn translate(&mut self, x: f64, y: f64) {
1024 self.e += x;
1025 self.f += y;
1026 }
1027
1028 #[deprecated(note = "use `translate()` instead")]
1032 #[inline]
1033 pub fn translate_by(&mut self, tx: f64, ty: f64) {
1034 self.translate(tx, ty);
1035 }
1036
1037 pub fn scale(&mut self, sx: f64, sy: f64) {
1041 self.a *= sx;
1042 self.b *= sy;
1043 self.c *= sx;
1044 self.d *= sy;
1045 self.e *= sx;
1046 self.f *= sy;
1047 }
1048
1049 #[deprecated(note = "use `scale()` instead")]
1053 #[inline]
1054 pub fn scale_by(&mut self, sx: f64, sy: f64) {
1055 self.scale(sx, sy);
1056 }
1057
1058 pub fn translate_prepend(&mut self, tx: f64, ty: f64) {
1062 self.e += tx * self.a + ty * self.c;
1063 self.f += tx * self.b + ty * self.d;
1064 }
1065
1066 pub fn rotate(&mut self, angle_radians: f64) {
1070 let cos = angle_radians.cos();
1071 let sin = angle_radians.sin();
1072 let rotation = Matrix::new(cos, sin, -sin, cos, 0.0, 0.0);
1073 self.concat(&rotation);
1074 }
1075
1076 #[deprecated(note = "use `rotate()` instead")]
1080 #[inline]
1081 pub fn rotate_by(&mut self, angle_radians: f64) {
1082 self.rotate(angle_radians);
1083 }
1084
1085 pub fn transform_distance(&self, dist: f64) -> f64 {
1089 let x_unit = (self.a * self.a + self.b * self.b).sqrt();
1090 let y_unit = (self.c * self.c + self.d * self.d).sqrt();
1091 dist * (x_unit + y_unit) / 2.0
1092 }
1093
1094 pub fn match_rect(dest: &Rect, src: &Rect) -> Self {
1101 let diff_x = src.left - src.right;
1102 let a = if diff_x.abs() < 0.001 {
1103 1.0
1104 } else {
1105 (dest.left - dest.right) / diff_x
1106 };
1107 let diff_y = src.bottom - src.top;
1108 let d = if diff_y.abs() < 0.001 {
1109 1.0
1110 } else {
1111 (dest.bottom - dest.top) / diff_y
1112 };
1113 Matrix {
1114 a,
1115 b: 0.0,
1116 c: 0.0,
1117 d,
1118 e: dest.left - src.left * a,
1119 f: dest.bottom - src.bottom * d,
1120 }
1121 }
1122
1123 pub fn transform_x_distance(&self, dx: f64) -> f64 {
1130 let fx = self.a * dx;
1131 let fy = self.b * dx;
1132 (fx * fx + fy * fy).sqrt()
1133 }
1134
1135 pub fn unit_rect(&self) -> Rect {
1140 self.transform_rect(Rect::new(0.0, 0.0, 1.0, 1.0))
1141 }
1142
1143 #[inline]
1147 pub fn get_unit_rect(&self) -> Rect {
1148 self.unit_rect()
1149 }
1150
1151 #[inline]
1155 pub fn transform(&self, point: Point) -> Point {
1156 self.transform_point(point)
1157 }
1158}
1159
1160impl Default for Matrix {
1161 fn default() -> Self {
1162 Self::identity()
1163 }
1164}
1165
1166#[cfg(test)]
1167mod tests {
1168 use super::*;
1169 use std::f64::consts::PI;
1170
1171 const EPSILON: f64 = 1e-10;
1172
1173 fn approx_eq(a: f64, b: f64) -> bool {
1174 (a - b).abs() < EPSILON
1175 }
1176
1177 fn point_approx_eq(a: Point, b: Point) -> bool {
1178 approx_eq(a.x, b.x) && approx_eq(a.y, b.y)
1179 }
1180
1181 #[test]
1182 fn test_point_new() {
1183 let p = Point::new(1.0, 2.0);
1184 assert_eq!(p.x, 1.0);
1185 assert_eq!(p.y, 2.0);
1186 }
1187
1188 #[test]
1189 fn test_point_origin() {
1190 assert_eq!(Point::ORIGIN.x, 0.0);
1191 assert_eq!(Point::ORIGIN.y, 0.0);
1192 }
1193
1194 #[test]
1195 fn test_size_new() {
1196 let s = Size::new(100.0, 200.0);
1197 assert_eq!(s.width, 100.0);
1198 assert_eq!(s.height, 200.0);
1199 }
1200
1201 fn vec_approx_eq(a: Vector2D, b: Vector2D) -> bool {
1204 approx_eq(a.x, b.x) && approx_eq(a.y, b.y)
1205 }
1206
1207 #[test]
1208 fn test_vector2d_new_and_zero() {
1209 let v = Vector2D::new(3.0, 4.0);
1210 assert_eq!(v.x, 3.0);
1211 assert_eq!(v.y, 4.0);
1212 let z = Vector2D::ZERO;
1213 assert_eq!(z.x, 0.0);
1214 assert_eq!(z.y, 0.0);
1215 }
1216
1217 #[test]
1218 fn test_vector2d_from_tuple() {
1219 let v: Vector2D = (1.5, 2.5).into();
1220 assert_eq!(v.x, 1.5);
1221 assert_eq!(v.y, 2.5);
1222 }
1223
1224 #[test]
1225 fn test_vector2d_from_point() {
1226 let p = Point::new(3.0, 7.0);
1227 let v = Vector2D::from(p);
1228 assert_eq!(v.x, 3.0);
1229 assert_eq!(v.y, 7.0);
1230 }
1231
1232 #[test]
1233 fn test_vector2d_add_sub() {
1234 let a = Vector2D::new(1.0, 2.0);
1235 let b = Vector2D::new(3.0, 4.0);
1236 let sum = a + b;
1237 assert_eq!(sum, Vector2D::new(4.0, 6.0));
1238 let diff = b - a;
1239 assert_eq!(diff, Vector2D::new(2.0, 2.0));
1240 }
1241
1242 #[test]
1243 fn test_vector2d_neg() {
1244 let v = Vector2D::new(1.0, -2.0);
1245 let neg = -v;
1246 assert_eq!(neg, Vector2D::new(-1.0, 2.0));
1247 }
1248
1249 #[test]
1250 fn test_vector2d_scalar_mul() {
1251 let v = Vector2D::new(2.0, 3.0);
1252 let scaled = v * 2.0;
1253 assert_eq!(scaled, Vector2D::new(4.0, 6.0));
1254 let scaled2 = 3.0 * v;
1255 assert_eq!(scaled2, Vector2D::new(6.0, 9.0));
1256 }
1257
1258 #[test]
1259 fn test_vector2d_assign_ops() {
1260 let mut v = Vector2D::new(1.0, 2.0);
1261 v += Vector2D::new(0.5, 0.5);
1262 assert_eq!(v, Vector2D::new(1.5, 2.5));
1263 v -= Vector2D::new(0.5, 0.5);
1264 assert_eq!(v, Vector2D::new(1.0, 2.0));
1265 v *= 2.0;
1266 assert_eq!(v, Vector2D::new(2.0, 4.0));
1267 }
1268
1269 #[test]
1270 fn test_vector2d_dot() {
1271 let a = Vector2D::new(1.0, 0.0);
1272 let b = Vector2D::new(0.0, 1.0);
1273 assert_eq!(a.dot(b), 0.0); let c = Vector2D::new(3.0, 4.0);
1275 assert_eq!(c.dot(c), 25.0);
1276 }
1277
1278 #[test]
1279 fn test_vector2d_length() {
1280 let v = Vector2D::new(3.0, 4.0);
1281 assert!(approx_eq(v.length_squared(), 25.0));
1282 assert!(approx_eq(v.length(), 5.0));
1283 assert!(approx_eq(Vector2D::ZERO.length(), 0.0));
1284 }
1285
1286 #[test]
1287 fn test_vector2d_normalize() {
1288 let v = Vector2D::new(3.0, 4.0);
1289 let n = v.normalize();
1290 assert!(approx_eq(n.length(), 1.0));
1291 assert!(approx_eq(n.x, 0.6));
1292 assert!(approx_eq(n.y, 0.8));
1293 }
1294
1295 #[test]
1296 fn test_vector2d_normalize_zero() {
1297 let n = Vector2D::ZERO.normalize();
1298 assert_eq!(n, Vector2D::ZERO);
1299 }
1300
1301 #[test]
1302 fn test_matrix_transform_vector_no_translation() {
1303 let m = Matrix::new(2.0, 0.0, 0.0, 2.0, 10.0, 20.0);
1305 let v = Vector2D::new(1.0, 1.0);
1306 let tv = m.transform_vector(v);
1307 assert!(vec_approx_eq(tv, Vector2D::new(2.0, 2.0)));
1308 }
1309
1310 #[test]
1311 fn test_matrix_transform_vector_rotation_90() {
1312 let m = Matrix::from_rotation(PI / 2.0);
1314 let v = Vector2D::new(1.0, 0.0);
1315 let tv = m.transform_vector(v);
1316 assert!(vec_approx_eq(tv, Vector2D::new(0.0, 1.0)));
1317 }
1318
1319 #[test]
1320 fn test_rect_dimensions() {
1321 let r = Rect::new(10.0, 20.0, 110.0, 220.0);
1322 assert_eq!(r.width(), 100.0);
1323 assert_eq!(r.height(), 200.0);
1324 }
1325
1326 #[test]
1327 fn test_rect_contains() {
1328 let r = Rect::new(0.0, 0.0, 100.0, 100.0);
1329 assert!(r.contains(Point::new(50.0, 50.0)));
1330 assert!(r.contains(Point::new(0.0, 0.0)));
1331 assert!(r.contains(Point::new(100.0, 100.0)));
1332 assert!(!r.contains(Point::new(-1.0, 50.0)));
1333 assert!(!r.contains(Point::new(50.0, 101.0)));
1334 }
1335
1336 #[test]
1337 fn test_rect_contains_rect_basic() {
1338 let outer = Rect::new(0.0, 0.0, 100.0, 100.0);
1339 let inner = Rect::new(10.0, 10.0, 90.0, 90.0);
1340 assert!(outer.contains_rect(&inner));
1341 assert!(!inner.contains_rect(&outer));
1342 }
1343
1344 #[test]
1345 fn test_rect_contains_rect_equal() {
1346 let r = Rect::new(0.0, 0.0, 100.0, 100.0);
1347 assert!(r.contains_rect(&r));
1348 }
1349
1350 #[test]
1351 fn test_rect_contains_rect_partial_overlap() {
1352 let a = Rect::new(0.0, 0.0, 60.0, 60.0);
1353 let b = Rect::new(40.0, 40.0, 100.0, 100.0);
1354 assert!(!a.contains_rect(&b));
1355 assert!(!b.contains_rect(&a));
1356 }
1357
1358 #[test]
1359 fn test_rect_contains_rect_unnormalized() {
1360 let outer = Rect::new(100.0, 100.0, 0.0, 0.0);
1362 let inner = Rect::new(10.0, 10.0, 90.0, 90.0);
1363 assert!(outer.contains_rect(&inner));
1364 }
1365
1366 #[test]
1367 fn test_rect_normalize() {
1368 let r = Rect::new(100.0, 200.0, 0.0, 0.0);
1369 let n = r.normalize();
1370 assert_eq!(n.left, 0.0);
1371 assert_eq!(n.bottom, 0.0);
1372 assert_eq!(n.right, 100.0);
1373 assert_eq!(n.top, 200.0);
1374 }
1375
1376 #[test]
1377 fn test_rect_size() {
1378 let r = Rect::new(10.0, 20.0, 50.0, 80.0);
1379 let s = r.size();
1380 assert_eq!(s.width, 40.0);
1381 assert_eq!(s.height, 60.0);
1382 }
1383
1384 #[test]
1385 fn test_matrix_identity() {
1386 let m = Matrix::identity();
1387 assert!(m.is_identity());
1388 assert_eq!(m.a, 1.0);
1389 assert_eq!(m.b, 0.0);
1390 assert_eq!(m.c, 0.0);
1391 assert_eq!(m.d, 1.0);
1392 assert_eq!(m.e, 0.0);
1393 assert_eq!(m.f, 0.0);
1394 }
1395
1396 #[test]
1397 fn test_matrix_default_is_identity() {
1398 let m = Matrix::default();
1399 assert!(m.is_identity());
1400 }
1401
1402 #[test]
1403 fn test_matrix_translate() {
1404 let m = Matrix::from_translation(10.0, 20.0);
1405 let p = m.transform_point(Point::new(5.0, 5.0));
1406 assert!(point_approx_eq(p, Point::new(15.0, 25.0)));
1407 }
1408
1409 #[test]
1410 fn test_matrix_scale() {
1411 let m = Matrix::from_scale(2.0, 3.0);
1412 let p = m.transform_point(Point::new(5.0, 10.0));
1413 assert!(point_approx_eq(p, Point::new(10.0, 30.0)));
1414 }
1415
1416 #[test]
1417 fn test_matrix_rotate_90_degrees() {
1418 let m = Matrix::from_rotation(PI / 2.0);
1419 let p = m.transform_point(Point::new(1.0, 0.0));
1420 assert!(point_approx_eq(p, Point::new(0.0, 1.0)));
1422 }
1423
1424 #[test]
1425 fn test_matrix_rotate_180_degrees() {
1426 let m = Matrix::from_rotation(PI);
1427 let p = m.transform_point(Point::new(1.0, 0.0));
1428 assert!(point_approx_eq(p, Point::new(-1.0, 0.0)));
1430 }
1431
1432 #[test]
1433 fn test_matrix_concat_identity() {
1434 let m = Matrix::from_translation(10.0, 20.0);
1435 let result = m.pre_concat(&Matrix::identity());
1436 let p = result.transform_point(Point::ORIGIN);
1437 assert!(point_approx_eq(p, Point::new(10.0, 20.0)));
1438 }
1439
1440 #[test]
1441 fn test_matrix_concat_translate_then_scale() {
1442 let scale = Matrix::from_scale(2.0, 2.0);
1444 let translate = Matrix::from_translation(10.0, 10.0);
1445 let m = translate.pre_concat(&scale);
1447 let p = m.transform_point(Point::new(5.0, 5.0));
1448 assert!(point_approx_eq(p, Point::new(20.0, 20.0)));
1450 }
1451
1452 #[test]
1453 fn test_matrix_concat_scale_then_translate() {
1454 let scale = Matrix::from_scale(2.0, 2.0);
1456 let translate = Matrix::from_translation(10.0, 10.0);
1457 let m = scale.pre_concat(&translate);
1459 let p = m.transform_point(Point::new(5.0, 5.0));
1460 assert!(point_approx_eq(p, Point::new(30.0, 30.0)));
1462 }
1463
1464 #[test]
1465 fn test_matrix_transform_rect() {
1466 let m = Matrix::from_translation(10.0, 20.0);
1467 let r = Rect::new(0.0, 0.0, 100.0, 100.0);
1468 let tr = m.transform_rect(r);
1469 assert!(approx_eq(tr.left, 10.0));
1470 assert!(approx_eq(tr.bottom, 20.0));
1471 assert!(approx_eq(tr.right, 110.0));
1472 assert!(approx_eq(tr.top, 120.0));
1473 }
1474
1475 #[test]
1476 fn test_matrix_transform_rect_with_rotation() {
1477 let m = Matrix::from_rotation(PI / 2.0);
1479 let r = Rect::new(0.0, 0.0, 1.0, 1.0);
1480 let tr = m.transform_rect(r);
1481 assert!(approx_eq(tr.left, -1.0));
1483 assert!(approx_eq(tr.bottom, 0.0));
1484 assert!(approx_eq(tr.right, 0.0));
1485 assert!(approx_eq(tr.top, 1.0));
1486 }
1487
1488 #[test]
1489 fn test_matrix_determinant() {
1490 let m = Matrix::from_scale(2.0, 3.0);
1491 assert!(approx_eq(m.determinant(), 6.0));
1492 }
1493
1494 #[test]
1495 fn test_matrix_inverse_identity() {
1496 let m = Matrix::identity();
1497 let inv = m.inverse().unwrap();
1498 assert!(inv.is_identity());
1499 }
1500
1501 #[test]
1502 fn test_matrix_inverse_translate() {
1503 let m = Matrix::from_translation(10.0, 20.0);
1504 let inv = m.inverse().unwrap();
1505 let p = m.transform_point(Point::new(5.0, 5.0));
1506 let p2 = inv.transform_point(p);
1507 assert!(point_approx_eq(p2, Point::new(5.0, 5.0)));
1508 }
1509
1510 #[test]
1511 fn test_matrix_inverse_scale() {
1512 let m = Matrix::from_scale(2.0, 4.0);
1513 let inv = m.inverse().unwrap();
1514 let p = m.transform_point(Point::new(3.0, 5.0));
1515 let p2 = inv.transform_point(p);
1516 assert!(point_approx_eq(p2, Point::new(3.0, 5.0)));
1517 }
1518
1519 #[test]
1520 fn test_matrix_singular_has_no_inverse() {
1521 let m = Matrix::new(1.0, 2.0, 2.0, 4.0, 0.0, 0.0);
1523 assert!(m.inverse().is_none());
1524 }
1525
1526 #[test]
1527 fn test_matrix_concat_with_inverse_is_identity() {
1528 let m = Matrix::new(2.0, 1.0, -1.0, 3.0, 5.0, 7.0);
1529 let inv = m.inverse().unwrap();
1530 let result = m.pre_concat(&inv);
1531 assert!(result.is_identity());
1532 }
1533
1534 #[test]
1535 fn test_geometry_types_are_send_sync() {
1536 fn assert_send_sync<T: Send + Sync>() {}
1537 assert_send_sync::<Point>();
1538 assert_send_sync::<Size>();
1539 assert_send_sync::<Rect>();
1540 assert_send_sync::<Matrix>();
1541 }
1542
1543 #[test]
1544 fn test_geometry_types_are_copy() {
1545 fn assert_copy<T: Copy>() {}
1546 assert_copy::<Point>();
1547 assert_copy::<Size>();
1548 assert_copy::<Rect>();
1549 assert_copy::<Matrix>();
1550 }
1551
1552 #[test]
1557 fn test_rect_is_empty_normal_rect_false() {
1558 assert!(!Rect::new(0.0, 0.0, 10.0, 10.0).is_empty());
1559 }
1560
1561 #[test]
1562 fn test_rect_is_empty_zero_width_true() {
1563 assert!(Rect::new(5.0, 0.0, 5.0, 10.0).is_empty());
1564 }
1565
1566 #[test]
1567 fn test_rect_is_empty_zero_height_true() {
1568 assert!(Rect::new(0.0, 5.0, 10.0, 5.0).is_empty());
1569 }
1570
1571 #[test]
1572 fn test_rect_is_empty_inverted_true() {
1573 assert!(Rect::new(10.0, 0.0, 0.0, 10.0).is_empty());
1575 assert!(Rect::new(0.0, 10.0, 10.0, 0.0).is_empty());
1577 }
1578
1579 #[test]
1584 fn test_rect_intersect_overlapping() {
1585 let a = Rect::new(0.0, 0.0, 10.0, 10.0);
1586 let b = Rect::new(5.0, 5.0, 15.0, 15.0);
1587 let i = a.intersect(&b).unwrap();
1588 assert_eq!(i.left, 5.0);
1589 assert_eq!(i.bottom, 5.0);
1590 assert_eq!(i.right, 10.0);
1591 assert_eq!(i.top, 10.0);
1592 }
1593
1594 #[test]
1595 fn test_rect_intersect_non_overlapping_returns_none() {
1596 let a = Rect::new(0.0, 0.0, 5.0, 5.0);
1597 let b = Rect::new(10.0, 10.0, 20.0, 20.0);
1598 assert!(a.intersect(&b).is_none());
1599 }
1600
1601 #[test]
1602 fn test_rect_intersect_touching_edge_returns_none() {
1603 let a = Rect::new(0.0, 0.0, 10.0, 10.0);
1605 let b = Rect::new(10.0, 0.0, 20.0, 10.0);
1606 assert!(a.intersect(&b).is_none());
1607 }
1608
1609 #[test]
1610 fn test_rect_union_basic() {
1611 let a = Rect::new(0.0, 0.0, 5.0, 5.0);
1612 let b = Rect::new(3.0, 3.0, 10.0, 10.0);
1613 let u = a.union(&b);
1614 assert_eq!(u.left, 0.0);
1615 assert_eq!(u.bottom, 0.0);
1616 assert_eq!(u.right, 10.0);
1617 assert_eq!(u.top, 10.0);
1618 }
1619
1620 #[test]
1621 fn test_rect_union_disjoint() {
1622 let a = Rect::new(0.0, 0.0, 2.0, 2.0);
1623 let b = Rect::new(5.0, 5.0, 10.0, 10.0);
1624 let u = a.union(&b);
1625 assert_eq!(u.left, 0.0);
1626 assert_eq!(u.bottom, 0.0);
1627 assert_eq!(u.right, 10.0);
1628 assert_eq!(u.top, 10.0);
1629 }
1630
1631 #[test]
1632 fn test_rect_inflate_expands_all_sides() {
1633 let r = Rect::new(5.0, 5.0, 15.0, 15.0);
1634 let inflated = r.inflate(2.0, 3.0);
1635 assert_eq!(inflated.left, 3.0);
1636 assert_eq!(inflated.bottom, 2.0);
1637 assert_eq!(inflated.right, 17.0);
1638 assert_eq!(inflated.top, 18.0);
1639 }
1640
1641 #[test]
1642 fn test_rect_deflate_shrinks_all_sides() {
1643 let r = Rect::new(0.0, 0.0, 20.0, 20.0);
1644 let deflated = r.deflate(3.0, 5.0);
1645 assert_eq!(deflated.left, 3.0);
1646 assert_eq!(deflated.bottom, 5.0);
1647 assert_eq!(deflated.right, 17.0);
1648 assert_eq!(deflated.top, 15.0);
1649 }
1650
1651 #[test]
1652 fn test_rect_inflate_sides_expands_independently() {
1653 let r = Rect::new(5.0, 5.0, 15.0, 15.0);
1654 let inflated = r.inflate_sides(1.0, 2.0, 3.0, 4.0);
1655 assert_eq!(inflated.left, 4.0);
1656 assert_eq!(inflated.bottom, 3.0);
1657 assert_eq!(inflated.right, 18.0);
1658 assert_eq!(inflated.top, 19.0);
1659 }
1660
1661 #[test]
1662 fn test_rect_deflate_sides_shrinks_independently() {
1663 let r = Rect::new(0.0, 0.0, 20.0, 20.0);
1664 let deflated = r.deflate_sides(1.0, 2.0, 3.0, 4.0);
1665 assert_eq!(deflated.left, 1.0);
1666 assert_eq!(deflated.bottom, 2.0);
1667 assert_eq!(deflated.right, 17.0);
1668 assert_eq!(deflated.top, 16.0);
1669 }
1670
1671 #[test]
1672 fn test_rect_from_points_single_point() {
1673 let pts = [Point::new(3.0, 7.0)];
1674 let r = Rect::from_points(&pts).unwrap();
1675 assert_eq!(r.left, 3.0);
1676 assert_eq!(r.bottom, 7.0);
1677 assert_eq!(r.right, 3.0);
1678 assert_eq!(r.top, 7.0);
1679 }
1680
1681 #[test]
1682 fn test_rect_from_points_multiple() {
1683 let pts = [
1684 Point::new(3.0, 7.0),
1685 Point::new(-1.0, 10.0),
1686 Point::new(5.0, 2.0),
1687 ];
1688 let r = Rect::from_points(&pts).unwrap();
1689 assert_eq!(r.left, -1.0);
1690 assert_eq!(r.bottom, 2.0);
1691 assert_eq!(r.right, 5.0);
1692 assert_eq!(r.top, 10.0);
1693 }
1694
1695 #[test]
1696 fn test_rect_from_points_empty_returns_none() {
1697 assert!(Rect::from_points(&[]).is_none());
1698 }
1699
1700 #[test]
1705 fn test_matrix_will_scale_identity_false() {
1706 assert!(!Matrix::identity().will_scale());
1707 }
1708
1709 #[test]
1710 fn test_matrix_will_scale_translation_false() {
1711 assert!(!Matrix::from_translation(10.0, 20.0).will_scale());
1712 }
1713
1714 #[test]
1715 fn test_matrix_will_scale_scale_matrix_true() {
1716 assert!(Matrix::from_scale(2.0, 1.0).will_scale());
1717 }
1718
1719 #[test]
1720 fn test_matrix_will_scale_rotation_true() {
1721 assert!(Matrix::from_rotation(std::f64::consts::PI / 4.0).will_scale());
1722 }
1723
1724 #[test]
1725 fn test_matrix_is_90_rotated_true() {
1726 let m = Matrix::from_rotation(std::f64::consts::PI / 2.0);
1728 assert!(m.is_90_rotated());
1729 }
1730
1731 #[test]
1732 fn test_matrix_is_90_rotated_identity_false() {
1733 assert!(!Matrix::identity().is_90_rotated());
1734 }
1735
1736 #[test]
1737 fn test_matrix_get_x_unit_identity() {
1738 assert!(approx_eq(Matrix::identity().get_x_unit(), 1.0));
1739 }
1740
1741 #[test]
1742 fn test_matrix_get_y_unit_identity() {
1743 assert!(approx_eq(Matrix::identity().get_y_unit(), 1.0));
1744 }
1745
1746 #[test]
1747 fn test_matrix_get_x_unit_scale() {
1748 let m = Matrix::from_scale(3.0, 5.0);
1749 assert!(approx_eq(m.get_x_unit(), 3.0));
1750 assert!(approx_eq(m.get_y_unit(), 5.0));
1751 }
1752
1753 #[test]
1754 fn test_matrix_get_x_unit_rotation() {
1755 let m = Matrix::from_rotation(1.0);
1757 assert!(approx_eq(m.get_x_unit(), 1.0));
1758 assert!(approx_eq(m.get_y_unit(), 1.0));
1759 }
1760
1761 #[test]
1766 fn test_rect_translate_positive() {
1767 let r = Rect::new(1.0, 2.0, 5.0, 6.0);
1768 let t = r.translate(3.0, 4.0);
1769 assert_eq!(t.left, 4.0);
1770 assert_eq!(t.bottom, 6.0);
1771 assert_eq!(t.right, 8.0);
1772 assert_eq!(t.top, 10.0);
1773 }
1774
1775 #[test]
1776 fn test_rect_translate_negative() {
1777 let r = Rect::new(10.0, 10.0, 20.0, 20.0);
1778 let t = r.translate(-5.0, -3.0);
1779 assert_eq!(t.left, 5.0);
1780 assert_eq!(t.bottom, 7.0);
1781 assert_eq!(t.right, 15.0);
1782 assert_eq!(t.top, 17.0);
1783 }
1784
1785 #[test]
1786 fn test_rect_scale_expands() {
1787 let r = Rect::new(1.0, 2.0, 3.0, 4.0);
1788 let s = r.scale(2.0);
1789 assert_eq!(s.left, 2.0);
1790 assert_eq!(s.bottom, 4.0);
1791 assert_eq!(s.right, 6.0);
1792 assert_eq!(s.top, 8.0);
1793 }
1794
1795 #[test]
1796 fn test_rect_get_outer_rect_expands() {
1797 let r = Rect::new(1.1, 2.2, 3.3, 4.4);
1798 let o = r.get_outer_rect();
1799 assert_eq!(o.left, 1);
1800 assert_eq!(o.bottom, 2);
1801 assert_eq!(o.right, 4);
1802 assert_eq!(o.top, 5);
1803 assert_eq!(o.width(), 3);
1804 assert_eq!(o.height(), 3);
1805 }
1806
1807 #[test]
1808 fn test_rect_get_inner_rect_shrinks() {
1809 let r = Rect::new(1.1, 2.2, 3.9, 4.8);
1810 let i = r.get_inner_rect();
1811 assert_eq!(i.left, 2);
1812 assert_eq!(i.bottom, 3);
1813 assert_eq!(i.right, 3);
1814 assert_eq!(i.top, 4);
1815 }
1816
1817 #[test]
1818 fn test_rect_get_closest_rect_preserves_dimension() {
1819 let r = Rect::new(1.0, 2.0, 4.0, 6.0);
1821 let c = r.get_closest_rect();
1822 assert_eq!(c.width(), 3);
1823 assert_eq!(c.height(), 4);
1824 }
1825
1826 #[test]
1827 fn test_rect_get_closest_rect_clamps_extreme_values() {
1828 let huge = 3.0e9_f64;
1830 let r = Rect::new(-huge, -huge, huge, huge);
1831 let c = r.get_closest_rect();
1832 assert_eq!(c.left, i32::MIN);
1833 assert_eq!(c.bottom, i32::MIN);
1834 assert_eq!(c.right, i32::MAX);
1835 assert_eq!(c.top, i32::MAX);
1836 }
1837
1838 #[test]
1839 fn test_rect_to_rect_i_truncates() {
1840 let r = Rect::new(1.9, 2.9, 3.1, 4.1);
1841 let ri = r.to_rect_i();
1842 assert_eq!(ri.left, 1);
1843 assert_eq!(ri.bottom, 2);
1844 assert_eq!(ri.right, 3);
1845 assert_eq!(ri.top, 4);
1846 }
1847
1848 #[test]
1849 fn test_rect_to_rounded_rect_i_rounds() {
1850 let r = Rect::new(1.4, 2.6, 3.5, 4.5);
1851 let rr = r.to_rounded_rect_i();
1852 assert_eq!(rr.left, 1);
1853 assert_eq!(rr.bottom, 3);
1854 assert_eq!(rr.right, 4);
1855 assert_eq!(rr.top, 5);
1856 }
1857
1858 #[test]
1864 fn test_matrix_concat_applies_right_after_self() {
1865 let mut m = Matrix::from_translation(10.0, 10.0);
1867 m.concat(&Matrix::from_scale(2.0, 2.0));
1868 let p = m.transform_point(Point::new(0.0, 0.0));
1871 assert!(point_approx_eq(p, Point::new(20.0, 20.0)));
1872 }
1873
1874 #[test]
1875 fn test_matrix_concat_identity_is_noop() {
1876 let mut m = Matrix::from_translation(5.0, 3.0);
1877 let orig_e = m.e;
1878 let orig_f = m.f;
1879 m.concat(&Matrix::identity());
1880 assert!(approx_eq(m.e, orig_e));
1881 assert!(approx_eq(m.f, orig_f));
1882 }
1883
1884 #[test]
1885 fn test_matrix_translate_shifts_ef() {
1886 let mut m = Matrix::identity();
1887 m.translate(3.0, 7.0);
1888 assert!(approx_eq(m.e, 3.0));
1889 assert!(approx_eq(m.f, 7.0));
1890 assert!(approx_eq(m.a, 1.0));
1892 assert!(approx_eq(m.d, 1.0));
1893 }
1894
1895 #[test]
1896 fn test_matrix_translate_accumulates() {
1897 let mut m = Matrix::identity();
1898 m.translate(1.0, 2.0);
1899 m.translate(3.0, 4.0);
1900 assert!(approx_eq(m.e, 4.0));
1901 assert!(approx_eq(m.f, 6.0));
1902 }
1903
1904 #[test]
1905 fn test_matrix_scale_multiplies_all_components() {
1906 let mut m = Matrix::new(2.0, 1.0, 1.0, 3.0, 4.0, 5.0);
1907 m.scale(2.0, 3.0);
1908 assert!(approx_eq(m.a, 4.0));
1910 assert!(approx_eq(m.b, 3.0));
1911 assert!(approx_eq(m.c, 2.0));
1912 assert!(approx_eq(m.d, 9.0));
1913 assert!(approx_eq(m.e, 8.0));
1914 assert!(approx_eq(m.f, 15.0));
1915 }
1916
1917 #[test]
1918 fn test_matrix_is_scaled_identity_true() {
1919 assert!(Matrix::identity().is_scaled());
1921 }
1922
1923 #[test]
1924 fn test_matrix_is_scaled_pure_scale_true() {
1925 assert!(Matrix::from_scale(3.0, 5.0).is_scaled());
1926 }
1927
1928 #[test]
1929 fn test_matrix_is_scaled_90_rotation_false() {
1930 let m = Matrix::from_rotation(std::f64::consts::PI / 2.0);
1933 assert!(!m.is_scaled());
1934 }
1935
1936 #[test]
1937 fn test_matrix_match_rect_basic() {
1938 let src = Rect::new(0.0, 0.0, 1.0, 1.0);
1940 let dest = Rect::new(0.0, 0.0, 2.0, 4.0);
1941 let m = Matrix::match_rect(&dest, &src);
1942 assert!(approx_eq(m.b, 0.0));
1944 assert!(approx_eq(m.c, 0.0));
1945 let p = m.transform_point(Point::new(0.0, 0.0));
1947 assert!(point_approx_eq(p, Point::new(0.0, 0.0)));
1948 let p = m.transform_point(Point::new(1.0, 1.0));
1950 assert!(point_approx_eq(p, Point::new(2.0, 4.0)));
1951 }
1952
1953 #[test]
1954 fn test_matrix_match_rect_zero_width_uses_scale_one() {
1955 let src = Rect::new(5.0, 0.0, 5.0, 1.0); let dest = Rect::new(0.0, 0.0, 10.0, 1.0);
1957 let m = Matrix::match_rect(&dest, &src);
1958 assert!(approx_eq(m.a, 1.0)); }
1960
1961 #[test]
1962 fn test_matrix_transform_x_distance_identity() {
1963 let m = Matrix::identity();
1964 assert!(approx_eq(m.transform_x_distance(3.0), 3.0));
1965 }
1966
1967 #[test]
1968 fn test_matrix_transform_x_distance_scale() {
1969 let m = Matrix::from_scale(2.0, 5.0);
1970 assert!(approx_eq(m.transform_x_distance(3.0), 6.0));
1972 }
1973
1974 #[test]
1975 fn test_matrix_transform_x_distance_rotation() {
1976 let m = Matrix::from_rotation(std::f64::consts::PI / 2.0);
1978 assert!(approx_eq(m.transform_x_distance(4.0), 4.0));
1979 }
1980
1981 #[test]
1982 fn test_matrix_get_unit_rect_identity() {
1983 let r = Matrix::identity().get_unit_rect();
1984 assert!(approx_eq(r.left, 0.0));
1985 assert!(approx_eq(r.bottom, 0.0));
1986 assert!(approx_eq(r.right, 1.0));
1987 assert!(approx_eq(r.top, 1.0));
1988 }
1989
1990 #[test]
1991 fn test_matrix_get_unit_rect_scale() {
1992 let r = Matrix::from_scale(3.0, 2.0).get_unit_rect();
1993 assert!(approx_eq(r.right, 3.0));
1994 assert!(approx_eq(r.top, 2.0));
1995 }
1996
1997 #[test]
2002 fn test_recti_normalize_noop() {
2003 let r = RectI::new(1, 2, 10, 20);
2004 let n = r.normalize();
2005 assert_eq!(n, r);
2006 }
2007
2008 #[test]
2009 fn test_recti_normalize_inverted() {
2010 let r = RectI::new(10, 20, 1, 2);
2011 let n = r.normalize();
2012 assert_eq!(n, RectI::new(1, 2, 10, 20));
2013 }
2014
2015 #[test]
2016 fn test_recti_intersect_overlap() {
2017 let a = RectI::new(0, 0, 10, 10);
2018 let b = RectI::new(5, 5, 15, 15);
2019 let i = a.intersect(&b).unwrap();
2020 assert_eq!(i, RectI::new(5, 5, 10, 10));
2021 }
2022
2023 #[test]
2024 fn test_recti_intersect_no_overlap() {
2025 let a = RectI::new(0, 0, 5, 5);
2026 let b = RectI::new(10, 10, 20, 20);
2027 assert!(a.intersect(&b).is_none());
2028 }
2029
2030 #[test]
2031 fn test_recti_contains_point_inside() {
2032 let r = RectI::new(0, 0, 10, 10);
2033 assert!(r.contains_point(5, 5));
2034 }
2035
2036 #[test]
2037 fn test_recti_contains_point_boundary() {
2038 let r = RectI::new(0, 0, 10, 10);
2039 assert!(r.contains_point(0, 0));
2041 assert!(!r.contains_point(10, 5));
2043 assert!(!r.contains_point(5, 10));
2044 }
2045
2046 #[test]
2047 fn test_recti_contains_point_outside() {
2048 let r = RectI::new(0, 0, 10, 10);
2049 assert!(!r.contains_point(-1, 5));
2050 assert!(!r.contains_point(5, -1));
2051 }
2052
2053 #[test]
2054 fn test_recti_is_empty_normal_false() {
2055 assert!(!RectI::new(0, 0, 10, 10).is_empty());
2056 }
2057
2058 #[test]
2059 fn test_recti_is_empty_zero_width_true() {
2060 assert!(RectI::new(5, 0, 5, 10).is_empty());
2061 }
2062
2063 #[test]
2064 fn test_recti_is_empty_zero_height_true() {
2065 assert!(RectI::new(0, 5, 10, 5).is_empty());
2066 }
2067
2068 #[test]
2069 fn test_recti_is_empty_inverted_true() {
2070 assert!(RectI::new(10, 0, 0, 10).is_empty());
2071 assert!(RectI::new(0, 10, 10, 0).is_empty());
2072 }
2073
2074 #[test]
2075 fn test_recti_offset_positive() {
2076 let r = RectI::new(1, 2, 5, 6);
2077 let o = r.offset(3, 4);
2078 assert_eq!(o, RectI::new(4, 6, 8, 10));
2079 }
2080
2081 #[test]
2082 fn test_recti_offset_negative() {
2083 let r = RectI::new(10, 10, 20, 20);
2084 let o = r.offset(-5, -3);
2085 assert_eq!(o, RectI::new(5, 7, 15, 17));
2086 }
2087
2088 #[test]
2093 fn test_rect_contains_rect_fully_inside() {
2094 let outer = Rect::new(0.0, 0.0, 100.0, 100.0);
2095 let inner = Rect::new(10.0, 10.0, 50.0, 50.0);
2096 assert!(outer.contains_rect(&inner));
2097 }
2098
2099 #[test]
2100 fn test_rect_contains_rect_same() {
2101 let r = Rect::new(0.0, 0.0, 100.0, 100.0);
2102 assert!(r.contains_rect(&r));
2103 }
2104
2105 #[test]
2106 fn test_rect_contains_rect_partial_overlap_normalized() {
2107 let outer = Rect::new(0.0, 0.0, 100.0, 100.0);
2108 let partial = Rect::new(50.0, 50.0, 150.0, 150.0);
2109 assert!(!outer.contains_rect(&partial));
2110 }
2111
2112 #[test]
2117 fn test_matrix_translate_prepend_identity() {
2118 let mut m = Matrix::identity();
2119 m.translate_prepend(5.0, 3.0);
2120 assert!(approx_eq(m.e, 5.0));
2122 assert!(approx_eq(m.f, 3.0));
2123 }
2124
2125 #[test]
2126 fn test_matrix_translate_prepend_with_scale() {
2127 let mut m = Matrix::from_scale(2.0, 3.0);
2128 m.translate_prepend(5.0, 7.0);
2129 assert!(approx_eq(m.e, 10.0));
2131 assert!(approx_eq(m.f, 21.0));
2132 }
2133
2134 #[test]
2139 fn test_matrix_transform_distance_identity() {
2140 let m = Matrix::identity();
2141 assert!(approx_eq(m.transform_distance(5.0), 5.0));
2143 }
2144
2145 #[test]
2146 fn test_matrix_transform_distance_scale() {
2147 let m = Matrix::from_scale(2.0, 4.0);
2148 assert!(approx_eq(m.transform_distance(5.0), 15.0));
2150 }
2151
2152 #[test]
2157 fn test_rect_center_basic() {
2158 let r = Rect::new(0.0, 0.0, 100.0, 200.0);
2159 let c = r.center();
2160 assert!(approx_eq(c.x, 50.0));
2161 assert!(approx_eq(c.y, 100.0));
2162 }
2163
2164 #[test]
2165 fn test_rect_center_offset() {
2166 let r = Rect::new(10.0, 20.0, 30.0, 40.0);
2167 let c = r.center();
2168 assert!(approx_eq(c.x, 20.0));
2169 assert!(approx_eq(c.y, 30.0));
2170 }
2171
2172 #[test]
2173 fn test_rect_scale_from_center_point_double() {
2174 let r = Rect::new(0.0, 0.0, 10.0, 20.0);
2175 let s = r.scale_from_center_point(2.0);
2176 assert!(approx_eq(s.left, -5.0));
2178 assert!(approx_eq(s.bottom, -10.0));
2179 assert!(approx_eq(s.right, 15.0));
2180 assert!(approx_eq(s.top, 30.0));
2181 }
2182
2183 #[test]
2184 fn test_rect_scale_from_center_point_half() {
2185 let r = Rect::new(0.0, 0.0, 10.0, 20.0);
2186 let s = r.scale_from_center_point(0.5);
2187 assert!(approx_eq(s.left, 2.5));
2189 assert!(approx_eq(s.bottom, 5.0));
2190 assert!(approx_eq(s.right, 7.5));
2191 assert!(approx_eq(s.top, 15.0));
2192 }
2193
2194 #[test]
2195 fn test_rect_center_square_landscape() {
2196 let r = Rect::new(0.0, 0.0, 100.0, 40.0);
2198 let sq = r.center_square();
2199 assert!(approx_eq(sq.left, 30.0));
2200 assert!(approx_eq(sq.bottom, 0.0));
2201 assert!(approx_eq(sq.right, 70.0));
2202 assert!(approx_eq(sq.top, 40.0));
2203 }
2204
2205 #[test]
2206 fn test_rect_center_square_portrait() {
2207 let r = Rect::new(0.0, 0.0, 40.0, 100.0);
2209 let sq = r.center_square();
2210 assert!(approx_eq(sq.left, 0.0));
2211 assert!(approx_eq(sq.bottom, 30.0));
2212 assert!(approx_eq(sq.right, 40.0));
2213 assert!(approx_eq(sq.top, 70.0));
2214 }
2215
2216 #[test]
2217 fn test_rect_center_square_already_square() {
2218 let r = Rect::new(0.0, 0.0, 50.0, 50.0);
2219 let sq = r.center_square();
2220 assert!(approx_eq(sq.left, 0.0));
2221 assert!(approx_eq(sq.bottom, 0.0));
2222 assert!(approx_eq(sq.right, 50.0));
2223 assert!(approx_eq(sq.top, 50.0));
2224 }
2225
2226 #[test]
2232 fn test_cfx_float_rect_get_bbox() {
2233 assert!(Rect::from_points(&[]).is_none());
2235
2236 let data = vec![Point::new(0.0, 0.0)];
2238 let r = Rect::from_points(&data).unwrap();
2239 assert_eq!(r, Rect::new(0.0, 0.0, 0.0, 0.0));
2240
2241 let data = vec![
2243 Point::new(0.0, 0.0),
2244 Point::new(2.5, 6.2),
2245 Point::new(1.5, 6.2),
2246 ];
2247 let r = Rect::from_points(&data[..2]).unwrap();
2249 assert!(approx_eq(r.left, 0.0));
2250 assert!(approx_eq(r.bottom, 0.0));
2251 assert!(approx_eq(r.right, 2.5));
2252 assert!(approx_eq(r.top, 6.2));
2253
2254 let r = Rect::from_points(&data).unwrap();
2256 assert!(approx_eq(r.left, 0.0));
2257 assert!(approx_eq(r.bottom, 0.0));
2258 assert!(approx_eq(r.right, 2.5));
2259 assert!(approx_eq(r.top, 6.2));
2260
2261 let mut data = data;
2263 data.push(Point::new(2.5, 6.3));
2264 let r = Rect::from_points(&data).unwrap();
2265 assert!(approx_eq(r.right, 2.5));
2266 assert!(approx_eq(r.top, 6.3));
2267
2268 data.push(Point::new(-3.0, 6.3));
2270 let r = Rect::from_points(&data).unwrap();
2271 assert!(approx_eq(r.left, -3.0));
2272 assert!(approx_eq(r.bottom, 0.0));
2273 assert!(approx_eq(r.right, 2.5));
2274 assert!(approx_eq(r.top, 6.3));
2275
2276 data.push(Point::new(4.0, -8.0));
2278 let r = Rect::from_points(&data).unwrap();
2279 assert!(approx_eq(r.left, -3.0));
2280 assert!(approx_eq(r.bottom, -8.0));
2281 assert!(approx_eq(r.right, 4.0));
2282 assert!(approx_eq(r.top, 6.3));
2283 }
2284
2285 #[test]
2287 fn test_cfx_matrix_get_inverse_cr702041() {
2288 let m = Matrix::new(
2293 0.947368443_f64,
2294 -0.108947366,
2295 -0.923076928,
2296 0.106153846,
2297 18.0,
2298 787.929993,
2299 );
2300 let rev = m.inverse().expect("matrix should be invertible");
2302
2303 let transformed = m.transform_point(Point::new(2.0, 3.0));
2306 let result = rev.transform_point(transformed);
2307 let _ = result; }
2312}