cranpose_ui_graphics/
geometry.rs

1//! Geometric primitives: Point, Size, Rect, Insets, Path
2
3use crate::Brush;
4use std::ops::AddAssign;
5
6#[derive(Clone, Copy, Debug, PartialEq, Default)]
7pub struct Point {
8    pub x: f32,
9    pub y: f32,
10}
11
12impl Point {
13    pub const fn new(x: f32, y: f32) -> Self {
14        Self { x, y }
15    }
16
17    pub const ZERO: Point = Point { x: 0.0, y: 0.0 };
18}
19
20#[derive(Clone, Copy, Debug, PartialEq, Default)]
21pub struct Size {
22    pub width: f32,
23    pub height: f32,
24}
25
26impl Size {
27    pub const fn new(width: f32, height: f32) -> Self {
28        Self { width, height }
29    }
30
31    pub const ZERO: Size = Size {
32        width: 0.0,
33        height: 0.0,
34    };
35}
36
37#[derive(Clone, Copy, Debug, PartialEq)]
38pub struct Rect {
39    pub x: f32,
40    pub y: f32,
41    pub width: f32,
42    pub height: f32,
43}
44
45impl Rect {
46    pub fn from_origin_size(origin: Point, size: Size) -> Self {
47        Self {
48            x: origin.x,
49            y: origin.y,
50            width: size.width,
51            height: size.height,
52        }
53    }
54
55    pub fn from_size(size: Size) -> Self {
56        Self {
57            x: 0.0,
58            y: 0.0,
59            width: size.width,
60            height: size.height,
61        }
62    }
63
64    pub fn translate(&self, dx: f32, dy: f32) -> Self {
65        Self {
66            x: self.x + dx,
67            y: self.y + dy,
68            width: self.width,
69            height: self.height,
70        }
71    }
72
73    pub fn contains(&self, x: f32, y: f32) -> bool {
74        x >= self.x && y >= self.y && x <= self.x + self.width && y <= self.y + self.height
75    }
76}
77
78/// Padding values for each edge of a rectangle.
79#[derive(Clone, Copy, Debug, Default, PartialEq)]
80pub struct EdgeInsets {
81    pub left: f32,
82    pub top: f32,
83    pub right: f32,
84    pub bottom: f32,
85}
86
87impl EdgeInsets {
88    pub fn uniform(all: f32) -> Self {
89        Self {
90            left: all,
91            top: all,
92            right: all,
93            bottom: all,
94        }
95    }
96
97    pub fn horizontal(horizontal: f32) -> Self {
98        Self {
99            left: horizontal,
100            right: horizontal,
101            ..Self::default()
102        }
103    }
104
105    pub fn vertical(vertical: f32) -> Self {
106        Self {
107            top: vertical,
108            bottom: vertical,
109            ..Self::default()
110        }
111    }
112
113    pub fn symmetric(horizontal: f32, vertical: f32) -> Self {
114        Self {
115            left: horizontal,
116            right: horizontal,
117            top: vertical,
118            bottom: vertical,
119        }
120    }
121
122    pub fn from_components(left: f32, top: f32, right: f32, bottom: f32) -> Self {
123        Self {
124            left,
125            top,
126            right,
127            bottom,
128        }
129    }
130
131    pub fn is_zero(&self) -> bool {
132        self.left == 0.0 && self.top == 0.0 && self.right == 0.0 && self.bottom == 0.0
133    }
134
135    pub fn horizontal_sum(&self) -> f32 {
136        self.left + self.right
137    }
138
139    pub fn vertical_sum(&self) -> f32 {
140        self.top + self.bottom
141    }
142}
143
144impl AddAssign for EdgeInsets {
145    fn add_assign(&mut self, rhs: Self) {
146        self.left += rhs.left;
147        self.top += rhs.top;
148        self.right += rhs.right;
149        self.bottom += rhs.bottom;
150    }
151}
152
153#[derive(Clone, Copy, Debug, Default, PartialEq)]
154pub struct CornerRadii {
155    pub top_left: f32,
156    pub top_right: f32,
157    pub bottom_right: f32,
158    pub bottom_left: f32,
159}
160
161impl CornerRadii {
162    pub fn uniform(radius: f32) -> Self {
163        Self {
164            top_left: radius,
165            top_right: radius,
166            bottom_right: radius,
167            bottom_left: radius,
168        }
169    }
170}
171
172#[derive(Clone, Copy, Debug, PartialEq)]
173pub struct RoundedCornerShape {
174    radii: CornerRadii,
175}
176
177impl RoundedCornerShape {
178    pub fn new(top_left: f32, top_right: f32, bottom_right: f32, bottom_left: f32) -> Self {
179        Self {
180            radii: CornerRadii {
181                top_left,
182                top_right,
183                bottom_right,
184                bottom_left,
185            },
186        }
187    }
188
189    pub fn uniform(radius: f32) -> Self {
190        Self {
191            radii: CornerRadii::uniform(radius),
192        }
193    }
194
195    pub fn with_radii(radii: CornerRadii) -> Self {
196        Self { radii }
197    }
198
199    pub fn resolve(&self, width: f32, height: f32) -> CornerRadii {
200        let mut resolved = self.radii;
201        let max_width = (width / 2.0).max(0.0);
202        let max_height = (height / 2.0).max(0.0);
203        resolved.top_left = resolved.top_left.clamp(0.0, max_width).min(max_height);
204        resolved.top_right = resolved.top_right.clamp(0.0, max_width).min(max_height);
205        resolved.bottom_right = resolved.bottom_right.clamp(0.0, max_width).min(max_height);
206        resolved.bottom_left = resolved.bottom_left.clamp(0.0, max_width).min(max_height);
207        resolved
208    }
209
210    pub fn radii(&self) -> CornerRadii {
211        self.radii
212    }
213}
214
215#[derive(Clone, Copy, Debug, PartialEq)]
216pub struct GraphicsLayer {
217    pub alpha: f32,
218    pub scale: f32,
219    pub translation_x: f32,
220    pub translation_y: f32,
221}
222
223impl Default for GraphicsLayer {
224    fn default() -> Self {
225        Self {
226            alpha: 1.0,
227            scale: 1.0,
228            translation_x: 0.0,
229            translation_y: 0.0,
230        }
231    }
232}
233
234#[derive(Clone, Debug, PartialEq)]
235pub enum DrawPrimitive {
236    Rect {
237        rect: Rect,
238        brush: Brush,
239    },
240    RoundRect {
241        rect: Rect,
242        brush: Brush,
243        radii: CornerRadii,
244    },
245}
246
247pub trait DrawScope {
248    fn size(&self) -> Size;
249    fn draw_content(&self);
250    fn draw_rect(&mut self, brush: Brush);
251    /// Draws a rectangle at the specified position and size.
252    fn draw_rect_at(&mut self, rect: Rect, brush: Brush);
253    fn draw_round_rect(&mut self, brush: Brush, radii: CornerRadii);
254    fn into_primitives(self) -> Vec<DrawPrimitive>;
255}
256
257#[derive(Default)]
258pub struct DrawScopeDefault {
259    size: Size,
260    primitives: Vec<DrawPrimitive>,
261}
262
263impl DrawScopeDefault {
264    pub fn new(size: Size) -> Self {
265        Self {
266            size,
267            primitives: Vec::new(),
268        }
269    }
270}
271
272impl DrawScope for DrawScopeDefault {
273    fn size(&self) -> Size {
274        self.size
275    }
276
277    fn draw_content(&self) {}
278
279    fn draw_rect(&mut self, brush: Brush) {
280        self.primitives.push(DrawPrimitive::Rect {
281            rect: Rect::from_size(self.size),
282            brush,
283        });
284    }
285
286    fn draw_rect_at(&mut self, rect: Rect, brush: Brush) {
287        self.primitives.push(DrawPrimitive::Rect { rect, brush });
288    }
289
290    fn draw_round_rect(&mut self, brush: Brush, radii: CornerRadii) {
291        self.primitives.push(DrawPrimitive::RoundRect {
292            rect: Rect::from_size(self.size),
293            brush,
294            radii,
295        });
296    }
297
298    fn into_primitives(self) -> Vec<DrawPrimitive> {
299        self.primitives
300    }
301}