emath/
ts_transform.rs

1use crate::{Pos2, Rect, Vec2};
2
3/// Linearly transforms positions via a translation, then a scaling.
4///
5/// [`TSTransform`] first scales points with the scaling origin at `0, 0`
6/// (the top left corner), then translates them.
7#[repr(C)]
8#[derive(Clone, Copy, Debug, PartialEq)]
9#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
10#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
11pub struct TSTransform {
12    /// Scaling applied first, scaled around (0, 0).
13    pub scaling: f32,
14
15    /// Translation amount, applied after scaling.
16    pub translation: Vec2,
17}
18
19impl Eq for TSTransform {}
20
21impl Default for TSTransform {
22    #[inline]
23    fn default() -> Self {
24        Self::IDENTITY
25    }
26}
27
28impl TSTransform {
29    pub const IDENTITY: Self = Self {
30        translation: Vec2::ZERO,
31        scaling: 1.0,
32    };
33
34    #[inline]
35    /// Creates a new translation that first scales points around
36    /// `(0, 0)`, then translates them.
37    pub fn new(translation: Vec2, scaling: f32) -> Self {
38        Self {
39            translation,
40            scaling,
41        }
42    }
43
44    #[inline]
45    pub fn from_translation(translation: Vec2) -> Self {
46        Self::new(translation, 1.0)
47    }
48
49    #[inline]
50    pub fn from_scaling(scaling: f32) -> Self {
51        Self::new(Vec2::ZERO, scaling)
52    }
53
54    /// Is this a valid, invertible transform?
55    pub fn is_valid(&self) -> bool {
56        self.scaling.is_finite() && self.translation.x.is_finite() && self.scaling != 0.0
57    }
58
59    /// Inverts the transform.
60    ///
61    /// ```
62    /// # use emath::{pos2, vec2, TSTransform};
63    /// let p1 = pos2(2.0, 3.0);
64    /// let p2 = pos2(12.0, 5.0);
65    /// let ts = TSTransform::new(vec2(2.0, 3.0), 2.0);
66    /// let inv = ts.inverse();
67    /// assert_eq!(inv.mul_pos(p1), pos2(0.0, 0.0));
68    /// assert_eq!(inv.mul_pos(p2), pos2(5.0, 1.0));
69    ///
70    /// assert_eq!(ts.inverse().inverse(), ts);
71    /// ```
72    #[inline]
73    pub fn inverse(&self) -> Self {
74        Self::new(-self.translation / self.scaling, 1.0 / self.scaling)
75    }
76
77    /// Transforms the given coordinate.
78    ///
79    /// ```
80    /// # use emath::{pos2, vec2, TSTransform};
81    /// let p1 = pos2(0.0, 0.0);
82    /// let p2 = pos2(5.0, 1.0);
83    /// let ts = TSTransform::new(vec2(2.0, 3.0), 2.0);
84    /// assert_eq!(ts.mul_pos(p1), pos2(2.0, 3.0));
85    /// assert_eq!(ts.mul_pos(p2), pos2(12.0, 5.0));
86    /// ```
87    #[inline]
88    pub fn mul_pos(&self, pos: Pos2) -> Pos2 {
89        self.scaling * pos + self.translation
90    }
91
92    /// Transforms the given rectangle.
93    ///
94    /// ```
95    /// # use emath::{pos2, vec2, Rect, TSTransform};
96    /// let rect = Rect::from_min_max(pos2(5.0, 5.0), pos2(15.0, 10.0));
97    /// let ts = TSTransform::new(vec2(1.0, 0.0), 3.0);
98    /// let transformed = ts.mul_rect(rect);
99    /// assert_eq!(transformed.min, pos2(16.0, 15.0));
100    /// assert_eq!(transformed.max, pos2(46.0, 30.0));
101    /// ```
102    #[inline]
103    pub fn mul_rect(&self, rect: Rect) -> Rect {
104        Rect {
105            min: self.mul_pos(rect.min),
106            max: self.mul_pos(rect.max),
107        }
108    }
109}
110
111/// Transforms the position.
112impl std::ops::Mul<Pos2> for TSTransform {
113    type Output = Pos2;
114
115    #[inline]
116    fn mul(self, pos: Pos2) -> Pos2 {
117        self.mul_pos(pos)
118    }
119}
120
121/// Transforms the rectangle.
122impl std::ops::Mul<Rect> for TSTransform {
123    type Output = Rect;
124
125    #[inline]
126    fn mul(self, rect: Rect) -> Rect {
127        self.mul_rect(rect)
128    }
129}
130
131impl std::ops::Mul<Self> for TSTransform {
132    type Output = Self;
133
134    #[inline]
135    /// Applies the right hand side transform, then the left hand side.
136    ///
137    /// ```
138    /// # use emath::{TSTransform, vec2};
139    /// let ts1 = TSTransform::new(vec2(1.0, 0.0), 2.0);
140    /// let ts2 = TSTransform::new(vec2(-1.0, -1.0), 3.0);
141    /// let ts_combined = TSTransform::new(vec2(2.0, -1.0), 6.0);
142    /// assert_eq!(ts_combined, ts2 * ts1);
143    /// ```
144    fn mul(self, rhs: Self) -> Self::Output {
145        // Apply rhs first.
146        Self {
147            scaling: self.scaling * rhs.scaling,
148            translation: self.translation + self.scaling * rhs.translation,
149        }
150    }
151}