emath/
ts_transform.rs

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 crate::{Pos2, Rect, Vec2};

/// Linearly transforms positions via a translation, then a scaling.
///
/// [`TSTransform`] first scales points with the scaling origin at `0, 0`
/// (the top left corner), then translates them.
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
pub struct TSTransform {
    /// Scaling applied first, scaled around (0, 0).
    pub scaling: f32,

    /// Translation amount, applied after scaling.
    pub translation: Vec2,
}

impl Eq for TSTransform {}

impl Default for TSTransform {
    #[inline]
    fn default() -> Self {
        Self::IDENTITY
    }
}

impl TSTransform {
    pub const IDENTITY: Self = Self {
        translation: Vec2::ZERO,
        scaling: 1.0,
    };

    #[inline]
    /// Creates a new translation that first scales points around
    /// `(0, 0)`, then translates them.  
    pub fn new(translation: Vec2, scaling: f32) -> Self {
        Self {
            translation,
            scaling,
        }
    }

    #[inline]
    pub fn from_translation(translation: Vec2) -> Self {
        Self::new(translation, 1.0)
    }

    #[inline]
    pub fn from_scaling(scaling: f32) -> Self {
        Self::new(Vec2::ZERO, scaling)
    }

    /// Inverts the transform.
    ///
    /// ```
    /// # use emath::{pos2, vec2, TSTransform};
    /// let p1 = pos2(2.0, 3.0);
    /// let p2 = pos2(12.0, 5.0);
    /// let ts = TSTransform::new(vec2(2.0, 3.0), 2.0);
    /// let inv = ts.inverse();
    /// assert_eq!(inv.mul_pos(p1), pos2(0.0, 0.0));
    /// assert_eq!(inv.mul_pos(p2), pos2(5.0, 1.0));
    ///
    /// assert_eq!(ts.inverse().inverse(), ts);
    /// ```
    #[inline]
    pub fn inverse(&self) -> Self {
        Self::new(-self.translation / self.scaling, 1.0 / self.scaling)
    }

    /// Transforms the given coordinate.
    ///
    /// ```
    /// # use emath::{pos2, vec2, TSTransform};
    /// let p1 = pos2(0.0, 0.0);
    /// let p2 = pos2(5.0, 1.0);
    /// let ts = TSTransform::new(vec2(2.0, 3.0), 2.0);
    /// assert_eq!(ts.mul_pos(p1), pos2(2.0, 3.0));
    /// assert_eq!(ts.mul_pos(p2), pos2(12.0, 5.0));
    /// ```
    #[inline]
    pub fn mul_pos(&self, pos: Pos2) -> Pos2 {
        self.scaling * pos + self.translation
    }

    /// Transforms the given rectangle.
    ///
    /// ```
    /// # use emath::{pos2, vec2, Rect, TSTransform};
    /// let rect = Rect::from_min_max(pos2(5.0, 5.0), pos2(15.0, 10.0));
    /// let ts = TSTransform::new(vec2(1.0, 0.0), 3.0);
    /// let transformed = ts.mul_rect(rect);
    /// assert_eq!(transformed.min, pos2(16.0, 15.0));
    /// assert_eq!(transformed.max, pos2(46.0, 30.0));
    /// ```
    #[inline]
    pub fn mul_rect(&self, rect: Rect) -> Rect {
        Rect {
            min: self.mul_pos(rect.min),
            max: self.mul_pos(rect.max),
        }
    }
}

/// Transforms the position.
impl std::ops::Mul<Pos2> for TSTransform {
    type Output = Pos2;

    #[inline]
    fn mul(self, pos: Pos2) -> Pos2 {
        self.mul_pos(pos)
    }
}

/// Transforms the rectangle.
impl std::ops::Mul<Rect> for TSTransform {
    type Output = Rect;

    #[inline]
    fn mul(self, rect: Rect) -> Rect {
        self.mul_rect(rect)
    }
}

impl std::ops::Mul<Self> for TSTransform {
    type Output = Self;

    #[inline]
    /// Applies the right hand side transform, then the left hand side.
    ///
    /// ```
    /// # use emath::{TSTransform, vec2};
    /// let ts1 = TSTransform::new(vec2(1.0, 0.0), 2.0);
    /// let ts2 = TSTransform::new(vec2(-1.0, -1.0), 3.0);
    /// let ts_combined = TSTransform::new(vec2(2.0, -1.0), 6.0);
    /// assert_eq!(ts_combined, ts2 * ts1);
    /// ```
    fn mul(self, rhs: Self) -> Self::Output {
        // Apply rhs first.
        Self {
            scaling: self.scaling * rhs.scaling,
            translation: self.translation + self.scaling * rhs.translation,
        }
    }
}