use glam::{Affine2, Mat2, Vec2};
use super::{CollisionShape, Support};
#[derive(Debug, Clone)]
pub struct Transform {
local_to_world: Affine2,
world_to_local: Mat2,
}
impl Transform {
pub(crate) fn new(local_to_world: Affine2) -> Self {
let world_to_local = local_to_world.matrix2.inverse();
Self {
local_to_world,
world_to_local,
}
}
#[inline]
#[must_use]
pub fn from_translation(translation: impl Into<[f32; 2]>) -> Self {
Self::new(Affine2::from_translation(translation.into().into()))
}
#[inline]
#[must_use]
pub fn from_angle_translation(angle: f32, translation: impl Into<[f32; 2]>) -> Self {
Self::new(Affine2::from_angle_translation(
angle,
translation.into().into(),
))
}
#[inline]
#[must_use]
pub fn from_scale_angle_translation(
scale: impl Into<[f32; 2]>,
angle: f32,
translation: impl Into<[f32; 2]>,
) -> Self {
Self::new(Affine2::from_scale_angle_translation(
scale.into().into(),
angle,
translation.into().into(),
))
}
pub(crate) fn position(&self) -> Vec2 {
self.local_to_world.translation
}
}
impl Default for Transform {
#[inline]
fn default() -> Self {
Self {
local_to_world: Affine2::IDENTITY,
world_to_local: Mat2::IDENTITY,
}
}
}
impl Support<Vec2> for CollisionShape {
fn support(&self, direction: Vec2) -> Vec2 {
let local_direction = self.transform.world_to_local * direction;
let local_support = self.data.support(local_direction);
self.transform
.local_to_world
.transform_point2(local_support)
}
}
#[cfg(test)]
mod tests {
use core::f32::consts;
use approx::assert_ulps_eq;
use glam::Vec2;
use super::*;
#[test]
fn transformed_shape_support() {
let transform: Transform = Transform::from_scale_angle_translation(
Vec2::splat(2.0), consts::FRAC_PI_4, Vec2::new(1., 2.), );
let support_point = CollisionShape::new_rectangle(2.0, 2.0)
.with_transform(transform)
.support(Vec2::X);
assert!((3.5..4.0).contains(&support_point.x));
assert_ulps_eq!(2.0, support_point.y);
}
}