1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
use super::*;
/// An object in 2d space, bounded by a [Quad]
///
/// TODO: better name
pub trait Transform2d<F: Float> {
/// Object's bounding [Quad]
fn bounding_quad(&self) -> Quad<F>;
/// Apply transformation matrix to this object
fn apply_transform(&mut self, transform: mat3<F>);
}
impl<F: Float, T: Transform2d<F> + ?Sized> Transform2d<F> for Box<T> {
fn bounding_quad(&self) -> Quad<F> {
(**self).bounding_quad()
}
fn apply_transform(&mut self, transform: mat3<F>) {
(**self).apply_transform(transform);
}
}
/// A reference to a 2d object with additional transformation applied
///
// TODO: is this needed? should it be reference?
pub struct Transformed2d<'a, F: Float, T: Transform2d<F> + ?Sized> {
/// Reference to the actual object
pub inner: &'a T,
/// Additional transformation
pub transform: mat3<F>,
}
impl<'a, F: Float, T: Transform2d<F> + ?Sized> Transformed2d<'a, F, T> {
/// Apply additional transformation to the given object
pub fn new(inner: &'a T, transform: mat3<F>) -> Self {
Self { inner, transform }
}
}
impl<'a, F: Float, T: Transform2d<F> + ?Sized> Transform2d<F> for Transformed2d<'a, F, T> {
fn bounding_quad(&self) -> Quad<F> {
self.inner.bounding_quad().transform(self.transform)
}
fn apply_transform(&mut self, transform: mat3<F>) {
self.transform = transform * self.transform;
}
}
/// Extra methods for [Transform2d] types
pub trait Transform2dExt<F: Float>: Transform2d<F> {
/// Apply transformation to the object, returning a modified value to allow chaining methods
fn transform(self, transform: mat3<F>) -> Self
where
Self: Sized,
{
let mut result = self;
result.apply_transform(transform);
result
}
/// Align bounding box of this object, making its origin located at (0, 0)
///
/// # Examples
/// ```
/// # use batbox_la::*;
/// # use batbox_lapp::*;
/// let quad = Quad::unit();
/// assert_eq!(quad.bounding_box(), Aabb2::from_corners(vec2(-1.0, -1.0), vec2(1.0, 1.0)));
/// let quad = quad.align_bounding_box(vec2(0.0, 1.0));
/// assert_eq!(quad.bounding_box(), Aabb2::from_corners(vec2(0.0, 0.0), vec2(2.0, -2.0)));
/// ```
fn align_bounding_box(self, alignment: vec2<F>) -> Self
where
Self: Sized,
{
let aabb = self.bounding_box();
self.translate(-aabb.bottom_left() - aabb.size() * alignment)
}
/// Rotate object around (0, 0) by given angle
fn rotate(self, angle: Angle<F>) -> Self
where
Self: Sized,
{
self.transform(mat3::rotate(angle))
}
/// Translate object by given vector
fn translate(self, delta: vec2<F>) -> Self
where
Self: Sized,
{
self.transform(mat3::translate(delta))
}
/// Scale object around (0, 0) by given factor
fn scale(self, factor: vec2<F>) -> Self
where
Self: Sized,
{
self.transform(mat3::scale(factor))
}
/// Scale object around (0, 0) by given factor uniformly along both axis
fn scale_uniform(self, factor: F) -> Self
where
Self: Sized,
{
self.transform(mat3::scale_uniform(factor))
}
/// Create a reference to this object with additional transformation applied
///
// TODO: bad naming
fn transformed(&self) -> Transformed2d<F, Self> {
Transformed2d::new(self, mat3::identity())
}
/// Calculate bounding box of this object, getting [Aabb2] instead of a [Quad]
fn bounding_box(&self) -> Aabb2<F> {
Aabb2::points_bounding_box(
[
vec2(-F::ONE, -F::ONE),
vec2(F::ONE, -F::ONE),
vec2(F::ONE, F::ONE),
vec2(-F::ONE, F::ONE),
]
.into_iter()
.map(|p| (self.bounding_quad().transform * p.extend(F::ONE)).into_2d()),
)
.unwrap()
}
/// Make this object's bounding [Quad] fit into given target
fn fit_into(self, target: impl FitTarget2d<F>) -> Self
where
Self: Sized,
{
let mut result = self;
target.make_fit(&mut result);
result
}
}
impl<F: Float, T: Transform2d<F> + ?Sized> Transform2dExt<F> for T {}