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}