| use crate::Artboard; |
| use crate::math::bbox::AxisAlignedBbox; |
| pub use crate::vector::ReferencePoint; |
| use core::f64; |
| use glam::{DAffine2, DMat2, DVec2}; |
|
|
| pub trait Transform { |
| fn transform(&self) -> DAffine2; |
| fn local_pivot(&self, pivot: DVec2) -> DVec2 { |
| pivot |
| } |
| fn decompose_scale(&self) -> DVec2 { |
| DVec2::new( |
| self.transform().transform_vector2((1., 0.).into()).length(), |
| self.transform().transform_vector2((0., 1.).into()).length(), |
| ) |
| } |
| } |
|
|
| pub trait TransformMut: Transform { |
| fn transform_mut(&mut self) -> &mut DAffine2; |
| fn translate(&mut self, offset: DVec2) { |
| *self.transform_mut() = DAffine2::from_translation(offset) * self.transform(); |
| } |
| } |
|
|
| |
| impl<T: Transform> Transform for &T { |
| fn transform(&self) -> DAffine2 { |
| (*self).transform() |
| } |
| } |
|
|
| |
| impl Transform for Artboard { |
| fn transform(&self) -> DAffine2 { |
| DAffine2::from_translation(self.location.as_dvec2()) |
| } |
| fn local_pivot(&self, pivot: DVec2) -> DVec2 { |
| self.location.as_dvec2() + self.dimensions.as_dvec2() * pivot |
| } |
| } |
|
|
| |
| impl Transform for DAffine2 { |
| fn transform(&self) -> DAffine2 { |
| *self |
| } |
| } |
| impl TransformMut for DAffine2 { |
| fn transform_mut(&mut self) -> &mut DAffine2 { |
| self |
| } |
| } |
|
|
| |
| impl Transform for Footprint { |
| fn transform(&self) -> DAffine2 { |
| self.transform |
| } |
| } |
| impl TransformMut for Footprint { |
| fn transform_mut(&mut self) -> &mut DAffine2 { |
| &mut self.transform |
| } |
| } |
|
|
| #[derive(Debug, Clone, Copy, dyn_any::DynAny, PartialEq, serde::Serialize, serde::Deserialize)] |
| pub enum RenderQuality { |
| |
| Preview, |
| |
| |
| Scale(f32), |
| |
| |
| Probability(f32), |
| |
| Full, |
| } |
| #[derive(Debug, Clone, Copy, dyn_any::DynAny, PartialEq, serde::Serialize, serde::Deserialize)] |
| pub struct Footprint { |
| |
| pub transform: DAffine2, |
| |
| pub resolution: glam::UVec2, |
| |
| pub quality: RenderQuality, |
| } |
|
|
| impl Default for Footprint { |
| fn default() -> Self { |
| Self::DEFAULT |
| } |
| } |
|
|
| impl Footprint { |
| pub const DEFAULT: Self = Self { |
| transform: DAffine2::IDENTITY, |
| resolution: glam::UVec2::new(1920, 1080), |
| quality: RenderQuality::Full, |
| }; |
|
|
| pub const BOUNDLESS: Self = Self { |
| transform: DAffine2 { |
| matrix2: DMat2::from_diagonal(DVec2::splat(f64::INFINITY)), |
| translation: DVec2::ZERO, |
| }, |
| resolution: glam::UVec2::new(0, 0), |
| quality: RenderQuality::Full, |
| }; |
|
|
| pub fn viewport_bounds_in_local_space(&self) -> AxisAlignedBbox { |
| let inverse = self.transform.inverse(); |
| let start = inverse.transform_point2((0., 0.).into()); |
| let end = inverse.transform_point2(self.resolution.as_dvec2()); |
| AxisAlignedBbox { start, end } |
| } |
|
|
| pub fn scale(&self) -> DVec2 { |
| self.transform.decompose_scale() |
| } |
|
|
| pub fn offset(&self) -> DVec2 { |
| self.transform.transform_point2(DVec2::ZERO) |
| } |
| } |
|
|
| impl From<()> for Footprint { |
| fn from(_: ()) -> Self { |
| Footprint::default() |
| } |
| } |
|
|
| impl std::hash::Hash for Footprint { |
| fn hash<H: std::hash::Hasher>(&self, state: &mut H) { |
| self.transform.to_cols_array().iter().for_each(|x| x.to_le_bytes().hash(state)); |
| self.resolution.hash(state) |
| } |
| } |
|
|
| pub trait ApplyTransform { |
| fn apply_transform(&mut self, modification: &DAffine2); |
| } |
| impl<T: TransformMut> ApplyTransform for T { |
| fn apply_transform(&mut self, &modification: &DAffine2) { |
| *self.transform_mut() = self.transform() * modification |
| } |
| } |
| impl ApplyTransform for () { |
| fn apply_transform(&mut self, &_modification: &DAffine2) {} |
| } |
|
|