Skip to main content

ascending_graphics/ui/
rectangle.rs

1use crate::{
2    AtlasSet, Bounds, CameraView, DrawOrder, GpuRenderer, GraphicsError, Index,
3    OrderedIndex, OtherError, RectVertex, Texture, Vec2, Vec3, Vec4,
4};
5use cosmic_text::Color;
6
7/// Rectangle to render to screen.
8/// Can contain a Images otherwise just colors.
9///
10#[derive(Debug)]
11pub struct Rect {
12    /// Position on the Screen.
13    pub pos: Vec3,
14    /// Width and Height of the Rect.
15    pub size: Vec2,
16    /// Color of the Rect.
17    pub color: Color,
18    /// Optional Image Index.
19    pub image: Option<usize>,
20    /// Texture X, Y, W and H if any apply.
21    pub uv: Vec4,
22    /// Width of the Rects Border.
23    pub border_width: f32,
24    /// Color of the Rects Border.
25    pub border_color: Color,
26    /// Rectangle Radius.
27    pub radius: f32,
28    /// [`CameraView`] used to render with.
29    pub camera_view: CameraView,
30    /// Instance Buffers Store ID.
31    pub store_id: Index,
32    /// the draw order of the rect. created/updated when update is called.
33    pub order: DrawOrder,
34    /// Optional Bounds for Clipping the Rect too.
35    pub bounds: Option<Bounds>,
36    /// If anything got updated we need to update the buffers too.
37    pub changed: bool,
38}
39
40impl Rect {
41    /// Creates a new [`Rect`] with rendering order layer, position and size.
42    ///
43    /// order_layer: Rendering Order Layer of the rect used in DrawOrder.
44    pub fn new(
45        renderer: &mut GpuRenderer,
46        pos: Vec3,
47        size: Vec2,
48        color: Color,
49        order_layer: u32,
50    ) -> Self {
51        let rect_size = bytemuck::bytes_of(&RectVertex::default()).len();
52
53        Self {
54            pos,
55            size,
56            color,
57            image: None,
58            uv: Vec4::default(),
59            border_width: 0.0,
60            border_color: Color::rgba(0, 0, 0, 0),
61            radius: 0.0,
62            camera_view: CameraView::default(),
63            store_id: renderer.new_buffer(rect_size, 0),
64            order: DrawOrder::new(false, Vec3::default(), order_layer),
65            bounds: None,
66            changed: true,
67        }
68    }
69
70    /// Creates a new [`Rect`] with rendering order layer, position and size, UV and Image
71    ///
72    /// order_layer: Rendering Order Layer of the rect used in DrawOrder.
73    pub fn new_with(
74        renderer: &mut GpuRenderer,
75        image: Option<usize>,
76        pos: Vec3,
77        size: Vec2,
78        uv: Vec4,
79        order_layer: u32,
80    ) -> Self {
81        let rect_size = bytemuck::bytes_of(&RectVertex::default()).len();
82
83        Self {
84            pos,
85            size,
86            color: Color::rgba(255, 255, 255, 255),
87            image,
88            uv,
89            border_width: 0.0,
90            border_color: Color::rgba(0, 0, 0, 0),
91            radius: 0.0,
92            camera_view: CameraView::default(),
93            store_id: renderer.new_buffer(rect_size, 0),
94            order: DrawOrder::new(false, Vec3::default(), order_layer),
95            bounds: None,
96            changed: true,
97        }
98    }
99
100    /// Unloads the [`Rect`] from the Instance Buffers Store.
101    ///
102    pub fn unload(self, renderer: &mut GpuRenderer) {
103        renderer.remove_buffer(self.store_id);
104    }
105
106    /// Updates the [`Rect`]'s order to overide the last set position.
107    /// Use this after calls to set_position to set it to a specific rendering order.
108    ///
109    pub fn set_order_pos(&mut self, order_override: Vec3) -> &mut Self {
110        self.order.set_pos(order_override);
111        self
112    }
113
114    /// Updates the [`Rect`]'s orders Render Layer.
115    ///
116    pub fn set_order_layer(&mut self, order_layer: u32) -> &mut Self {
117        self.order.order_layer = order_layer;
118        self
119    }
120
121    /// Updates the [`Rect`]'s [`DrawOrder`]'s is Alpha.
122    /// Use this after set_color, set_border_color or set_radius to overide the alpha sorting.
123    ///
124    pub fn set_order_alpha(&mut self, alpha: bool) -> &mut Self {
125        self.order.alpha = alpha;
126        self
127    }
128
129    /// Updates the [`Rect`]'s Clipping Bounds.
130    ///
131    pub fn update_bounds(&mut self, bounds: Option<Bounds>) -> &mut Self {
132        self.bounds = bounds;
133        self
134    }
135
136    /// Sets the [`Rect`]'s [`CameraView`] for rendering.
137    ///
138    pub fn set_camera_view(&mut self, camera_view: CameraView) -> &mut Self {
139        self.camera_view = camera_view;
140        self.changed = true;
141        self
142    }
143
144    /// Sets the [`Rect`]'s Color.
145    ///
146    pub fn set_color(&mut self, color: Color) -> &mut Self {
147        self.color = color;
148        self.order.alpha = self.border_color.a() < 255
149            || self.radius > 0.0
150            || self.color.a() < 255;
151        self.changed = true;
152        self
153    }
154
155    /// Sets the [`Rect`]'s Border Color.
156    ///
157    pub fn set_border_color(&mut self, color: Color) -> &mut Self {
158        self.border_color = color;
159        self.order.alpha = self.border_color.a() < 255
160            || self.radius > 0.0
161            || self.color.a() < 255;
162        self.changed = true;
163        self
164    }
165
166    /// Sets the [`Rect`]'s Texture.
167    ///
168    pub fn set_texture(
169        &mut self,
170        renderer: &GpuRenderer,
171        atlas: &mut AtlasSet,
172        path: String,
173    ) -> Result<&mut Self, GraphicsError> {
174        let (id, allocation) =
175            Texture::upload_from_with_alloc(path, atlas, renderer)
176                .ok_or_else(|| OtherError::new("failed to upload image"))?;
177
178        let rect = allocation.rect();
179
180        self.uv = Vec4::new(0.0, 0.0, rect.2 as f32, rect.3 as f32);
181        self.image = Some(id);
182        self.changed = true;
183        Ok(self)
184    }
185
186    /// Sets the [`Rect`]'s Texture X,Y, W, H details.
187    ///
188    pub fn set_container_uv(&mut self, uv: Vec4) -> &mut Self {
189        self.uv = uv;
190        self.changed = true;
191        self
192    }
193
194    /// Sets the [`Rect`]'s Position.
195    ///
196    pub fn set_pos(&mut self, pos: Vec3) -> &mut Self {
197        self.pos = pos;
198        self.order.set_pos(pos);
199        self.changed = true;
200        self
201    }
202
203    /// Sets the [`Rect`]'s Width and Height.
204    ///
205    pub fn set_size(&mut self, size: Vec2) -> &mut Self {
206        self.size = size;
207        self.changed = true;
208        self
209    }
210
211    /// Sets the [`Rect`]'s Border Width.
212    ///
213    pub fn set_border_width(&mut self, size: f32) -> &mut Self {
214        self.border_width = size;
215        self.changed = true;
216        self
217    }
218
219    /// Sets the [`Rect`]'s Corner Radius.
220    ///
221    pub fn set_radius(&mut self, radius: f32) -> &mut Self {
222        self.radius = radius;
223        self.order.alpha =
224            self.border_color.a() < 255 || radius > 0.0 || self.color.a() < 255;
225        self.changed = true;
226        self
227    }
228
229    /// Updates the [`Rect`]'s Buffers to prepare them for rendering.
230    ///
231    pub fn create_quad(
232        &mut self,
233        renderer: &mut GpuRenderer,
234        atlas: &mut AtlasSet,
235    ) {
236        let (uv, layer) = if let Some(id) = self.image {
237            let tex = match atlas.get(id) {
238                Some(tex) => tex,
239                None => return,
240            };
241            let (u, v, width, height) = tex.rect();
242            (
243                [
244                    self.uv.x + u as f32,
245                    self.uv.y + v as f32,
246                    self.uv.z.min(width as f32),
247                    self.uv.w.min(height as f32),
248                ],
249                tex.layer as u32,
250            )
251        } else {
252            ([0.0, 0.0, 0.0, 0.0], 0)
253        };
254
255        let instance = RectVertex {
256            pos: self.pos.to_array(),
257            size: self.size.to_array(),
258            border_width: self.border_width,
259            radius: self.radius,
260            uv,
261            layer,
262            color: self.color.0,
263            border_color: self.border_color.0,
264            camera_view: self.camera_view as u32,
265        };
266
267        if let Some(store) = renderer.get_buffer_mut(self.store_id) {
268            let bytes = bytemuck::bytes_of(&instance);
269
270            if bytes.len() != store.store.len() {
271                store.store.resize_with(bytes.len(), || 0);
272            }
273
274            store.store.copy_from_slice(bytes);
275            store.changed = true;
276        }
277    }
278
279    /// Used to check and update the vertex array.
280    /// Returns a [`OrderedIndex`] used in Rendering.
281    ///
282    pub fn update(
283        &mut self,
284        renderer: &mut GpuRenderer,
285        atlas: &mut AtlasSet,
286    ) -> OrderedIndex {
287        // if points added or any data changed recalculate paths.
288        if self.changed {
289            self.create_quad(renderer, atlas);
290            self.changed = false;
291        }
292
293        OrderedIndex::new_with_bounds(
294            self.order,
295            self.store_id,
296            0,
297            self.bounds,
298            self.camera_view,
299        )
300    }
301
302    /// Checks if the Mouse position is within the Rects location.
303    ///
304    pub fn check_mouse_bounds(&self, mouse_pos: Vec2) -> bool {
305        if self.radius > 0.0 {
306            let pos = [self.pos.x, self.pos.y];
307
308            let inner_size = [
309                self.size.x - self.radius * 2.0,
310                self.size.y - self.radius * 2.0,
311            ];
312            let top_left = [pos[0] + self.radius, pos[1] + self.radius];
313            let bottom_right =
314                [top_left[0] + inner_size[0], top_left[1] + inner_size[1]];
315
316            let top_left_distance =
317                [top_left[0] - mouse_pos.x, top_left[1] - mouse_pos.y];
318            let bottom_right_distance =
319                [mouse_pos.x - bottom_right[0], mouse_pos.y - bottom_right[1]];
320
321            let dist = [
322                top_left_distance[0].max(bottom_right_distance[0]).max(0.0),
323                top_left_distance[1].max(bottom_right_distance[1]).max(0.0),
324            ];
325
326            let dist = (dist[0] * dist[0] + dist[1] * dist[1]).sqrt();
327
328            dist < self.radius
329        } else {
330            mouse_pos[0] > self.pos.x
331                && mouse_pos[0] < self.pos.x + self.size.x
332                && mouse_pos[1] > self.pos.y
333                && mouse_pos[1] < self.pos.y + self.size.y
334        }
335    }
336}