use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use crate::{Px, PxBox, PxPoint, PxVector};
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum PxTransform {
Offset(euclid::Vector2D<f32, Px>),
#[serde(with = "serde_px_transform3d")]
Transform(euclid::Transform3D<f32, Px, Px>),
}
impl PartialEq for PxTransform {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Offset(l0), Self::Offset(r0)) => l0 == r0,
(Self::Transform(l0), Self::Transform(r0)) => l0 == r0,
(a, b) => a.is_identity() && b.is_identity() || a.to_transform() == b.to_transform(),
}
}
}
impl Default for PxTransform {
fn default() -> Self {
Self::identity()
}
}
impl PxTransform {
pub fn identity() -> Self {
PxTransform::Offset(euclid::vec2(0.0, 0.0))
}
pub fn translation(x: f32, y: f32) -> Self {
PxTransform::Offset(euclid::vec2(x, y))
}
pub fn translation_3d(x: f32, y: f32, z: f32) -> Self {
PxTransform::Transform(euclid::Transform3D::translation(x, y, z))
}
pub fn rotation(x: f32, y: f32, theta: euclid::Angle<f32>) -> Self {
Self::rotation_3d(x, y, 1.0, theta)
}
pub fn rotation_3d(x: f32, y: f32, z: f32, theta: euclid::Angle<f32>) -> Self {
let [x, y, z] = euclid::vec3::<_, ()>(x, y, z).normalize().to_array();
PxTransform::Transform(euclid::Transform3D::rotation(x, y, z, theta))
}
pub fn skew(alpha: euclid::Angle<f32>, beta: euclid::Angle<f32>) -> Self {
PxTransform::Transform(euclid::Transform3D::skew(alpha, beta))
}
pub fn scale(x: f32, y: f32) -> Self {
PxTransform::Transform(euclid::Transform3D::scale(x, y, 1.0))
}
pub fn scale_3d(x: f32, y: f32, z: f32) -> Self {
PxTransform::Transform(euclid::Transform3D::scale(x, y, z))
}
pub fn perspective(d: f32) -> Self {
PxTransform::Transform(euclid::Transform3D::perspective(d))
}
pub fn to_transform(self) -> euclid::Transform3D<f32, Px, Px> {
match self {
PxTransform::Offset(v) => euclid::Transform3D::translation(v.x, v.y, 0.0),
PxTransform::Transform(t) => t,
}
}
pub fn is_identity(&self) -> bool {
match self {
PxTransform::Offset(offset) => offset == &euclid::Vector2D::zero(),
PxTransform::Transform(transform) => transform == &euclid::Transform3D::identity(),
}
}
#[must_use]
pub fn then(&self, other: &PxTransform) -> PxTransform {
match (self, other) {
(PxTransform::Offset(a), PxTransform::Offset(b)) => PxTransform::Offset(*a + *b),
(PxTransform::Offset(a), PxTransform::Transform(b)) => {
PxTransform::Transform(euclid::Transform3D::translation(a.x, a.y, 0.0).then(b))
}
(PxTransform::Transform(a), PxTransform::Offset(b)) => PxTransform::Transform(a.then_translate(b.to_3d())),
(PxTransform::Transform(a), PxTransform::Transform(b)) => PxTransform::Transform(a.then(b)),
}
}
#[must_use]
pub fn then_translate(&self, offset: euclid::Vector2D<f32, Px>) -> PxTransform {
match self {
PxTransform::Offset(a) => PxTransform::Offset(*a + offset),
PxTransform::Transform(a) => PxTransform::Transform(a.then_translate(offset.to_3d())),
}
}
#[must_use]
pub fn pre_translate(&self, offset: euclid::Vector2D<f32, Px>) -> PxTransform {
match self {
PxTransform::Offset(b) => PxTransform::Offset(offset + *b),
PxTransform::Transform(b) => PxTransform::Transform(euclid::Transform3D::translation(offset.x, offset.y, 0.0).then(b)),
}
}
pub fn is_invertible(&self) -> bool {
match self {
PxTransform::Offset(_) => true,
PxTransform::Transform(t) => t.is_invertible(),
}
}
pub fn inverse(&self) -> Option<PxTransform> {
match self {
PxTransform::Offset(v) => Some(PxTransform::Offset(-*v)),
PxTransform::Transform(t) => t.inverse().map(PxTransform::Transform),
}
}
pub fn is_2d(&self) -> bool {
match self {
PxTransform::Offset(_) => true,
PxTransform::Transform(t) => t.is_2d(),
}
}
pub fn transform_point(&self, point: PxPoint) -> Option<PxPoint> {
self.transform_point_f32(point.cast()).map(|p| p.cast())
}
pub fn transform_point_f32(&self, point: euclid::Point2D<f32, Px>) -> Option<euclid::Point2D<f32, Px>> {
match self {
PxTransform::Offset(v) => Some(point + *v),
PxTransform::Transform(t) => t.transform_point2d(point),
}
}
pub fn transform_vector(&self, vector: PxVector) -> PxVector {
self.transform_vector_f32(vector.cast()).cast()
}
pub fn transform_vector_f32(&self, vector: euclid::Vector2D<f32, Px>) -> euclid::Vector2D<f32, Px> {
match self {
PxTransform::Offset(v) => vector + *v,
PxTransform::Transform(t) => t.transform_vector2d(vector),
}
}
pub fn project_point(&self, point: PxPoint) -> Option<PxPoint> {
self.project_point_f32(point.cast()).map(|p| p.cast())
}
pub fn project_point_f32(&self, point: euclid::Point2D<f32, Px>) -> Option<euclid::Point2D<f32, Px>> {
match self {
PxTransform::Offset(v) => Some(point + *v),
PxTransform::Transform(t) => {
let z = -(point.x * t.m13 + point.y * t.m23 + t.m43) / t.m33;
t.transform_point3d(euclid::point3(point.x, point.y, z))
.map(|p3| euclid::point2(p3.x, p3.y))
}
}
}
pub fn outer_transformed(&self, px_box: PxBox) -> Option<PxBox> {
self.outer_transformed_f32(px_box.cast()).map(|p| p.cast())
}
pub fn outer_transformed_f32(&self, px_box: euclid::Box2D<f32, Px>) -> Option<euclid::Box2D<f32, Px>> {
match self {
PxTransform::Offset(v) => {
let v = *v;
let mut r = px_box;
r.min += v;
r.max += v;
Some(r)
}
PxTransform::Transform(t) => t.outer_transformed_box2d(&px_box),
}
}
}
impl From<euclid::Vector2D<f32, Px>> for PxTransform {
fn from(offset: euclid::Vector2D<f32, Px>) -> Self {
PxTransform::Offset(offset)
}
}
impl From<PxVector> for PxTransform {
fn from(offset: PxVector) -> Self {
PxTransform::Offset(offset.cast())
}
}
impl From<euclid::Transform3D<f32, Px, Px>> for PxTransform {
fn from(transform: euclid::Transform3D<f32, Px, Px>) -> Self {
PxTransform::Transform(transform)
}
}
mod serde_px_transform3d {
use crate::Px;
use super::*;
use serde::*;
#[derive(Serialize, Deserialize)]
struct SerdeTransform3D {
pub m11: f32,
pub m12: f32,
pub m13: f32,
pub m14: f32,
pub m21: f32,
pub m22: f32,
pub m23: f32,
pub m24: f32,
pub m31: f32,
pub m32: f32,
pub m33: f32,
pub m34: f32,
pub m41: f32,
pub m42: f32,
pub m43: f32,
pub m44: f32,
}
pub fn serialize<S: Serializer>(t: &euclid::Transform3D<f32, Px, Px>, serializer: S) -> Result<S::Ok, S::Error> {
SerdeTransform3D {
m11: t.m11,
m12: t.m12,
m13: t.m13,
m14: t.m14,
m21: t.m21,
m22: t.m22,
m23: t.m23,
m24: t.m24,
m31: t.m31,
m32: t.m32,
m33: t.m33,
m34: t.m34,
m41: t.m41,
m42: t.m42,
m43: t.m43,
m44: t.m44,
}
.serialize(serializer)
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<euclid::Transform3D<f32, Px, Px>, D::Error> {
let t = SerdeTransform3D::deserialize(deserializer)?;
Ok(euclid::Transform3D {
m11: t.m11,
m12: t.m12,
m13: t.m13,
m14: t.m14,
m21: t.m21,
m22: t.m22,
m23: t.m23,
m24: t.m24,
m31: t.m31,
m32: t.m32,
m33: t.m33,
m34: t.m34,
m41: t.m41,
m42: t.m42,
m43: t.m43,
m44: t.m44,
_unit: PhantomData,
})
}
}