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    /// Inverts the transform.
55    ///
56    /// ```
57    /// # use emath::{pos2, vec2, TSTransform};
58    /// let p1 = pos2(2.0, 3.0);
59    /// let p2 = pos2(12.0, 5.0);
60    /// let ts = TSTransform::new(vec2(2.0, 3.0), 2.0);
61    /// let inv = ts.inverse();
62    /// assert_eq!(inv.mul_pos(p1), pos2(0.0, 0.0));
63    /// assert_eq!(inv.mul_pos(p2), pos2(5.0, 1.0));
64    ///
65    /// assert_eq!(ts.inverse().inverse(), ts);
66    /// ```
67    #[inline]
68    pub fn inverse(&self) -> Self {
69        Self::new(-self.translation / self.scaling, 1.0 / self.scaling)
70    }
71
72    /// Transforms the given coordinate.
73    ///
74    /// ```
75    /// # use emath::{pos2, vec2, TSTransform};
76    /// let p1 = pos2(0.0, 0.0);
77    /// let p2 = pos2(5.0, 1.0);
78    /// let ts = TSTransform::new(vec2(2.0, 3.0), 2.0);
79    /// assert_eq!(ts.mul_pos(p1), pos2(2.0, 3.0));
80    /// assert_eq!(ts.mul_pos(p2), pos2(12.0, 5.0));
81    /// ```
82    #[inline]
83    pub fn mul_pos(&self, pos: Pos2) -> Pos2 {
84        self.scaling * pos + self.translation
85    }
86
87    /// Transforms the given rectangle.
88    ///
89    /// ```
90    /// # use emath::{pos2, vec2, Rect, TSTransform};
91    /// let rect = Rect::from_min_max(pos2(5.0, 5.0), pos2(15.0, 10.0));
92    /// let ts = TSTransform::new(vec2(1.0, 0.0), 3.0);
93    /// let transformed = ts.mul_rect(rect);
94    /// assert_eq!(transformed.min, pos2(16.0, 15.0));
95    /// assert_eq!(transformed.max, pos2(46.0, 30.0));
96    /// ```
97    #[inline]
98    pub fn mul_rect(&self, rect: Rect) -> Rect {
99        Rect {
100            min: self.mul_pos(rect.min),
101            max: self.mul_pos(rect.max),
102        }
103    }
104}
105
106/// Transforms the position.
107impl std::ops::Mul<Pos2> for TSTransform {
108    type Output = Pos2;
109
110    #[inline]
111    fn mul(self, pos: Pos2) -> Pos2 {
112        self.mul_pos(pos)
113    }
114}
115
116/// Transforms the rectangle.
117impl std::ops::Mul<Rect> for TSTransform {
118    type Output = Rect;
119
120    #[inline]
121    fn mul(self, rect: Rect) -> Rect {
122        self.mul_rect(rect)
123    }
124}
125
126impl std::ops::Mul<Self> for TSTransform {
127    type Output = Self;
128
129    #[inline]
130    /// Applies the right hand side transform, then the left hand side.
131    ///
132    /// ```
133    /// # use emath::{TSTransform, vec2};
134    /// let ts1 = TSTransform::new(vec2(1.0, 0.0), 2.0);
135    /// let ts2 = TSTransform::new(vec2(-1.0, -1.0), 3.0);
136    /// let ts_combined = TSTransform::new(vec2(2.0, -1.0), 6.0);
137    /// assert_eq!(ts_combined, ts2 * ts1);
138    /// ```
139    fn mul(self, rhs: Self) -> Self::Output {
140        // Apply rhs first.
141        Self {
142            scaling: self.scaling * rhs.scaling,
143            translation: self.translation + self.scaling * rhs.translation,
144        }
145    }
146}