1#[repr(C)]
2#[derive(Clone, Copy, Debug, Default, PartialEq)]
3pub struct Vec2 {
4 pub x: f32,
5 pub y: f32,
6}
7
8impl Vec2 {
9 pub const ZERO: Vec2 = Vec2 { x: 0.0, y: 0.0 };
10
11 pub fn to_value(self) -> crate::value::Value {
13 use crate::value::Value;
14 Value::map([("x", Value::float(self.x)), ("y", Value::float(self.y))])
15 }
16
17 pub fn new(x: f32, y: f32) -> Self {
18 Self { x, y }
19 }
20
21 pub fn plus(self, o: Vec2) -> Vec2 {
23 Vec2::new(self.x + o.x, self.y + o.y)
24 }
25
26 pub fn minus(self, o: Vec2) -> Vec2 {
28 Vec2::new(self.x - o.x, self.y - o.y)
29 }
30
31 pub(crate) fn snapped(self, scale: f32) -> Self {
43 if scale <= 0.0 || !scale.is_finite() {
44 return self;
45 }
46 Self::new(
47 snap_px(self.x * scale) / scale,
48 snap_px(self.y * scale) / scale,
49 )
50 }
51}
52
53pub(crate) fn snap_px(v: f32) -> f32 {
73 const TIE: f32 = 1.0 / 1024.0;
74 (v + 0.5 + TIE).floor()
75}
76
77#[repr(C)]
78#[derive(Clone, Copy, Debug, Default, PartialEq)]
79pub struct Size {
80 pub w: f32,
81 pub h: f32,
82}
83
84impl Size {
85 pub const ZERO: Size = Size { w: 0.0, h: 0.0 };
86
87 pub fn new(w: f32, h: f32) -> Self {
88 Self { w, h }
89 }
90}
91
92#[repr(C)]
93#[derive(Clone, Copy, Debug, Default, PartialEq)]
94pub struct Rect {
95 pub x: f32,
96 pub y: f32,
97 pub w: f32,
98 pub h: f32,
99}
100
101impl Rect {
102 pub fn to_value(self) -> crate::value::Value {
105 use crate::value::Value;
106 Value::map([
107 ("x", Value::float(self.x)),
108 ("y", Value::float(self.y)),
109 ("w", Value::float(self.w)),
110 ("h", Value::float(self.h)),
111 ])
112 }
113
114 pub fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
115 Self { x, y, w, h }
116 }
117
118 pub fn from_pos_size(pos: Vec2, size: Size) -> Self {
119 Self {
120 x: pos.x,
121 y: pos.y,
122 w: size.w,
123 h: size.h,
124 }
125 }
126
127 pub fn center(&self) -> Vec2 {
129 Vec2 {
130 x: self.x + self.w / 2.0,
131 y: self.y + self.h / 2.0,
132 }
133 }
134
135 pub fn contains(&self, p: Vec2) -> bool {
136 p.x >= self.x && p.x < self.x + self.w && p.y >= self.y && p.y < self.y + self.h
137 }
138
139 pub fn scaled(&self, s: f32) -> Rect {
140 Rect {
141 x: self.x * s,
142 y: self.y * s,
143 w: self.w * s,
144 h: self.h * s,
145 }
146 }
147
148 pub(crate) fn on_pixels(&self) -> Rect {
153 let (x0, y0) = (snap_px(self.x), snap_px(self.y));
154 let (x1, y1) = (snap_px(self.x + self.w), snap_px(self.y + self.h));
155 Rect::new(x0, y0, x1 - x0, y1 - y0)
156 }
157
158 pub fn union(&self, other: &Rect) -> Rect {
161 let x = self.x.min(other.x);
162 let y = self.y.min(other.y);
163 let r = (self.x + self.w).max(other.x + other.w);
164 let b = (self.y + self.h).max(other.y + other.h);
165 Rect::new(x, y, r - x, b - y)
166 }
167
168 pub fn intersect(&self, other: &Rect) -> Rect {
169 let x = self.x.max(other.x);
170 let y = self.y.max(other.y);
171 let r = (self.x + self.w).min(other.x + other.w);
172 let b = (self.y + self.h).min(other.y + other.h);
173 Rect {
174 x,
175 y,
176 w: (r - x).max(0.0),
177 h: (b - y).max(0.0),
178 }
179 }
180}
181
182#[repr(C)]
184#[derive(Clone, Copy, Debug, Default, PartialEq)]
185pub struct Edges {
186 pub l: f32,
187 pub r: f32,
188 pub t: f32,
189 pub b: f32,
190}
191
192impl Edges {
193 pub fn all(v: f32) -> Self {
194 Self {
195 l: v,
196 r: v,
197 t: v,
198 b: v,
199 }
200 }
201
202 pub fn xy(x: f32, y: f32) -> Self {
203 Self {
204 l: x,
205 r: x,
206 t: y,
207 b: y,
208 }
209 }
210
211 pub fn x(&self) -> f32 {
213 self.l + self.r
214 }
215
216 pub fn y(&self) -> f32 {
218 self.t + self.b
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225
226 #[test]
227 fn a_whole_pixel_shift_moves_a_snapped_coordinate_by_exactly_that() {
228 for v in [0.0f32, 0.25, 0.5, 10.5, -0.5, -26.5, 31.5, 7.3, -118.5] {
233 for k in [-200.0f32, -1.0, 0.0, 1.0, 3.0, 141.0] {
234 assert_eq!(
235 snap_px(v + k),
236 snap_px(v) + k,
237 "snap_px({v}) shifted by {k}"
238 );
239 }
240 }
241 }
242
243 #[test]
244 fn a_snapped_displacement_is_whole_physical_pixels() {
245 for scale in [1.0f32, 1.25, 1.5, 2.0, 3.0] {
246 for d in [0.0f32, 0.1, -0.4, 12.34, -99.9] {
247 let s = Vec2::new(d, -d).snapped(scale);
248 for v in [s.x, s.y] {
249 let px = v * scale;
250 assert!((px - px.round()).abs() < 1e-3, "{v} at {scale} is {px} px");
251 }
252 }
253 }
254 assert_eq!(Vec2::new(1.5, 2.5).snapped(0.0), Vec2::new(1.5, 2.5));
257 }
258}