1use core::ops::{Add, Mul, Neg, Sub};
4
5#[derive(Copy, Clone, Debug, Default, PartialEq)]
7pub struct Point {
8 pub x: f32,
9 pub y: f32,
10}
11
12impl Point {
13 pub const ZERO: Point = Point { x: 0.0, y: 0.0 };
14
15 #[inline]
16 pub const fn new(x: f32, y: f32) -> Self {
17 Point { x, y }
18 }
19
20 #[inline]
22 pub fn length(self) -> f32 {
23 (self.x * self.x + self.y * self.y).sqrt()
24 }
25
26 #[inline]
28 pub fn distance(self, other: Point) -> f32 {
29 (self - other).length()
30 }
31
32 #[inline]
34 pub fn dot(self, other: Point) -> f32 {
35 self.x * other.x + self.y * other.y
36 }
37
38 #[inline]
40 pub fn cross(self, other: Point) -> f32 {
41 self.x * other.y - self.y * other.x
42 }
43
44 #[inline]
46 pub fn normalize(self) -> Point {
47 let len = self.length();
48 if len > 0.0 {
49 Point::new(self.x / len, self.y / len)
50 } else {
51 Point::ZERO
52 }
53 }
54
55 #[inline]
57 pub fn left_normal(self) -> Point {
58 Point::new(-self.y, self.x)
59 }
60
61 #[inline]
63 pub fn lerp(self, other: Point, t: f32) -> Point {
64 Point::new(self.x + (other.x - self.x) * t, self.y + (other.y - self.y) * t)
65 }
66}
67
68impl Add for Point {
69 type Output = Point;
70 #[inline]
71 fn add(self, rhs: Point) -> Point {
72 Point::new(self.x + rhs.x, self.y + rhs.y)
73 }
74}
75
76impl Sub for Point {
77 type Output = Point;
78 #[inline]
79 fn sub(self, rhs: Point) -> Point {
80 Point::new(self.x - rhs.x, self.y - rhs.y)
81 }
82}
83
84impl Mul<f32> for Point {
85 type Output = Point;
86 #[inline]
87 fn mul(self, rhs: f32) -> Point {
88 Point::new(self.x * rhs, self.y * rhs)
89 }
90}
91
92impl Neg for Point {
93 type Output = Point;
94 #[inline]
95 fn neg(self) -> Point {
96 Point::new(-self.x, -self.y)
97 }
98}
99
100#[derive(Copy, Clone, Debug, PartialEq)]
102pub struct Rect {
103 pub left: f32,
104 pub top: f32,
105 pub right: f32,
106 pub bottom: f32,
107}
108
109impl Rect {
110 pub fn from_xywh(x: f32, y: f32, w: f32, h: f32) -> Option<Rect> {
113 let valid = w > 0.0 && h > 0.0 && x.is_finite() && y.is_finite();
114 if !valid {
115 return None;
116 }
117 Some(Rect { left: x, top: y, right: x + w, bottom: y + h })
118 }
119
120 pub fn from_ltrb(left: f32, top: f32, right: f32, bottom: f32) -> Option<Rect> {
122 let (left, right) = if left <= right { (left, right) } else { (right, left) };
123 let (top, bottom) = if top <= bottom { (top, bottom) } else { (bottom, top) };
124 if right > left && bottom > top {
125 Some(Rect { left, top, right, bottom })
126 } else {
127 None
128 }
129 }
130
131 #[inline]
132 pub fn width(&self) -> f32 {
133 self.right - self.left
134 }
135
136 #[inline]
137 pub fn height(&self) -> f32 {
138 self.bottom - self.top
139 }
140
141 #[inline]
142 pub fn center(&self) -> Point {
143 Point::new((self.left + self.right) * 0.5, (self.top + self.bottom) * 0.5)
144 }
145
146 #[inline]
148 pub fn contains(&self, p: Point) -> bool {
149 p.x >= self.left && p.x < self.right && p.y >= self.top && p.y < self.bottom
150 }
151}
152
153#[derive(Copy, Clone, Debug, PartialEq)]
159pub struct Transform {
160 pub sx: f32,
161 pub ky: f32,
162 pub kx: f32,
163 pub sy: f32,
164 pub tx: f32,
165 pub ty: f32,
166}
167
168impl Default for Transform {
169 fn default() -> Self {
170 Transform::identity()
171 }
172}
173
174impl Transform {
175 #[inline]
176 pub const fn identity() -> Self {
177 Transform { sx: 1.0, ky: 0.0, kx: 0.0, sy: 1.0, tx: 0.0, ty: 0.0 }
178 }
179
180 #[inline]
181 pub const fn from_row(sx: f32, ky: f32, kx: f32, sy: f32, tx: f32, ty: f32) -> Self {
182 Transform { sx, ky, kx, sy, tx, ty }
183 }
184
185 #[inline]
186 pub const fn from_translate(tx: f32, ty: f32) -> Self {
187 Transform { sx: 1.0, ky: 0.0, kx: 0.0, sy: 1.0, tx, ty }
188 }
189
190 #[inline]
191 pub const fn from_scale(sx: f32, sy: f32) -> Self {
192 Transform { sx, ky: 0.0, kx: 0.0, sy, tx: 0.0, ty: 0.0 }
193 }
194
195 pub fn from_rotate(degrees: f32) -> Self {
197 let r = degrees.to_radians();
198 let (s, c) = r.sin_cos();
199 Transform { sx: c, ky: s, kx: -s, sy: c, tx: 0.0, ty: 0.0 }
200 }
201
202 pub fn from_rotate_at(degrees: f32, cx: f32, cy: f32) -> Self {
204 Transform::from_translate(cx, cy)
205 .pre_concat(Transform::from_rotate(degrees))
206 .pre_concat(Transform::from_translate(-cx, -cy))
207 }
208
209 #[inline]
210 pub fn is_identity(&self) -> bool {
211 *self == Transform::identity()
212 }
213
214 pub fn pre_concat(&self, other: Transform) -> Transform {
216 Transform {
217 sx: self.sx * other.sx + self.kx * other.ky,
218 ky: self.ky * other.sx + self.sy * other.ky,
219 kx: self.sx * other.kx + self.kx * other.sy,
220 sy: self.ky * other.kx + self.sy * other.sy,
221 tx: self.sx * other.tx + self.kx * other.ty + self.tx,
222 ty: self.ky * other.tx + self.sy * other.ty + self.ty,
223 }
224 }
225
226 pub fn post_concat(&self, other: Transform) -> Transform {
228 other.pre_concat(*self)
229 }
230
231 #[inline]
233 pub fn map_point(&self, p: Point) -> Point {
234 Point::new(self.sx * p.x + self.kx * p.y + self.tx, self.ky * p.x + self.sy * p.y + self.ty)
235 }
236
237 pub fn map_points(&self, points: &mut [Point]) {
239 for p in points.iter_mut() {
240 *p = self.map_point(*p);
241 }
242 }
243
244 pub fn invert(&self) -> Option<Transform> {
246 let det = self.sx * self.sy - self.kx * self.ky;
247 let scale = self
253 .sx
254 .abs()
255 .max(self.ky.abs())
256 .max(self.kx.abs())
257 .max(self.sy.abs());
258 if !det.is_finite() || det.abs() <= f32::EPSILON * scale * scale {
259 return None;
260 }
261 let inv = 1.0 / det;
262 Some(Transform {
263 sx: self.sy * inv,
264 ky: -self.ky * inv,
265 kx: -self.kx * inv,
266 sy: self.sx * inv,
267 tx: (self.kx * self.ty - self.sy * self.tx) * inv,
268 ty: (self.ky * self.tx - self.sx * self.ty) * inv,
269 })
270 }
271
272 pub fn max_scale(&self) -> f32 {
275 let sa = (self.sx * self.sx + self.ky * self.ky).sqrt();
276 let sb = (self.kx * self.kx + self.sy * self.sy).sqrt();
277 sa.max(sb)
278 }
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284
285 #[test]
286 fn transform_roundtrip() {
287 let t = Transform::from_translate(10.0, 5.0)
288 .pre_concat(Transform::from_rotate(33.0))
289 .pre_concat(Transform::from_scale(2.0, 3.0));
290 let inv = t.invert().unwrap();
291 let p = Point::new(7.0, -4.0);
292 let mapped = inv.map_point(t.map_point(p));
293 assert!((mapped.x - p.x).abs() < 1e-3, "{mapped:?}");
294 assert!((mapped.y - p.y).abs() < 1e-3, "{mapped:?}");
295 }
296
297 #[test]
298 fn invert_small_scale_is_not_degenerate() {
299 let t = Transform::from_scale(1e-4, 1e-4);
302 let inv = t.invert().expect("a matrix with a small scale is invertible");
303 let p = Point::new(3.0, -5.0);
304 let mapped = inv.map_point(t.map_point(p));
305 assert!((mapped.x - p.x).abs() < 1e-2, "{mapped:?}");
306 assert!((mapped.y - p.y).abs() < 1e-2, "{mapped:?}");
307 }
308
309 #[test]
310 fn invert_singular_is_none() {
311 assert!(Transform::from_scale(1.0, 0.0).invert().is_none());
313 }
314
315 #[test]
316 fn translate_then_scale_order() {
317 let t = Transform::from_scale(2.0, 2.0).pre_concat(Transform::from_translate(1.0, 1.0));
319 assert_eq!(t.map_point(Point::ZERO), Point::new(2.0, 2.0));
321 }
322}