rg3d_ui/
draw.rs

1use crate::{
2    brush::Brush,
3    core::{
4        algebra::Vector2,
5        color::Color,
6        math::{self, Rect, TriangleDefinition},
7    },
8    formatted_text::FormattedText,
9    ttf::SharedFont,
10    Thickness,
11};
12use std::{any::Any, ops::Range, sync::Arc};
13
14#[derive(Clone)]
15#[repr(C)]
16pub struct Vertex {
17    pub pos: Vector2<f32>,
18    pub tex_coord: Vector2<f32>,
19    pub color: Color,
20}
21
22impl Vertex {
23    fn new(pos: Vector2<f32>, tex_coord: Vector2<f32>) -> Vertex {
24        Vertex {
25            pos,
26            tex_coord,
27            color: Color::WHITE,
28        }
29    }
30}
31
32pub type Texture = dyn Any + Sync + Send;
33
34#[derive(Debug, Clone)]
35pub struct SharedTexture(pub Arc<Texture>);
36
37impl<T: Any + Sync + Send> From<Arc<T>> for SharedTexture {
38    fn from(arc: Arc<T>) -> Self {
39        SharedTexture(arc)
40    }
41}
42
43impl PartialEq for SharedTexture {
44    fn eq(&self, other: &Self) -> bool {
45        // Cast fat pointers to thin first.
46        let ptr_a = &*self.0 as *const _ as *const ();
47        let ptr_b = &*other.0 as *const _ as *const ();
48        // Compare thin pointers.
49        std::ptr::eq(ptr_a, ptr_b)
50    }
51}
52
53#[derive(Clone)]
54pub enum CommandTexture {
55    None,
56    Texture(SharedTexture),
57    Font(SharedFont),
58}
59
60/// A set of triangles that will be used for clipping.
61#[derive(Clone)]
62pub struct ClippingGeometry {
63    pub vertex_buffer: Vec<Vertex>,
64    pub triangle_buffer: Vec<TriangleDefinition>,
65}
66
67impl Draw for ClippingGeometry {
68    #[inline(always)]
69    fn push_vertex(&mut self, pos: Vector2<f32>, tex_coord: Vector2<f32>) {
70        self.vertex_buffer.push(Vertex::new(pos, tex_coord));
71    }
72
73    #[inline(always)]
74    fn push_vertex_raw(&mut self, vertex: Vertex) {
75        self.vertex_buffer.push(vertex);
76    }
77
78    #[inline(always)]
79    fn push_triangle(&mut self, a: u32, b: u32, c: u32) {
80        self.triangle_buffer.push(TriangleDefinition([a, b, c]));
81    }
82
83    #[inline(always)]
84    fn last_vertex_index(&self) -> u32 {
85        self.vertex_buffer.len() as u32
86    }
87}
88
89impl ClippingGeometry {
90    pub fn is_contains_point(&self, pos: Vector2<f32>) -> bool {
91        for triangle in self.triangle_buffer.iter() {
92            if let Some((va, vb, vc)) = self.triangle_points(triangle) {
93                if math::is_point_inside_2d_triangle(pos, va.pos, vb.pos, vc.pos) {
94                    return true;
95                }
96            }
97        }
98
99        false
100    }
101
102    pub fn triangle_points(
103        &self,
104        triangle: &TriangleDefinition,
105    ) -> Option<(&Vertex, &Vertex, &Vertex)> {
106        let a = self.vertex_buffer.get(triangle[0] as usize)?;
107        let b = self.vertex_buffer.get(triangle[1] as usize)?;
108        let c = self.vertex_buffer.get(triangle[2] as usize)?;
109        Some((a, b, c))
110    }
111}
112
113#[derive(Clone)]
114pub struct Command {
115    /// Clipping bounds, should be used for scissor-test. Screen-space.
116    pub clip_bounds: Rect<f32>,
117    /// Total bounds of command's geometry. Screen-space.
118    pub bounds: Rect<f32>,
119    /// Brush defines visual appearance of rendered geometry.
120    pub brush: Brush,
121    pub texture: CommandTexture,
122    pub triangles: Range<usize>,
123    pub opacity: f32,
124    /// A set of triangles that defines clipping region.
125    pub clipping_geometry: Option<ClippingGeometry>,
126}
127
128pub trait Draw {
129    fn push_vertex(&mut self, pos: Vector2<f32>, tex_coord: Vector2<f32>);
130
131    fn push_vertex_raw(&mut self, vertex: Vertex);
132
133    fn push_triangle(&mut self, a: u32, b: u32, c: u32);
134
135    fn last_vertex_index(&self) -> u32;
136
137    fn push_triangle_multicolor(&mut self, vertices: [(Vector2<f32>, Color); 3]) {
138        let index = self.last_vertex_index();
139        for &(pos, color) in &vertices {
140            self.push_vertex_raw(Vertex {
141                pos,
142                tex_coord: Vector2::new(0.0, 0.0),
143                color,
144            });
145        }
146
147        self.push_triangle(index, index + 1, index + 2);
148    }
149
150    fn push_triangle_filled(&mut self, vertices: [Vector2<f32>; 3]) {
151        let index = self.last_vertex_index();
152
153        for &pos in &vertices {
154            self.push_vertex(pos, Default::default());
155        }
156
157        self.push_triangle(index, index + 1, index + 2);
158    }
159
160    fn push_line(&mut self, a: Vector2<f32>, b: Vector2<f32>, thickness: f32) {
161        let index = self.last_vertex_index();
162        let perp = get_line_thickness_vector(a, b, thickness);
163        self.push_vertex(a - perp, Vector2::new(0.0, 0.0));
164        self.push_vertex(b - perp, Vector2::new(1.0, 0.0));
165        self.push_vertex(a + perp, Vector2::new(1.0, 1.0));
166        self.push_vertex(b + perp, Vector2::new(0.0, 1.0));
167
168        self.push_triangle(index, index + 1, index + 2);
169        self.push_triangle(index + 2, index + 1, index + 3);
170    }
171
172    fn push_rect(&mut self, rect: &Rect<f32>, thickness: f32) {
173        let offset = thickness * 0.5;
174
175        let left_top = Vector2::new(rect.x() + offset, rect.y() + thickness);
176        let right_top = Vector2::new(rect.x() + rect.w() - offset, rect.y() + thickness);
177        let right_bottom = Vector2::new(
178            rect.x() + rect.w() - offset,
179            rect.y() + rect.h() - thickness,
180        );
181        let left_bottom = Vector2::new(rect.x() + offset, rect.y() + rect.h() - thickness);
182        let left_top_off = Vector2::new(rect.x(), rect.y() + offset);
183        let right_top_off = Vector2::new(rect.x() + rect.w(), rect.y() + offset);
184        let right_bottom_off = Vector2::new(rect.x() + rect.w(), rect.y() + rect.h() - offset);
185        let left_bottom_off = Vector2::new(rect.x(), rect.y() + rect.h() - offset);
186
187        // Horizontal lines
188        self.push_line(left_top_off, right_top_off, thickness);
189        self.push_line(right_bottom_off, left_bottom_off, thickness);
190
191        // Vertical line
192        self.push_line(right_top, right_bottom, thickness);
193        self.push_line(left_bottom, left_top, thickness);
194    }
195
196    fn push_rect_vary(&mut self, rect: &Rect<f32>, thickness: Thickness) {
197        let left_top = Vector2::new(rect.x() + thickness.left * 0.5, rect.y() + thickness.top);
198        let right_top = Vector2::new(
199            rect.x() + rect.w() - thickness.right * 0.5,
200            rect.y() + thickness.top,
201        );
202        let right_bottom = Vector2::new(
203            rect.x() + rect.w() - thickness.right * 0.5,
204            rect.y() + rect.h() - thickness.bottom,
205        );
206        let left_bottom = Vector2::new(
207            rect.x() + thickness.left * 0.5,
208            rect.y() + rect.h() - thickness.bottom,
209        );
210        let left_top_off = Vector2::new(rect.x(), rect.y() + thickness.top * 0.5);
211        let right_top_off = Vector2::new(rect.x() + rect.w(), rect.y() + thickness.top * 0.5);
212        let right_bottom_off = Vector2::new(
213            rect.x() + rect.w(),
214            rect.y() + rect.h() - thickness.bottom * 0.5,
215        );
216        let left_bottom_off = Vector2::new(rect.x(), rect.y() + rect.h() - thickness.bottom * 0.5);
217
218        // Horizontal lines
219        self.push_line(left_top_off, right_top_off, thickness.top);
220        self.push_line(right_bottom_off, left_bottom_off, thickness.bottom);
221
222        // Vertical lines
223        self.push_line(right_top, right_bottom, thickness.right);
224        self.push_line(left_bottom, left_top, thickness.left);
225    }
226
227    fn push_rect_filled(&mut self, rect: &Rect<f32>, tex_coords: Option<&[Vector2<f32>; 4]>) {
228        let index = self.last_vertex_index();
229        self.push_vertex(
230            Vector2::new(rect.x(), rect.y()),
231            tex_coords.map_or(Vector2::new(0.0, 0.0), |t| t[0]),
232        );
233        self.push_vertex(
234            Vector2::new(rect.x() + rect.w(), rect.y()),
235            tex_coords.map_or(Vector2::new(1.0, 0.0), |t| t[1]),
236        );
237        self.push_vertex(
238            Vector2::new(rect.x() + rect.w(), rect.y() + rect.h()),
239            tex_coords.map_or(Vector2::new(1.0, 1.0), |t| t[2]),
240        );
241        self.push_vertex(
242            Vector2::new(rect.x(), rect.y() + rect.h()),
243            tex_coords.map_or(Vector2::new(0.0, 1.0), |t| t[3]),
244        );
245
246        self.push_triangle(index, index + 1, index + 2);
247        self.push_triangle(index, index + 2, index + 3);
248    }
249
250    fn push_rect_multicolor(&mut self, rect: &Rect<f32>, colors: [Color; 4]) {
251        let index = self.last_vertex_index();
252        self.push_vertex_raw(Vertex {
253            pos: rect.left_top_corner(),
254            tex_coord: Vector2::new(0.0, 0.0),
255            color: colors[0],
256        });
257        self.push_vertex_raw(Vertex {
258            pos: rect.right_top_corner(),
259            tex_coord: Vector2::new(1.0, 0.0),
260            color: colors[1],
261        });
262        self.push_vertex_raw(Vertex {
263            pos: rect.right_bottom_corner(),
264            tex_coord: Vector2::new(1.0, 1.0),
265            color: colors[2],
266        });
267        self.push_vertex_raw(Vertex {
268            pos: rect.left_bottom_corner(),
269            tex_coord: Vector2::new(0.0, 1.0),
270            color: colors[3],
271        });
272
273        self.push_triangle(index, index + 1, index + 2);
274        self.push_triangle(index, index + 2, index + 3);
275    }
276
277    fn push_circle(&mut self, origin: Vector2<f32>, radius: f32, segments: usize, color: Color) {
278        if segments >= 3 {
279            let center_index = self.last_vertex_index();
280
281            self.push_vertex_raw(Vertex {
282                pos: origin,
283                tex_coord: Vector2::default(),
284                color,
285            });
286
287            let two_pi = 2.0 * std::f32::consts::PI;
288            let delta_angle = two_pi / (segments as f32);
289            let mut angle: f32 = 0.0;
290            for _ in 0..segments {
291                let x = origin.x + radius * angle.cos();
292                let y = origin.y + radius * angle.sin();
293                self.push_vertex_raw(Vertex {
294                    pos: Vector2::new(x, y),
295                    tex_coord: Vector2::default(),
296                    color,
297                });
298                angle += delta_angle;
299            }
300
301            let first_vertex = center_index + 1;
302            for segment in 0..segments {
303                self.push_triangle(
304                    center_index,
305                    first_vertex + segment as u32,
306                    first_vertex + (segment as u32 + 1) % segments as u32,
307                );
308            }
309        }
310    }
311}
312
313pub struct DrawingContext {
314    vertex_buffer: Vec<Vertex>,
315    triangle_buffer: Vec<TriangleDefinition>,
316    command_buffer: Vec<Command>,
317    opacity_stack: Vec<f32>,
318    triangles_to_commit: usize,
319}
320
321fn get_line_thickness_vector(a: Vector2<f32>, b: Vector2<f32>, thickness: f32) -> Vector2<f32> {
322    if let Some(dir) = (b - a).try_normalize(f32::EPSILON) {
323        Vector2::new(dir.y, -dir.x).scale(thickness * 0.5)
324    } else {
325        Vector2::default()
326    }
327}
328
329impl Default for DrawingContext {
330    fn default() -> Self {
331        Self::new()
332    }
333}
334
335impl Draw for DrawingContext {
336    #[inline(always)]
337    fn push_vertex(&mut self, pos: Vector2<f32>, tex_coord: Vector2<f32>) {
338        self.vertex_buffer.push(Vertex::new(pos, tex_coord));
339    }
340
341    #[inline(always)]
342    fn push_vertex_raw(&mut self, vertex: Vertex) {
343        self.vertex_buffer.push(vertex);
344    }
345
346    #[inline(always)]
347    fn push_triangle(&mut self, a: u32, b: u32, c: u32) {
348        self.triangle_buffer.push(TriangleDefinition([a, b, c]));
349        self.triangles_to_commit += 1;
350    }
351
352    #[inline(always)]
353    fn last_vertex_index(&self) -> u32 {
354        self.vertex_buffer.len() as u32
355    }
356}
357
358impl DrawingContext {
359    pub fn new() -> DrawingContext {
360        DrawingContext {
361            vertex_buffer: Vec::new(),
362            triangle_buffer: Vec::new(),
363            command_buffer: Vec::new(),
364            triangles_to_commit: 0,
365            opacity_stack: vec![1.0],
366        }
367    }
368
369    #[inline]
370    pub fn clear(&mut self) {
371        self.vertex_buffer.clear();
372        self.triangle_buffer.clear();
373        self.command_buffer.clear();
374        self.opacity_stack.clear();
375        self.opacity_stack.push(1.0);
376        self.triangles_to_commit = 0;
377    }
378
379    #[inline]
380    pub fn get_vertices(&self) -> &[Vertex] {
381        self.vertex_buffer.as_slice()
382    }
383
384    #[inline]
385    pub fn get_triangles(&self) -> &[TriangleDefinition] {
386        self.triangle_buffer.as_slice()
387    }
388
389    #[inline]
390    pub fn get_commands(&self) -> &Vec<Command> {
391        &self.command_buffer
392    }
393
394    pub fn push_opacity(&mut self, opacity: f32) {
395        self.opacity_stack.push(opacity);
396    }
397
398    pub fn pop_opacity(&mut self) {
399        self.opacity_stack.pop().unwrap();
400    }
401
402    pub fn triangle_points(
403        &self,
404        triangle: &TriangleDefinition,
405    ) -> Option<(&Vertex, &Vertex, &Vertex)> {
406        let a = self.vertex_buffer.get(triangle[0] as usize)?;
407        let b = self.vertex_buffer.get(triangle[1] as usize)?;
408        let c = self.vertex_buffer.get(triangle[2] as usize)?;
409        Some((a, b, c))
410    }
411
412    pub fn is_command_contains_point(&self, command: &Command, pos: Vector2<f32>) -> bool {
413        for i in command.triangles.clone() {
414            if let Some(triangle) = self.triangle_buffer.get(i) {
415                if let Some((va, vb, vc)) = self.triangle_points(triangle) {
416                    if math::is_point_inside_2d_triangle(pos, va.pos, vb.pos, vc.pos) {
417                        return true;
418                    }
419                }
420            }
421        }
422
423        false
424    }
425
426    fn pending_range(&self) -> Range<usize> {
427        if self.triangle_buffer.is_empty() {
428            0..self.triangles_to_commit
429        } else {
430            (self.triangle_buffer.len() - self.triangles_to_commit)..self.triangle_buffer.len()
431        }
432    }
433
434    fn bounds_of(&self, range: Range<usize>) -> Rect<f32> {
435        let mut bounds = Rect::new(f32::MAX, f32::MAX, 0.0, 0.0);
436        for i in range {
437            for &k in self.triangle_buffer[i].as_ref() {
438                bounds.push(self.vertex_buffer[k as usize].pos);
439            }
440        }
441        bounds
442    }
443
444    pub fn commit(
445        &mut self,
446        clip_bounds: Rect<f32>,
447        brush: Brush,
448        texture: CommandTexture,
449        clipping_geometry: Option<ClippingGeometry>,
450    ) {
451        if self.triangles_to_commit > 0 {
452            let triangles = self.pending_range();
453            let bounds = self.bounds_of(triangles.clone());
454
455            let opacity = *self.opacity_stack.last().unwrap();
456            self.command_buffer.push(Command {
457                clip_bounds,
458                bounds,
459                brush,
460                texture,
461                triangles,
462                opacity,
463                clipping_geometry,
464            });
465            self.triangles_to_commit = 0;
466        }
467    }
468
469    pub fn draw_text(
470        &mut self,
471        clip_bounds: Rect<f32>,
472        position: Vector2<f32>,
473        formatted_text: &FormattedText,
474    ) {
475        let font = formatted_text.get_font();
476
477        for element in formatted_text.get_glyphs() {
478            let bounds = element.get_bounds();
479
480            let final_bounds = Rect::new(
481                position.x + bounds.x(),
482                position.y + bounds.y(),
483                bounds.w(),
484                bounds.h(),
485            );
486
487            self.push_rect_filled(&final_bounds, Some(element.get_tex_coords()));
488        }
489
490        self.commit(
491            clip_bounds,
492            formatted_text.brush(),
493            CommandTexture::Font(font),
494            None,
495        )
496    }
497}