|
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) {} |
|
} |
|
|