1#[derive(Debug, Clone, Copy, PartialEq)]
2pub struct Point {
3 pub x: f64,
4 pub y: f64,
5}
6
7impl Point {
8 pub fn new(x: f64, y: f64) -> Self {
9 Self { x, y }
10 }
11
12 pub fn zero() -> Self {
13 Self { x: 0.0, y: 0.0 }
14 }
15
16 pub fn transform(self, m: &Matrix) -> Self {
17 Self {
18 x: m.a * self.x + m.c * self.y + m.e,
19 y: m.b * self.x + m.d * self.y + m.f,
20 }
21 }
22}
23
24#[derive(Debug, Clone, Copy, PartialEq)]
26pub struct Rect {
27 pub x0: f64,
28 pub y0: f64,
29 pub x1: f64,
30 pub y1: f64,
31}
32
33impl Rect {
34 pub fn new(x0: f64, y0: f64, x1: f64, y1: f64) -> Self {
35 Self { x0, y0, x1, y1 }
36 }
37
38 pub fn width(&self) -> f64 {
39 (self.x1 - self.x0).abs()
40 }
41
42 pub fn height(&self) -> f64 {
43 (self.y1 - self.y0).abs()
44 }
45
46 pub fn normalize(&self) -> Self {
47 Self {
48 x0: self.x0.min(self.x1),
49 y0: self.y0.min(self.y1),
50 x1: self.x0.max(self.x1),
51 y1: self.y0.max(self.y1),
52 }
53 }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq)]
62pub struct Matrix {
63 pub a: f64,
64 pub b: f64,
65 pub c: f64,
66 pub d: f64,
67 pub e: f64,
68 pub f: f64,
69}
70
71impl Matrix {
72 pub fn identity() -> Self {
73 Self {
74 a: 1.0,
75 b: 0.0,
76 c: 0.0,
77 d: 1.0,
78 e: 0.0,
79 f: 0.0,
80 }
81 }
82
83 pub fn new(a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) -> Self {
84 Self { a, b, c, d, e, f }
85 }
86
87 pub fn translate(tx: f64, ty: f64) -> Self {
88 Self {
89 a: 1.0,
90 b: 0.0,
91 c: 0.0,
92 d: 1.0,
93 e: tx,
94 f: ty,
95 }
96 }
97
98 pub fn scale(sx: f64, sy: f64) -> Self {
99 Self {
100 a: sx,
101 b: 0.0,
102 c: 0.0,
103 d: sy,
104 e: 0.0,
105 f: 0.0,
106 }
107 }
108
109 pub fn concat(&self, other: &Matrix) -> Self {
114 Self {
116 a: self.a * other.a + self.c * other.b,
117 b: self.b * other.a + self.d * other.b,
118 c: self.a * other.c + self.c * other.d,
119 d: self.b * other.c + self.d * other.d,
120 e: self.a * other.e + self.c * other.f + self.e,
121 f: self.b * other.e + self.d * other.f + self.f,
122 }
123 }
124
125 pub fn determinant(&self) -> f64 {
126 self.a * self.d - self.b * self.c
127 }
128
129 pub fn is_finite(&self) -> bool {
130 [self.a, self.b, self.c, self.d, self.e, self.f]
131 .iter()
132 .all(|v| v.is_finite())
133 }
134
135 pub fn inverse(&self) -> Option<Self> {
136 if !self.is_finite() {
137 return None;
138 }
139 let det = self.determinant();
140 if !det.is_finite() || det.abs() < 1e-12 {
141 return None;
142 }
143 let inv_det = 1.0 / det;
144 Some(Self {
145 a: self.d * inv_det,
146 b: -self.b * inv_det,
147 c: -self.c * inv_det,
148 d: self.a * inv_det,
149 e: (self.c * self.f - self.e * self.d) * inv_det,
150 f: (self.e * self.b - self.a * self.f) * inv_det,
151 })
152 }
153}
154
155impl Default for Matrix {
156 fn default() -> Self {
157 Self::identity()
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 #[test]
166 fn identity_concat() {
167 let id = Matrix::identity();
168 let m = Matrix::new(2.0, 0.0, 0.0, 3.0, 10.0, 20.0);
169 assert_eq!(id.concat(&m), m);
170 assert_eq!(m.concat(&id), m);
171 }
172
173 #[test]
174 fn inverse_roundtrip() {
175 let m = Matrix::new(2.0, 1.0, -1.0, 3.0, 5.0, 7.0);
176 let inv = m.inverse().unwrap();
177 let result = m.concat(&inv);
178 let id = Matrix::identity();
179 assert!((result.a - id.a).abs() < 1e-10);
180 assert!((result.d - id.d).abs() < 1e-10);
181 assert!((result.e - id.e).abs() < 1e-10);
182 }
183
184 #[test]
185 fn non_finite_matrix_has_no_inverse() {
186 let m = Matrix::new(f64::INFINITY, 0.0, 0.0, 1.0, 0.0, 0.0);
187 assert!(!m.is_finite());
188 assert!(m.inverse().is_none());
189 }
190
191 #[test]
192 fn point_transform() {
193 let m = Matrix::translate(10.0, 20.0);
194 let p = Point::new(1.0, 2.0);
195 let t = p.transform(&m);
196 assert!((t.x - 11.0).abs() < 1e-10);
197 assert!((t.y - 22.0).abs() < 1e-10);
198 }
199
200 #[test]
201 fn rect_normalize() {
202 let r = Rect::new(10.0, 20.0, 5.0, 8.0);
203 let n = r.normalize();
204 assert_eq!(n, Rect::new(5.0, 8.0, 10.0, 20.0));
205 }
206}