1use serde::{Deserialize, Serialize};
2
3use std::marker::PhantomData;
4
5use crate::{Px, PxBox, PxPoint, PxVector};
6
7#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
9pub enum PxTransform {
10 Offset(euclid::Vector2D<f32, Px>),
12 #[serde(with = "serde_px_transform3d")]
14 Transform(euclid::Transform3D<f32, Px, Px>),
15}
16
17impl PartialEq for PxTransform {
18 fn eq(&self, other: &Self) -> bool {
19 match (self, other) {
20 (Self::Offset(l0), Self::Offset(r0)) => l0 == r0,
21 (Self::Transform(l0), Self::Transform(r0)) => l0 == r0,
22 (a, b) => a.is_identity() && b.is_identity() || a.to_transform() == b.to_transform(),
23 }
24 }
25}
26impl Default for PxTransform {
27 fn default() -> Self {
29 Self::identity()
30 }
31}
32impl PxTransform {
33 pub fn identity() -> Self {
35 PxTransform::Offset(euclid::vec2(0.0, 0.0))
36 }
37
38 pub fn translation(x: f32, y: f32) -> Self {
40 PxTransform::Offset(euclid::vec2(x, y))
41 }
42
43 pub fn translation_3d(x: f32, y: f32, z: f32) -> Self {
45 PxTransform::Transform(euclid::Transform3D::translation(x, y, z))
46 }
47
48 pub fn rotation(x: f32, y: f32, theta: euclid::Angle<f32>) -> Self {
50 Self::rotation_3d(x, y, 1.0, theta)
51 }
52
53 pub fn rotation_3d(x: f32, y: f32, z: f32, theta: euclid::Angle<f32>) -> Self {
55 let [x, y, z] = euclid::vec3::<_, ()>(x, y, z).normalize().to_array();
56 PxTransform::Transform(euclid::Transform3D::rotation(x, y, z, theta))
57 }
58
59 pub fn skew(alpha: euclid::Angle<f32>, beta: euclid::Angle<f32>) -> Self {
61 PxTransform::Transform(euclid::Transform3D::skew(alpha, beta))
62 }
63
64 pub fn scale(x: f32, y: f32) -> Self {
66 PxTransform::Transform(euclid::Transform3D::scale(x, y, 1.0))
67 }
68
69 pub fn scale_3d(x: f32, y: f32, z: f32) -> Self {
71 PxTransform::Transform(euclid::Transform3D::scale(x, y, z))
72 }
73
74 pub fn perspective(d: f32) -> Self {
76 PxTransform::Transform(euclid::Transform3D::perspective(d))
77 }
78
79 pub fn to_transform(self) -> euclid::Transform3D<f32, Px, Px> {
81 match self {
82 PxTransform::Offset(v) => euclid::Transform3D::translation(v.x, v.y, 0.0),
83 PxTransform::Transform(t) => t,
84 }
85 }
86
87 pub fn is_identity(&self) -> bool {
89 match self {
90 PxTransform::Offset(offset) => offset == &euclid::Vector2D::zero(),
91 PxTransform::Transform(transform) => transform == &euclid::Transform3D::identity(),
92 }
93 }
94
95 #[must_use]
98 pub fn then(&self, other: &PxTransform) -> PxTransform {
99 match (self, other) {
100 (PxTransform::Offset(a), PxTransform::Offset(b)) => PxTransform::Offset(*a + *b),
101 (PxTransform::Offset(a), PxTransform::Transform(b)) => {
102 PxTransform::Transform(euclid::Transform3D::translation(a.x, a.y, 0.0).then(b))
103 }
104 (PxTransform::Transform(a), PxTransform::Offset(b)) => PxTransform::Transform(a.then_translate(b.to_3d())),
105 (PxTransform::Transform(a), PxTransform::Transform(b)) => PxTransform::Transform(a.then(b)),
106 }
107 }
108
109 #[must_use]
111 pub fn then_translate(&self, offset: euclid::Vector2D<f32, Px>) -> PxTransform {
112 match self {
113 PxTransform::Offset(a) => PxTransform::Offset(*a + offset),
114 PxTransform::Transform(a) => PxTransform::Transform(a.then_translate(offset.to_3d())),
115 }
116 }
117
118 #[must_use]
120 pub fn pre_translate(&self, offset: euclid::Vector2D<f32, Px>) -> PxTransform {
121 match self {
122 PxTransform::Offset(b) => PxTransform::Offset(offset + *b),
123 PxTransform::Transform(b) => PxTransform::Transform(euclid::Transform3D::translation(offset.x, offset.y, 0.0).then(b)),
124 }
125 }
126
127 pub fn is_invertible(&self) -> bool {
129 match self {
130 PxTransform::Offset(_) => true,
131 PxTransform::Transform(t) => t.is_invertible(),
132 }
133 }
134
135 pub fn inverse(&self) -> Option<PxTransform> {
137 match self {
138 PxTransform::Offset(v) => Some(PxTransform::Offset(-*v)),
139 PxTransform::Transform(t) => t.inverse().map(PxTransform::Transform),
140 }
141 }
142
143 pub fn is_2d(&self) -> bool {
145 match self {
146 PxTransform::Offset(_) => true,
147 PxTransform::Transform(t) => t.is_2d(),
148 }
149 }
150
151 pub fn transform_point(&self, point: PxPoint) -> Option<PxPoint> {
159 self.transform_point_f32(point.cast()).map(|p| p.cast())
160 }
161
162 pub fn transform_point_f32(&self, point: euclid::Point2D<f32, Px>) -> Option<euclid::Point2D<f32, Px>> {
170 match self {
171 PxTransform::Offset(v) => Some(point + *v),
172 PxTransform::Transform(t) => t.transform_point2d(point),
173 }
174 }
175
176 pub fn transform_vector(&self, vector: PxVector) -> PxVector {
178 self.transform_vector_f32(vector.cast()).cast()
179 }
180
181 pub fn transform_vector_f32(&self, vector: euclid::Vector2D<f32, Px>) -> euclid::Vector2D<f32, Px> {
183 match self {
184 PxTransform::Offset(v) => vector + *v,
185 PxTransform::Transform(t) => t.transform_vector2d(vector),
186 }
187 }
188
189 pub fn project_point(&self, point: PxPoint) -> Option<PxPoint> {
191 self.project_point_f32(point.cast()).map(|p| p.cast())
192 }
193
194 pub fn project_point_f32(&self, point: euclid::Point2D<f32, Px>) -> Option<euclid::Point2D<f32, Px>> {
196 match self {
197 PxTransform::Offset(v) => Some(point + *v),
198 PxTransform::Transform(t) => {
199 let z = -(point.x * t.m13 + point.y * t.m23 + t.m43) / t.m33;
208
209 t.transform_point3d(euclid::point3(point.x, point.y, z))
210 .map(|p3| euclid::point2(p3.x, p3.y))
211 }
212 }
213 }
214
215 pub fn outer_transformed(&self, px_box: PxBox) -> Option<PxBox> {
218 self.outer_transformed_f32(px_box.cast()).map(|p| p.cast())
219 }
220
221 pub fn outer_transformed_f32(&self, px_box: euclid::Box2D<f32, Px>) -> Option<euclid::Box2D<f32, Px>> {
224 match self {
225 PxTransform::Offset(v) => {
226 let v = *v;
227 let mut r = px_box;
228 r.min += v;
229 r.max += v;
230 Some(r)
231 }
232 PxTransform::Transform(t) => t.outer_transformed_box2d(&px_box),
233 }
234 }
235}
236
237impl From<euclid::Vector2D<f32, Px>> for PxTransform {
238 fn from(offset: euclid::Vector2D<f32, Px>) -> Self {
239 PxTransform::Offset(offset)
240 }
241}
242impl From<PxVector> for PxTransform {
243 fn from(offset: PxVector) -> Self {
244 PxTransform::Offset(offset.cast())
245 }
246}
247impl From<euclid::Transform3D<f32, Px, Px>> for PxTransform {
248 fn from(transform: euclid::Transform3D<f32, Px, Px>) -> Self {
249 PxTransform::Transform(transform)
250 }
251}
252
253mod serde_px_transform3d {
255 use crate::Px;
256
257 use super::*;
258 use serde::*;
259
260 #[derive(Serialize, Deserialize)]
261 struct SerdeTransform3D {
262 pub m11: f32,
263 pub m12: f32,
264 pub m13: f32,
265 pub m14: f32,
266 pub m21: f32,
267 pub m22: f32,
268 pub m23: f32,
269 pub m24: f32,
270 pub m31: f32,
271 pub m32: f32,
272 pub m33: f32,
273 pub m34: f32,
274 pub m41: f32,
275 pub m42: f32,
276 pub m43: f32,
277 pub m44: f32,
278 }
279
280 pub fn serialize<S: Serializer>(t: &euclid::Transform3D<f32, Px, Px>, serializer: S) -> Result<S::Ok, S::Error> {
281 SerdeTransform3D {
282 m11: t.m11,
283 m12: t.m12,
284 m13: t.m13,
285 m14: t.m14,
286 m21: t.m21,
287 m22: t.m22,
288 m23: t.m23,
289 m24: t.m24,
290 m31: t.m31,
291 m32: t.m32,
292 m33: t.m33,
293 m34: t.m34,
294 m41: t.m41,
295 m42: t.m42,
296 m43: t.m43,
297 m44: t.m44,
298 }
299 .serialize(serializer)
300 }
301
302 pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<euclid::Transform3D<f32, Px, Px>, D::Error> {
303 let t = SerdeTransform3D::deserialize(deserializer)?;
304 Ok(euclid::Transform3D {
305 m11: t.m11,
306 m12: t.m12,
307 m13: t.m13,
308 m14: t.m14,
309 m21: t.m21,
310 m22: t.m22,
311 m23: t.m23,
312 m24: t.m24,
313 m31: t.m31,
314 m32: t.m32,
315 m33: t.m33,
316 m34: t.m34,
317 m41: t.m41,
318 m42: t.m42,
319 m43: t.m43,
320 m44: t.m44,
321 _unit: PhantomData,
322 })
323 }
324}