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 inverse(&self) -> Option<Self> {
130 let det = self.determinant();
131 if det.abs() < 1e-12 {
132 return None;
133 }
134 let inv_det = 1.0 / det;
135 Some(Self {
136 a: self.d * inv_det,
137 b: -self.b * inv_det,
138 c: -self.c * inv_det,
139 d: self.a * inv_det,
140 e: (self.c * self.f - self.e * self.d) * inv_det,
141 f: (self.e * self.b - self.a * self.f) * inv_det,
142 })
143 }
144}
145
146impl Default for Matrix {
147 fn default() -> Self {
148 Self::identity()
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn identity_concat() {
158 let id = Matrix::identity();
159 let m = Matrix::new(2.0, 0.0, 0.0, 3.0, 10.0, 20.0);
160 assert_eq!(id.concat(&m), m);
161 assert_eq!(m.concat(&id), m);
162 }
163
164 #[test]
165 fn inverse_roundtrip() {
166 let m = Matrix::new(2.0, 1.0, -1.0, 3.0, 5.0, 7.0);
167 let inv = m.inverse().unwrap();
168 let result = m.concat(&inv);
169 let id = Matrix::identity();
170 assert!((result.a - id.a).abs() < 1e-10);
171 assert!((result.d - id.d).abs() < 1e-10);
172 assert!((result.e - id.e).abs() < 1e-10);
173 }
174
175 #[test]
176 fn point_transform() {
177 let m = Matrix::translate(10.0, 20.0);
178 let p = Point::new(1.0, 2.0);
179 let t = p.transform(&m);
180 assert!((t.x - 11.0).abs() < 1e-10);
181 assert!((t.y - 22.0).abs() < 1e-10);
182 }
183
184 #[test]
185 fn rect_normalize() {
186 let r = Rect::new(10.0, 20.0, 5.0, 8.0);
187 let n = r.normalize();
188 assert_eq!(n, Rect::new(5.0, 8.0, 10.0, 20.0));
189 }
190}