use std::cmp::Ordering;
use glam::{DVec3, dvec3};
use itertools::Itertools;
use crate::{anchor::Aabb, traits::StrokeWidth};
#[derive(Debug, Clone, Copy)]
pub enum ScaleHint {
X(f64),
Y(f64),
Z(f64),
PorportionalX(f64),
PorportionalY(f64),
PorportionalZ(f64),
}
pub trait ScaleTransform {
fn scale(&mut self, scale: DVec3) -> &mut Self;
}
impl ScaleTransform for DVec3 {
fn scale(&mut self, scale: DVec3) -> &mut Self {
*self *= scale;
self
}
}
impl<T: ScaleTransform> ScaleTransform for [T] {
fn scale(&mut self, scale: DVec3) -> &mut Self {
self.iter_mut().for_each(|x| {
x.scale(scale);
});
self
}
}
impl<T: ScaleTransform> ScaleTransform for Vec<T> {
fn scale(&mut self, scale: DVec3) -> &mut Self {
self.as_mut_slice().scale(scale);
self
}
}
pub trait ScaleTransformExt: ScaleTransform {
fn calc_scale_ratio(&self, hint: ScaleHint) -> DVec3
where
Self: Aabb,
{
let aabb_size = self.aabb_size();
match hint {
ScaleHint::X(v) => dvec3(v / (aabb_size.x), 1.0, 1.0),
ScaleHint::Y(v) => dvec3(1.0, v / (aabb_size.y), 1.0),
ScaleHint::Z(v) => dvec3(1.0, 1.0, v / aabb_size.z),
ScaleHint::PorportionalX(v) => DVec3::splat(v / aabb_size.x),
ScaleHint::PorportionalY(v) => DVec3::splat(v / aabb_size.y),
ScaleHint::PorportionalZ(v) => DVec3::splat(v / aabb_size.z),
}
}
fn scale_to(&mut self, hint: ScaleHint) -> &mut Self
where
Self: Aabb,
{
self.scale(self.calc_scale_ratio(hint));
self
}
fn scale_to_min(&mut self, hints: &[ScaleHint]) -> &mut Self
where
Self: Aabb,
{
let scale = hints
.iter()
.map(|hint| self.calc_scale_ratio(*hint))
.reduce(|a, b| a.min(b))
.unwrap_or(DVec3::ONE);
self.scale(scale);
self
}
fn scale_to_max(&mut self, hints: &[ScaleHint]) -> &mut Self
where
Self: Aabb,
{
let scale = hints
.iter()
.map(|hint| self.calc_scale_ratio(*hint))
.reduce(|a, b| a.max(b))
.unwrap_or(DVec3::ONE);
self.scale(scale);
self
}
}
impl<T: ScaleTransform + ?Sized> ScaleTransformExt for T {}
pub trait ScaleTransformStrokeExt: ScaleTransform + StrokeWidth {
fn scale_with_stroke(&mut self, scale: DVec3) -> &mut Self {
self.scale(scale);
let scales = [scale.x, scale.y, scale.z];
let idx = scales
.iter()
.map(|x: &f64| if *x > 1.0 { *x } else { 1.0 / *x })
.position_max_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal))
.unwrap_or(0);
let scale = scales[idx];
self.apply_stroke_func(|widths| widths.iter_mut().for_each(|w| w.0 *= scale as f32));
self
}
fn scale_to_with_stroke(&mut self, hint: ScaleHint) -> &mut Self
where
Self: Aabb,
{
let scale = self.calc_scale_ratio(hint);
self.scale_with_stroke(scale)
}
}
impl<T: ScaleTransform + StrokeWidth + ?Sized> ScaleTransformStrokeExt for T {}