Skip to main content

repose_core/
geometry.rs

1#[derive(Clone, Copy, Debug, Default, PartialEq)]
2pub struct Vec2 {
3    pub x: f32,
4    pub y: f32,
5}
6
7impl Vec2 {
8    pub const ZERO: Vec2 = Vec2 { x: 0.0, y: 0.0 };
9}
10
11impl std::ops::Add for Vec2 {
12    type Output = Vec2;
13    fn add(self, other: Vec2) -> Vec2 {
14        Vec2 {
15            x: self.x + other.x,
16            y: self.y + other.y,
17        }
18    }
19}
20
21impl std::ops::Sub for Vec2 {
22    type Output = Vec2;
23    fn sub(self, other: Vec2) -> Vec2 {
24        Vec2 {
25            x: self.x - other.x,
26            y: self.y - other.y,
27        }
28    }
29}
30
31impl std::ops::Neg for Vec2 {
32    type Output = Vec2;
33    fn neg(self) -> Vec2 {
34        Vec2 {
35            x: -self.x,
36            y: -self.y,
37        }
38    }
39}
40
41#[derive(Clone, Copy, Debug, Default, PartialEq)]
42pub struct Size {
43    pub width: f32,
44    pub height: f32,
45}
46
47#[derive(Clone, Copy, Debug, Default, PartialEq)]
48pub struct Rect {
49    pub x: f32,
50    pub y: f32,
51    pub w: f32,
52    pub h: f32,
53}
54
55impl Rect {
56    pub fn contains(&self, p: Vec2) -> bool {
57        p.x >= self.x && p.x <= self.x + self.w && p.y >= self.y && p.y <= self.y + self.h
58    }
59}
60
61#[derive(Clone, Copy, Debug, Default, PartialEq)]
62pub struct Transform {
63    pub translate_x: f32,
64    pub translate_y: f32,
65    pub scale_x: f32,
66    pub scale_y: f32,
67    pub rotate: f32, // radians
68    pub origin_x: f32,
69    pub origin_y: f32,
70}
71
72impl Transform {
73    pub fn identity() -> Self {
74        Self {
75            translate_x: 0.0,
76            translate_y: 0.0,
77            scale_x: 1.0,
78            scale_y: 1.0,
79            rotate: 0.0,
80            origin_x: 0.5,
81            origin_y: 0.5,
82        }
83    }
84
85    pub fn translate(x: f32, y: f32) -> Self {
86        Self {
87            translate_x: x,
88            translate_y: y,
89            scale_x: 1.0,
90            scale_y: 1.0,
91            rotate: 0.0,
92            origin_x: 0.5,
93            origin_y: 0.5,
94        }
95    }
96
97    pub fn apply_to_point(&self, p: Vec2) -> Vec2 {
98        let ox = self.origin_x;
99        let oy = self.origin_y;
100        let mut x = (p.x - ox) * self.scale_x;
101        let mut y = (p.y - oy) * self.scale_y;
102
103        if self.rotate != 0.0 {
104            let cos = self.rotate.cos();
105            let sin = self.rotate.sin();
106            let nx = x * cos - y * sin;
107            let ny = x * sin + y * cos;
108            x = nx;
109            y = ny;
110        }
111
112        Vec2 {
113            x: x + ox + self.translate_x,
114            y: y + oy + self.translate_y,
115        }
116    }
117
118    pub fn apply_to_rect(&self, r: Rect) -> Rect {
119        let ox = r.x + r.w * self.origin_x;
120        let oy = r.y + r.h * self.origin_y;
121        let corners = [
122            Vec2 { x: r.x, y: r.y },
123            Vec2 {
124                x: r.x + r.w,
125                y: r.y,
126            },
127            Vec2 {
128                x: r.x,
129                y: r.y + r.h,
130            },
131            Vec2 {
132                x: r.x + r.w,
133                y: r.y + r.h,
134            },
135        ];
136        let mut min_x = f32::MAX;
137        let mut min_y = f32::MAX;
138        let mut max_x = f32::MIN;
139        let mut max_y = f32::MIN;
140        for c in corners {
141            let mut x = (c.x - ox) * self.scale_x;
142            let mut y = (c.y - oy) * self.scale_y;
143            if self.rotate != 0.0 {
144                let cos = self.rotate.cos();
145                let sin = self.rotate.sin();
146                let nx = x * cos - y * sin;
147                let ny = x * sin + y * cos;
148                x = nx;
149                y = ny;
150            }
151            x += ox + self.translate_x;
152            y += oy + self.translate_y;
153            min_x = min_x.min(x);
154            min_y = min_y.min(y);
155            max_x = max_x.max(x);
156            max_y = max_y.max(y);
157        }
158        Rect {
159            x: min_x,
160            y: min_y,
161            w: max_x - min_x,
162            h: max_y - min_y,
163        }
164    }
165
166    /// Compose two transforms: `self` then `other` (post-multiply).
167    ///
168    /// Returns a transform such that `combined.apply_to_point(p) ==
169    /// other.apply_to_point(self.apply_to_point(p))`.
170    ///
171    /// **Limitation:** the T*R*S representation is not closed under matrix
172    /// multiplication when both transforms have rotation *and* non-uniform
173    /// scale. In that case the result is an approximation (off-diagonal
174    /// rotation/scale coupling is dropped). For UI use (uniform scale or no
175    /// rotation) this is exact.
176    pub fn combine(&self, other: &Transform) -> Transform {
177        let c = self.rotate.cos();
178        let s = self.rotate.sin();
179
180        Transform {
181            translate_x: other.translate_x * self.scale_x * c
182                - other.translate_y * self.scale_y * s
183                + self.translate_x,
184            translate_y: other.translate_x * self.scale_x * s
185                + other.translate_y * self.scale_y * c
186                + self.translate_y,
187            scale_x: self.scale_x * other.scale_x,
188            scale_y: self.scale_y * other.scale_y,
189            rotate: self.rotate + other.rotate,
190            origin_x: self.origin_x,
191            origin_y: self.origin_y,
192        }
193    }
194}