1 | // pathfinder/geometry/src/basic/transform2d.rs |
2 | // |
3 | // Copyright © 2019 The Pathfinder Project Developers. |
4 | // |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
8 | // option. This file may not be copied, modified, or distributed |
9 | // except according to those terms. |
10 | |
11 | //! 2D affine transforms. |
12 | |
13 | use crate::line_segment::LineSegment2F; |
14 | use crate::rect::RectF; |
15 | use crate::transform3d::Transform4F; |
16 | use crate::unit_vector::UnitVector; |
17 | use crate::vector::{IntoVector2F, Vector2F, vec2f}; |
18 | use pathfinder_simd::default::F32x4; |
19 | use std::ops::{Mul, MulAssign, Sub}; |
20 | |
21 | /// A 2x2 matrix, optimized with SIMD, in column-major order. |
22 | #[derive (Clone, Copy, Debug, PartialEq)] |
23 | pub struct Matrix2x2F(pub F32x4); |
24 | |
25 | impl Default for Matrix2x2F { |
26 | #[inline ] |
27 | fn default() -> Matrix2x2F { |
28 | Self::from_scale(1.0) |
29 | } |
30 | } |
31 | |
32 | impl Matrix2x2F { |
33 | #[inline ] |
34 | pub fn from_scale<S>(scale: S) -> Matrix2x2F where S: IntoVector2F { |
35 | let scale = scale.into_vector_2f(); |
36 | Matrix2x2F(F32x4::new(scale.x(), 0.0, 0.0, scale.y())) |
37 | } |
38 | |
39 | #[inline ] |
40 | pub fn from_rotation(theta: f32) -> Matrix2x2F { |
41 | Matrix2x2F::from_rotation_vector(UnitVector::from_angle(theta)) |
42 | } |
43 | |
44 | #[inline ] |
45 | pub fn from_rotation_vector(vector: UnitVector) -> Matrix2x2F { |
46 | Matrix2x2F((vector.0).0.to_f32x4().xyyx() * F32x4::new(1.0, 1.0, -1.0, 1.0)) |
47 | } |
48 | |
49 | #[inline ] |
50 | pub fn row_major(m00: f32, m01: f32, m10: f32, m11: f32) -> Matrix2x2F { |
51 | Matrix2x2F(F32x4::new(m00, m10, m01, m11)) |
52 | } |
53 | |
54 | #[inline ] |
55 | pub fn entrywise_mul(&self, other: &Matrix2x2F) -> Matrix2x2F { |
56 | Matrix2x2F(self.0 * other.0) |
57 | } |
58 | |
59 | #[inline ] |
60 | pub fn adjugate(&self) -> Matrix2x2F { |
61 | Matrix2x2F(self.0.wyzx() * F32x4::new(1.0, -1.0, -1.0, 1.0)) |
62 | } |
63 | |
64 | #[inline ] |
65 | pub fn det(&self) -> f32 { |
66 | self.0[0] * self.0[3] - self.0[2] * self.0[1] |
67 | } |
68 | |
69 | #[inline ] |
70 | pub fn inverse(&self) -> Matrix2x2F { |
71 | Matrix2x2F(F32x4::splat(1.0 / self.det()) * self.adjugate().0) |
72 | } |
73 | |
74 | #[inline ] |
75 | pub fn scale(&self, factor: f32) -> Matrix2x2F { |
76 | Matrix2x2F(self.0 * F32x4::splat(factor)) |
77 | } |
78 | |
79 | /// Extracts the scale from this matrix. |
80 | #[inline ] |
81 | pub fn extract_scale(&self) -> Vector2F { |
82 | let squared = self.0 * self.0; |
83 | Vector2F((squared.xy() + squared.zw()).sqrt()) |
84 | } |
85 | |
86 | #[inline ] |
87 | pub fn m11(&self) -> f32 { |
88 | self.0[0] |
89 | } |
90 | |
91 | #[inline ] |
92 | pub fn m21(&self) -> f32 { |
93 | self.0[1] |
94 | } |
95 | |
96 | #[inline ] |
97 | pub fn m12(&self) -> f32 { |
98 | self.0[2] |
99 | } |
100 | |
101 | #[inline ] |
102 | pub fn m22(&self) -> f32 { |
103 | self.0[3] |
104 | } |
105 | } |
106 | |
107 | impl Sub<Matrix2x2F> for Matrix2x2F { |
108 | type Output = Matrix2x2F; |
109 | #[inline ] |
110 | fn sub(self, other: Matrix2x2F) -> Matrix2x2F { |
111 | Matrix2x2F(self.0 - other.0) |
112 | } |
113 | } |
114 | |
115 | impl Mul<Matrix2x2F> for Matrix2x2F { |
116 | type Output = Matrix2x2F; |
117 | #[inline ] |
118 | fn mul(self, other: Matrix2x2F) -> Matrix2x2F { |
119 | Matrix2x2F(self.0.xyxy() * other.0.xxzz() + self.0.zwzw() * other.0.yyww()) |
120 | } |
121 | } |
122 | |
123 | impl Mul<Vector2F> for Matrix2x2F { |
124 | type Output = Vector2F; |
125 | #[inline ] |
126 | fn mul(self, vector: Vector2F) -> Vector2F { |
127 | let halves: F32x4 = self.0 * vector.0.to_f32x4().xxyy(); |
128 | Vector2F(halves.xy() + halves.zw()) |
129 | } |
130 | } |
131 | |
132 | /// An affine transform, optimized with SIMD. |
133 | #[derive (Clone, Copy, Debug, PartialEq)] |
134 | pub struct Transform2F { |
135 | pub matrix: Matrix2x2F, |
136 | pub vector: Vector2F, |
137 | } |
138 | |
139 | impl Default for Transform2F { |
140 | #[inline ] |
141 | fn default() -> Transform2F { |
142 | Self::from_scale(vec2f(x:1.0, y:1.0)) |
143 | } |
144 | } |
145 | |
146 | impl Transform2F { |
147 | #[inline ] |
148 | pub fn from_scale<S>(scale: S) -> Transform2F where S: IntoVector2F { |
149 | let scale = scale.into_vector_2f(); |
150 | Transform2F { |
151 | matrix: Matrix2x2F::from_scale(scale), |
152 | vector: Vector2F::zero(), |
153 | } |
154 | } |
155 | |
156 | #[inline ] |
157 | pub fn from_rotation(theta: f32) -> Transform2F { |
158 | Transform2F { |
159 | matrix: Matrix2x2F::from_rotation(theta), |
160 | vector: Vector2F::zero(), |
161 | } |
162 | } |
163 | |
164 | #[inline ] |
165 | pub fn from_rotation_vector(vector: UnitVector) -> Transform2F { |
166 | Transform2F { |
167 | matrix: Matrix2x2F::from_rotation_vector(vector), |
168 | vector: Vector2F::zero(), |
169 | } |
170 | } |
171 | |
172 | #[inline ] |
173 | pub fn from_translation(vector: Vector2F) -> Transform2F { |
174 | Transform2F { matrix: Matrix2x2F::default(), vector } |
175 | } |
176 | |
177 | #[inline ] |
178 | pub fn from_scale_rotation_translation<S>(scale: S, theta: f32, translation: Vector2F) |
179 | -> Transform2F where S: IntoVector2F { |
180 | let scale = scale.into_vector_2f(); |
181 | let rotation = Transform2F::from_rotation(theta); |
182 | let translation = Transform2F::from_translation(translation); |
183 | Transform2F::from_scale(scale) * rotation * translation |
184 | } |
185 | |
186 | #[inline ] |
187 | pub fn row_major(m11: f32, m12: f32, m21: f32, m22: f32, m31: f32, m32: f32) -> Transform2F { |
188 | Transform2F { |
189 | matrix: Matrix2x2F::row_major(m11, m12, m21, m22), |
190 | vector: Vector2F::new(m31, m32), |
191 | } |
192 | } |
193 | |
194 | // TODO(pcwalton): Optimize better with SIMD. |
195 | #[inline ] |
196 | pub fn to_3d(&self) -> Transform4F { |
197 | Transform4F::row_major( |
198 | self.matrix.0[0], |
199 | self.matrix.0[1], |
200 | 0.0, |
201 | self.vector.x(), |
202 | self.matrix.0[2], |
203 | self.matrix.0[3], |
204 | 0.0, |
205 | self.vector.y(), |
206 | 0.0, |
207 | 0.0, |
208 | 0.0, |
209 | 0.0, |
210 | 0.0, |
211 | 0.0, |
212 | 0.0, |
213 | 1.0, |
214 | ) |
215 | } |
216 | |
217 | #[inline ] |
218 | pub fn is_identity(&self) -> bool { |
219 | *self == Transform2F::default() |
220 | } |
221 | |
222 | /// Extracts the scale from this matrix. |
223 | #[inline ] |
224 | pub fn extract_scale(&self) -> Vector2F { |
225 | self.matrix.extract_scale() |
226 | } |
227 | |
228 | #[inline ] |
229 | pub fn m11(&self) -> f32 { |
230 | self.matrix.m11() |
231 | } |
232 | #[inline ] |
233 | pub fn m21(&self) -> f32 { |
234 | self.matrix.m21() |
235 | } |
236 | #[inline ] |
237 | pub fn m12(&self) -> f32 { |
238 | self.matrix.m12() |
239 | } |
240 | #[inline ] |
241 | pub fn m22(&self) -> f32 { |
242 | self.matrix.m22() |
243 | } |
244 | #[inline ] |
245 | pub fn m31(&self) -> f32 { |
246 | self.vector.x() |
247 | } |
248 | #[inline ] |
249 | pub fn m32(&self) -> f32 { |
250 | self.vector.y() |
251 | } |
252 | |
253 | #[inline ] |
254 | pub fn translate(&self, vector: Vector2F) -> Transform2F { |
255 | Transform2F::from_translation(vector) * *self |
256 | } |
257 | |
258 | #[inline ] |
259 | pub fn rotate(&self, theta: f32) -> Transform2F { |
260 | Transform2F::from_rotation(theta) * *self |
261 | } |
262 | |
263 | #[inline ] |
264 | pub fn scale<S>(&self, scale: S) -> Transform2F where S: IntoVector2F { |
265 | let scale = scale.into_vector_2f(); |
266 | Transform2F::from_scale(scale) * *self |
267 | } |
268 | |
269 | /// Returns the translation part of this matrix. |
270 | /// |
271 | /// This decomposition assumes that scale, rotation, and translation are applied in that order. |
272 | #[inline ] |
273 | pub fn translation(&self) -> Vector2F { |
274 | self.vector |
275 | } |
276 | |
277 | /// Returns the rotation angle of this matrix. |
278 | /// |
279 | /// This decomposition assumes that scale, rotation, and translation are applied in that order. |
280 | #[inline ] |
281 | pub fn rotation(&self) -> f32 { |
282 | f32::atan2(self.m21(), self.m11()) |
283 | } |
284 | |
285 | /// Returns the scale factor of this matrix. |
286 | /// |
287 | /// This decomposition assumes that scale, rotation, and translation are applied in that order. |
288 | #[inline ] |
289 | pub fn scale_factor(&self) -> f32 { |
290 | Vector2F(self.matrix.0.zw()).length() |
291 | } |
292 | |
293 | #[inline ] |
294 | pub fn inverse(&self) -> Transform2F { |
295 | let matrix_inv = self.matrix.inverse(); |
296 | let vector_inv = -(matrix_inv * self.vector); |
297 | Transform2F { matrix: matrix_inv, vector: vector_inv } |
298 | } |
299 | } |
300 | |
301 | impl Mul<Transform2F> for Transform2F { |
302 | type Output = Transform2F; |
303 | #[inline ] |
304 | fn mul(self, other: Transform2F) -> Transform2F { |
305 | Transform2F { |
306 | matrix: self.matrix * other.matrix, |
307 | vector: self * other.vector, |
308 | } |
309 | } |
310 | } |
311 | |
312 | impl Mul<Vector2F> for Transform2F { |
313 | type Output = Vector2F; |
314 | #[inline ] |
315 | fn mul(self, vector: Vector2F) -> Vector2F { |
316 | self.matrix * vector + self.vector |
317 | } |
318 | } |
319 | |
320 | impl Mul<LineSegment2F> for Transform2F { |
321 | type Output = LineSegment2F; |
322 | #[inline ] |
323 | fn mul(self, line_segment: LineSegment2F) -> LineSegment2F { |
324 | LineSegment2F::new(self * line_segment.from(), self * line_segment.to()) |
325 | } |
326 | } |
327 | |
328 | impl Mul<RectF> for Transform2F { |
329 | type Output = RectF; |
330 | #[inline ] |
331 | fn mul(self, rect: RectF) -> RectF { |
332 | let (upper_left: Vector2F, upper_right: Vector2F) = (self * rect.origin(), self * rect.upper_right()); |
333 | let (lower_left: Vector2F, lower_right: Vector2F) = (self * rect.lower_left(), self * rect.lower_right()); |
334 | let min_point: Vector2F = upper_left.min(upper_right).min(lower_left).min(lower_right); |
335 | let max_point: Vector2F = upper_left.max(upper_right).max(lower_left).max(lower_right); |
336 | RectF::from_points(origin:min_point, lower_right:max_point) |
337 | } |
338 | } |
339 | |
340 | impl MulAssign for Transform2F { |
341 | #[inline ] |
342 | fn mul_assign(&mut self, other: Transform2F) { |
343 | *self = *self * other |
344 | } |
345 | } |
346 | |