1#[derive(Clone, Copy, Debug, Default, PartialEq)]
2pub struct Point {
3 pub x: f32,
4 pub y: f32,
5}
6
7impl Point {
8 #[must_use]
9 pub const fn new(x: f32, y: f32) -> Self {
10 Self { x, y }
11 }
12}
13
14#[derive(Clone, Copy, Debug, Default, PartialEq)]
15pub struct Size {
16 pub width: f32,
17 pub height: f32,
18}
19
20impl Size {
21 #[must_use]
22 pub const fn new(width: f32, height: f32) -> Self {
23 Self { width, height }
24 }
25}
26
27#[derive(Clone, Copy, Debug, Default, PartialEq)]
28pub struct Rect {
29 pub origin: Point,
30 pub size: Size,
31}
32
33impl Rect {
34 #[must_use]
35 pub const fn new(origin: Point, size: Size) -> Self {
36 Self { origin, size }
37 }
38
39 #[must_use]
40 pub fn contains(self, point: Point) -> bool {
41 point.x >= self.origin.x
42 && point.y >= self.origin.y
43 && point.x <= self.origin.x + self.size.width
44 && point.y <= self.origin.y + self.size.height
45 }
46
47 #[must_use]
48 pub fn intersection(self, other: Self) -> Option<Self> {
49 let left = self.origin.x.max(other.origin.x);
50 let top = self.origin.y.max(other.origin.y);
51 let right = (self.origin.x + self.size.width).min(other.origin.x + other.size.width);
52 let bottom = (self.origin.y + self.size.height).min(other.origin.y + other.size.height);
53 (right > left && bottom > top)
54 .then(|| Self::new(Point::new(left, top), Size::new(right - left, bottom - top)))
55 }
56
57 #[must_use]
58 pub fn corners(self) -> [Point; 4] {
59 let right = self.origin.x + self.size.width;
60 let bottom = self.origin.y + self.size.height;
61 [
62 self.origin,
63 Point::new(right, self.origin.y),
64 Point::new(right, bottom),
65 Point::new(self.origin.x, bottom),
66 ]
67 }
68}
69
70#[derive(Clone, Copy, Debug, PartialEq)]
72pub struct Transform2D {
73 pub translation: Point,
74 pub scale: Point,
75 pub rotation: f32,
76 pub skew: Point,
77}
78
79impl Default for Transform2D {
80 fn default() -> Self {
81 Self::IDENTITY
82 }
83}
84
85impl Transform2D {
86 pub const IDENTITY: Self = Self {
87 translation: Point::new(0.0, 0.0),
88 scale: Point::new(1.0, 1.0),
89 rotation: 0.0,
90 skew: Point::new(0.0, 0.0),
91 };
92
93 #[must_use]
94 pub const fn translate(mut self, x: f32, y: f32) -> Self {
95 self.translation = Point::new(x, y);
96 self
97 }
98
99 #[must_use]
100 pub const fn scale(mut self, x: f32, y: f32) -> Self {
101 self.scale = Point::new(x, y);
102 self
103 }
104
105 #[must_use]
106 pub const fn rotate(mut self, radians: f32) -> Self {
107 self.rotation = radians;
108 self
109 }
110
111 #[must_use]
112 pub const fn skew(mut self, x_radians: f32, y_radians: f32) -> Self {
113 self.skew = Point::new(x_radians, y_radians);
114 self
115 }
116
117 #[must_use]
118 pub fn affine(self, bounds: Rect, origin: TransformOrigin) -> Affine2D {
119 let pivot = Point::new(
120 bounds.origin.x + bounds.size.width * origin.x,
121 bounds.origin.y + bounds.size.height * origin.y,
122 );
123 let (sin, cos) = self.rotation.sin_cos();
124 let skew_x = self.skew.x.tan();
125 let skew_y = self.skew.y.tan();
126 let linear = Affine2D {
127 matrix: [
128 self.scale.x * (cos - sin * skew_y),
129 self.scale.x * (sin + cos * skew_y),
130 self.scale.y * (cos * skew_x - sin),
131 self.scale.y * (sin * skew_x + cos),
132 ],
133 translation: Point::default(),
134 };
135 Affine2D::translation(self.translation.x + pivot.x, self.translation.y + pivot.y)
136 * linear
137 * Affine2D::translation(-pivot.x, -pivot.y)
138 }
139}
140
141#[derive(Clone, Copy, Debug, PartialEq)]
142pub struct TransformOrigin {
143 pub x: f32,
144 pub y: f32,
145}
146
147impl TransformOrigin {
148 pub const TOP_LEFT: Self = Self { x: 0.0, y: 0.0 };
149 pub const CENTER: Self = Self { x: 0.5, y: 0.5 };
150
151 #[must_use]
152 pub const fn new(x: f32, y: f32) -> Self {
153 Self { x, y }
154 }
155}
156
157impl Default for TransformOrigin {
158 fn default() -> Self {
159 Self::CENTER
160 }
161}
162
163#[derive(Clone, Copy, Debug, PartialEq)]
165pub struct Affine2D {
166 pub matrix: [f32; 4],
167 pub translation: Point,
168}
169
170impl Default for Affine2D {
171 fn default() -> Self {
172 Self::IDENTITY
173 }
174}
175
176impl Affine2D {
177 pub const IDENTITY: Self = Self {
178 matrix: [1.0, 0.0, 0.0, 1.0],
179 translation: Point::new(0.0, 0.0),
180 };
181
182 #[must_use]
183 pub const fn translation(x: f32, y: f32) -> Self {
184 Self {
185 matrix: [1.0, 0.0, 0.0, 1.0],
186 translation: Point::new(x, y),
187 }
188 }
189
190 #[must_use]
191 pub fn transform_point(self, point: Point) -> Point {
192 Point::new(
193 self.matrix[0] * point.x + self.matrix[2] * point.y + self.translation.x,
194 self.matrix[1] * point.x + self.matrix[3] * point.y + self.translation.y,
195 )
196 }
197
198 #[must_use]
199 pub fn transform_rect(self, rect: Rect) -> Rect {
200 let corners = rect.corners().map(|point| self.transform_point(point));
201 let left = corners.iter().map(|p| p.x).fold(f32::INFINITY, f32::min);
202 let top = corners.iter().map(|p| p.y).fold(f32::INFINITY, f32::min);
203 let right = corners
204 .iter()
205 .map(|p| p.x)
206 .fold(f32::NEG_INFINITY, f32::max);
207 let bottom = corners
208 .iter()
209 .map(|p| p.y)
210 .fold(f32::NEG_INFINITY, f32::max);
211 Rect::new(Point::new(left, top), Size::new(right - left, bottom - top))
212 }
213
214 #[must_use]
215 pub fn inverse(self) -> Option<Self> {
216 let determinant = self.matrix[0] * self.matrix[3] - self.matrix[1] * self.matrix[2];
217 if determinant.abs() <= f32::EPSILON {
218 return None;
219 }
220 let inverse = [
221 self.matrix[3] / determinant,
222 -self.matrix[1] / determinant,
223 -self.matrix[2] / determinant,
224 self.matrix[0] / determinant,
225 ];
226 let translation = Point::new(
227 -(inverse[0] * self.translation.x + inverse[2] * self.translation.y),
228 -(inverse[1] * self.translation.x + inverse[3] * self.translation.y),
229 );
230 Some(Self {
231 matrix: inverse,
232 translation,
233 })
234 }
235
236 #[must_use]
237 pub const fn scaled(self, factor: f32) -> Self {
238 Self {
239 matrix: self.matrix,
240 translation: Point::new(self.translation.x * factor, self.translation.y * factor),
241 }
242 }
243}
244
245impl core::ops::Mul for Affine2D {
246 type Output = Self;
247
248 fn mul(self, rhs: Self) -> Self::Output {
249 Self {
250 matrix: [
251 self.matrix[0] * rhs.matrix[0] + self.matrix[2] * rhs.matrix[1],
252 self.matrix[1] * rhs.matrix[0] + self.matrix[3] * rhs.matrix[1],
253 self.matrix[0] * rhs.matrix[2] + self.matrix[2] * rhs.matrix[3],
254 self.matrix[1] * rhs.matrix[2] + self.matrix[3] * rhs.matrix[3],
255 ],
256 translation: self.transform_point(rhs.translation),
257 }
258 }
259}