use crate::{core::components::maths::coordinates::Coordinates, utils::maths::Vector};
#[derive(Default, Debug, Copy, Clone)]
struct Bounds {
pub(crate) min_x: Option<f32>,
pub(crate) max_x: Option<f32>,
pub(crate) min_y: Option<f32>,
pub(crate) max_y: Option<f32>,
}
#[derive(Debug, Copy, Clone)]
pub struct Transform {
pub(crate) local_translation: Coordinates,
pub(crate) global_translation: Coordinates,
pub(crate) scale: f32,
pub(crate) angle: f32,
pub(crate) dirty: bool,
pub(crate) dirty_child: bool,
pub(crate) use_screen_as_origin: bool,
bounds: Bounds,
}
impl Default for Transform {
fn default() -> Self {
Self {
local_translation: Default::default(),
global_translation: Default::default(),
scale: 1.0,
angle: 0.0,
dirty: false,
dirty_child: true,
use_screen_as_origin: false,
bounds: Default::default(),
}
}
}
impl Transform {
pub fn new(translation: Coordinates, scale: f32, angle: f32) -> Self {
Self {
local_translation: translation,
global_translation: translation,
scale,
angle,
dirty: false,
dirty_child: true,
use_screen_as_origin: false,
bounds: Default::default(),
}
}
pub fn from_xy(x: f32, y: f32) -> Self {
Self::new(Coordinates::new(x, y), 1., 0.)
}
pub fn from_xyz(x: f32, y: f32, z: usize) -> Self {
Self::new(Coordinates::new_with_z(x, y, z), 1., 0.)
}
pub fn append_translation(&mut self, x: f32, y: f32) {
self.local_translation.x += x;
self.local_translation.y += y;
self.global_translation.x += x;
self.global_translation.y += y;
self.dirty = true;
self.handle_bounds();
}
pub fn append_vector(&mut self, vector: Vector) {
self.append_translation(vector.x, vector.y);
}
pub fn append_x(&mut self, x: f32) {
self.local_translation.x += x;
self.global_translation.x += x;
self.dirty = true;
self.handle_bounds();
}
pub fn append_y(&mut self, y: f32) {
self.local_translation.y += y;
self.global_translation.y += y;
self.dirty = true;
self.handle_bounds();
}
pub fn move_down(&mut self, y: f32) {
self.local_translation.y += y;
self.global_translation.y += y;
self.dirty = true;
self.handle_bounds();
}
pub fn append_angle(&mut self, angle: f32) {
self.angle += angle;
}
pub fn translation(&self) -> &Coordinates {
&self.local_translation
}
pub fn global_translation(&self) -> &Coordinates {
&self.global_translation
}
pub fn set_scale(&mut self, scale: f32) {
self.scale = scale
}
pub fn set_z(&mut self, z: usize) {
self.local_translation.z = z
}
pub fn set_min_x(&mut self, min_x: Option<f32>) {
self.bounds.min_x = min_x;
self.handle_bounds();
}
pub fn set_max_x(&mut self, max_x: Option<f32>) {
self.bounds.max_x = max_x;
self.handle_bounds();
}
pub fn set_min_y(&mut self, min_y: Option<f32>) {
self.bounds.min_y = min_y;
self.handle_bounds();
}
pub fn set_max_y(&mut self, max_y: Option<f32>) {
self.bounds.max_y = max_y;
self.handle_bounds();
}
pub fn set_use_screen_as_origin(&mut self, new_value: bool) {
self.use_screen_as_origin = new_value;
self.handle_bounds();
}
pub fn set_global_translation_bounds(
&mut self,
min_x: Option<f32>,
max_x: Option<f32>,
min_y: Option<f32>,
max_y: Option<f32>,
) {
self.bounds.min_x = min_x;
self.bounds.max_x = max_x;
self.bounds.min_y = min_y;
self.bounds.max_y = max_y;
self.handle_bounds();
}
pub(crate) fn compute_global_from_parent(&mut self, parent_translation: &Coordinates) {
let mut new_global = parent_translation.clone();
new_global.x += self.local_translation.x;
new_global.y += self.local_translation.y;
new_global.z = self.local_translation.z;
self.global_translation = new_global;
self.dirty = true;
self.handle_bounds();
}
fn handle_bounds(&mut self) {
if let Some(min_x) = self.bounds.min_x {
if self.global_translation.x < min_x {
self.global_translation.set_x(min_x);
}
}
if let Some(max_x) = self.bounds.max_x {
if self.global_translation.x > max_x {
self.global_translation.set_x(max_x);
}
}
if let Some(min_y) = self.bounds.min_y {
if self.global_translation.y < min_y {
self.global_translation.set_y(min_y);
}
}
if let Some(max_y) = self.bounds.max_y {
if self.global_translation.y > max_y {
self.global_translation.set_y(max_y);
}
}
}
}
pub struct TransformBuilder {
transform: Transform,
}
impl TransformBuilder {
pub fn new() -> Self {
Self { transform: Transform::default() }
}
pub fn with_xy(mut self, x: f32, y: f32) -> Self {
let translation = Coordinates::new(x, y);
self.transform.local_translation = translation;
self.transform.global_translation = translation;
self
}
pub fn with_translation(mut self, x: f32, y: f32, z: usize) -> Self {
let translation = Coordinates::new_with_z(x, y, z);
self.transform.local_translation = translation;
self.transform.global_translation = translation;
self
}
pub fn with_scale(mut self, scale: f32) -> Self {
self.transform.scale = scale;
self
}
pub fn with_angle(mut self, angle: f32) -> Self {
self.transform.angle = angle;
self
}
pub fn with_screen_as_origin(mut self) -> Self {
self.transform.use_screen_as_origin = true;
self
}
pub fn build(self) -> Transform {
self.transform
}
}
#[cfg(test)]
mod tests {
use crate::core::components::maths::transform::{Coordinates, Transform};
#[test]
fn compute_global_from_parent_test() {
let parent_translation = Coordinates::new(1., 2.);
let mut child_transform = Transform::new(Coordinates::new(5., 3.), 1., 1.);
assert_eq!(5., child_transform.global_translation.x);
assert_eq!(3., child_transform.global_translation.y);
child_transform.compute_global_from_parent(&parent_translation);
assert_eq!(6., child_transform.global_translation.x);
assert_eq!(5., child_transform.global_translation.y);
}
#[test]
fn modify_transform_should_set_dirty_test() {
let mut transform = Transform::new(Coordinates::new(5., 3.), 1., 1.);
assert_eq!(false, transform.dirty);
transform.append_translation(1., 1.);
assert_eq!(true, transform.dirty);
transform.dirty = false;
transform.move_down(1.);
assert_eq!(true, transform.dirty);
transform.dirty = false;
transform.append_x(1.);
assert_eq!(true, transform.dirty);
transform.dirty = false;
transform.append_y(1.);
assert_eq!(true, transform.dirty);
transform.dirty = false;
transform.compute_global_from_parent(&Coordinates::new(1., 2.));
assert_eq!(true, transform.dirty);
}
#[test]
fn handle_bounds_test() {
let mut t = Transform::default();
t.set_min_x(Some(1.));
assert_eq!(1., t.global_translation.x);
t.append_x(-6.);
assert_eq!(1., t.global_translation.x);
}
}