fyrox_ui/
draw.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21use crate::font::FontHeight;
22use crate::style::resource::StyleResource;
23use crate::{
24    brush::Brush,
25    core::{
26        algebra::{Matrix3, Point2, Vector2},
27        color::Color,
28        log::Log,
29        math::{self, Rect, TriangleDefinition},
30    },
31    font::FontResource,
32    formatted_text::FormattedText,
33    Thickness,
34};
35use bytemuck::{Pod, Zeroable};
36use fyrox_core::math::round_to_step;
37use fyrox_texture::TextureResource;
38use std::ops::Range;
39
40#[derive(Clone, Copy, Debug, Pod, Zeroable)]
41#[repr(C)]
42pub struct Vertex {
43    pub pos: Vector2<f32>,
44    pub tex_coord: Vector2<f32>,
45    pub color: Color,
46}
47
48impl Vertex {
49    fn new(pos: Vector2<f32>, tex_coord: Vector2<f32>) -> Vertex {
50        Vertex {
51            pos,
52            tex_coord,
53            color: Color::WHITE,
54        }
55    }
56}
57
58#[derive(Clone, Debug)]
59pub enum CommandTexture {
60    None,
61    Texture(TextureResource),
62    Font {
63        font: FontResource,
64        height: FontHeight,
65        page_index: usize,
66    },
67}
68
69/// A set of triangles that will be used for clipping.
70#[derive(Clone, Debug)]
71pub struct ClippingGeometry {
72    pub vertex_buffer: Vec<Vertex>,
73    pub triangle_buffer: Vec<TriangleDefinition>,
74    pub transform_stack: TransformStack,
75}
76
77impl Draw for ClippingGeometry {
78    #[inline(always)]
79    fn push_vertex_raw(&mut self, mut vertex: Vertex) {
80        vertex.pos = self
81            .transform_stack
82            .transform
83            .transform_point(&Point2::from(vertex.pos))
84            .coords;
85
86        self.vertex_buffer.push(vertex);
87    }
88
89    #[inline(always)]
90    fn push_triangle(&mut self, a: u32, b: u32, c: u32) {
91        self.triangle_buffer.push(TriangleDefinition([a, b, c]));
92    }
93
94    #[inline(always)]
95    fn last_vertex_index(&self) -> u32 {
96        self.vertex_buffer.len() as u32
97    }
98}
99
100impl ClippingGeometry {
101    pub fn is_contains_point(&self, pos: Vector2<f32>) -> bool {
102        for triangle in self.triangle_buffer.iter() {
103            if let Some((va, vb, vc)) = self.triangle_points(triangle) {
104                if math::is_point_inside_2d_triangle(pos, va.pos, vb.pos, vc.pos) {
105                    return true;
106                }
107            }
108        }
109
110        false
111    }
112
113    pub fn triangle_points(
114        &self,
115        triangle: &TriangleDefinition,
116    ) -> Option<(&Vertex, &Vertex, &Vertex)> {
117        let a = self.vertex_buffer.get(triangle[0] as usize)?;
118        let b = self.vertex_buffer.get(triangle[1] as usize)?;
119        let c = self.vertex_buffer.get(triangle[2] as usize)?;
120        Some((a, b, c))
121    }
122}
123
124#[derive(Clone, Debug)]
125pub struct Command {
126    /// Clipping bounds, should be used for scissor-test. Screen-space.
127    pub clip_bounds: Rect<f32>,
128    /// Total bounds of command's geometry. Screen-space.
129    pub bounds: Rect<f32>,
130    /// Brush defines visual appearance of rendered geometry.
131    pub brush: Brush,
132    pub texture: CommandTexture,
133    pub triangles: Range<usize>,
134    pub opacity: f32,
135    /// A set of triangles that defines clipping region.
136    pub clipping_geometry: Option<ClippingGeometry>,
137}
138
139pub trait Draw {
140    fn push_vertex(&mut self, pos: Vector2<f32>, tex_coord: Vector2<f32>) {
141        self.push_vertex_raw(Vertex::new(pos, tex_coord))
142    }
143
144    fn push_vertex_raw(&mut self, vertex: Vertex);
145
146    fn push_triangle(&mut self, a: u32, b: u32, c: u32);
147
148    fn last_vertex_index(&self) -> u32;
149
150    fn push_triangle_multicolor(&mut self, vertices: [(Vector2<f32>, Color); 3]) {
151        let index = self.last_vertex_index();
152        for &(pos, color) in &vertices {
153            self.push_vertex_raw(Vertex {
154                pos,
155                tex_coord: Vector2::new(0.0, 0.0),
156                color,
157            });
158        }
159
160        self.push_triangle(index, index + 1, index + 2);
161    }
162
163    fn push_triangle_filled(&mut self, vertices: [Vector2<f32>; 3]) {
164        let index = self.last_vertex_index();
165
166        for &pos in &vertices {
167            self.push_vertex(pos, Default::default());
168        }
169
170        self.push_triangle(index, index + 1, index + 2);
171    }
172
173    fn push_line(&mut self, a: Vector2<f32>, b: Vector2<f32>, thickness: f32) {
174        let index = self.last_vertex_index();
175        let perp = get_line_thickness_vector(a, b, thickness);
176        self.push_vertex(a - perp, Vector2::new(0.0, 0.0));
177        self.push_vertex(b - perp, Vector2::new(1.0, 0.0));
178        self.push_vertex(a + perp, Vector2::new(1.0, 1.0));
179        self.push_vertex(b + perp, Vector2::new(0.0, 1.0));
180
181        self.push_triangle(index, index + 1, index + 2);
182        self.push_triangle(index + 2, index + 1, index + 3);
183    }
184
185    fn push_rect(&mut self, rect: &Rect<f32>, thickness: f32) {
186        let offset = thickness * 0.5;
187
188        let left_top = Vector2::new(rect.x() + offset, rect.y() + thickness);
189        let right_top = Vector2::new(rect.x() + rect.w() - offset, rect.y() + thickness);
190        let right_bottom = Vector2::new(
191            rect.x() + rect.w() - offset,
192            rect.y() + rect.h() - thickness,
193        );
194        let left_bottom = Vector2::new(rect.x() + offset, rect.y() + rect.h() - thickness);
195        let left_top_off = Vector2::new(rect.x(), rect.y() + offset);
196        let right_top_off = Vector2::new(rect.x() + rect.w(), rect.y() + offset);
197        let right_bottom_off = Vector2::new(rect.x() + rect.w(), rect.y() + rect.h() - offset);
198        let left_bottom_off = Vector2::new(rect.x(), rect.y() + rect.h() - offset);
199
200        // Horizontal lines
201        self.push_line(left_top_off, right_top_off, thickness);
202        self.push_line(right_bottom_off, left_bottom_off, thickness);
203
204        // Vertical line
205        self.push_line(right_top, right_bottom, thickness);
206        self.push_line(left_bottom, left_top, thickness);
207    }
208
209    fn push_rect_vary(&mut self, rect: &Rect<f32>, thickness: Thickness) {
210        let left_top = Vector2::new(rect.x() + thickness.left * 0.5, rect.y() + thickness.top);
211        let right_top = Vector2::new(
212            rect.x() + rect.w() - thickness.right * 0.5,
213            rect.y() + thickness.top,
214        );
215        let right_bottom = Vector2::new(
216            rect.x() + rect.w() - thickness.right * 0.5,
217            rect.y() + rect.h() - thickness.bottom,
218        );
219        let left_bottom = Vector2::new(
220            rect.x() + thickness.left * 0.5,
221            rect.y() + rect.h() - thickness.bottom,
222        );
223        let left_top_off = Vector2::new(rect.x(), rect.y() + thickness.top * 0.5);
224        let right_top_off = Vector2::new(rect.x() + rect.w(), rect.y() + thickness.top * 0.5);
225        let right_bottom_off = Vector2::new(
226            rect.x() + rect.w(),
227            rect.y() + rect.h() - thickness.bottom * 0.5,
228        );
229        let left_bottom_off = Vector2::new(rect.x(), rect.y() + rect.h() - thickness.bottom * 0.5);
230
231        // Horizontal lines
232        self.push_line(left_top_off, right_top_off, thickness.top);
233        self.push_line(right_bottom_off, left_bottom_off, thickness.bottom);
234
235        // Vertical lines
236        self.push_line(right_top, right_bottom, thickness.right);
237        self.push_line(left_bottom, left_top, thickness.left);
238    }
239
240    fn push_rect_filled(&mut self, rect: &Rect<f32>, tex_coords: Option<&[Vector2<f32>; 4]>) {
241        let index = self.last_vertex_index();
242        self.push_vertex(
243            Vector2::new(rect.x(), rect.y()),
244            tex_coords.map_or(Vector2::new(0.0, 0.0), |t| t[0]),
245        );
246        self.push_vertex(
247            Vector2::new(rect.x() + rect.w(), rect.y()),
248            tex_coords.map_or(Vector2::new(1.0, 0.0), |t| t[1]),
249        );
250        self.push_vertex(
251            Vector2::new(rect.x() + rect.w(), rect.y() + rect.h()),
252            tex_coords.map_or(Vector2::new(1.0, 1.0), |t| t[2]),
253        );
254        self.push_vertex(
255            Vector2::new(rect.x(), rect.y() + rect.h()),
256            tex_coords.map_or(Vector2::new(0.0, 1.0), |t| t[3]),
257        );
258
259        self.push_triangle(index, index + 1, index + 2);
260        self.push_triangle(index, index + 2, index + 3);
261    }
262
263    fn push_rect_multicolor(&mut self, rect: &Rect<f32>, colors: [Color; 4]) {
264        let index = self.last_vertex_index();
265        self.push_vertex_raw(Vertex {
266            pos: rect.left_top_corner(),
267            tex_coord: Vector2::new(0.0, 0.0),
268            color: colors[0],
269        });
270        self.push_vertex_raw(Vertex {
271            pos: rect.right_top_corner(),
272            tex_coord: Vector2::new(1.0, 0.0),
273            color: colors[1],
274        });
275        self.push_vertex_raw(Vertex {
276            pos: rect.right_bottom_corner(),
277            tex_coord: Vector2::new(1.0, 1.0),
278            color: colors[2],
279        });
280        self.push_vertex_raw(Vertex {
281            pos: rect.left_bottom_corner(),
282            tex_coord: Vector2::new(0.0, 1.0),
283            color: colors[3],
284        });
285
286        self.push_triangle(index, index + 1, index + 2);
287        self.push_triangle(index, index + 2, index + 3);
288    }
289
290    fn push_circle_filled(
291        &mut self,
292        origin: Vector2<f32>,
293        radius: f32,
294        segments: usize,
295        color: Color,
296    ) {
297        if segments >= 3 {
298            let center_index = self.last_vertex_index();
299
300            self.push_vertex_raw(Vertex {
301                pos: origin,
302                tex_coord: Vector2::default(),
303                color,
304            });
305
306            let two_pi = 2.0 * std::f32::consts::PI;
307            let delta_angle = two_pi / (segments as f32);
308            let mut angle: f32 = 0.0;
309            for _ in 0..segments {
310                let x = origin.x + radius * angle.cos();
311                let y = origin.y + radius * angle.sin();
312                self.push_vertex_raw(Vertex {
313                    pos: Vector2::new(x, y),
314                    tex_coord: Vector2::default(),
315                    color,
316                });
317                angle += delta_angle;
318            }
319
320            let first_vertex = center_index + 1;
321            for segment in 0..segments {
322                self.push_triangle(
323                    center_index,
324                    first_vertex + segment as u32,
325                    first_vertex + (segment as u32 + 1) % segments as u32,
326                );
327            }
328        }
329    }
330
331    fn push_circle(
332        &mut self,
333        center: Vector2<f32>,
334        radius: f32,
335        subdivisions: usize,
336        thickness: f32,
337    ) {
338        let start_vertex = self.last_vertex_index();
339        let d = std::f32::consts::TAU / subdivisions as f32;
340
341        let half_thickness = thickness * 0.5;
342
343        let mut angle = 0.0;
344        while angle < std::f32::consts::TAU {
345            let r = Vector2::new(angle.cos(), angle.sin());
346
347            let p0 = center + r.scale(radius - half_thickness);
348            self.push_vertex(p0, Default::default());
349
350            let p1 = center + r.scale(radius + half_thickness);
351            self.push_vertex(p1, Default::default());
352
353            angle += d;
354        }
355        let last_vertex_index = self.last_vertex_index();
356
357        self.connect_as_line(start_vertex, last_vertex_index, true)
358    }
359
360    fn connect_as_line(&mut self, from: u32, to: u32, closed: bool) {
361        if closed {
362            let count = to - from;
363            for i in (0..count).step_by(2) {
364                let i0 = from + i % count;
365                let i1 = from + (i + 1) % count;
366                let i2 = from + (i + 2) % count;
367                let i3 = from + (i + 3) % count;
368                self.push_triangle(i0, i1, i2);
369                self.push_triangle(i1, i3, i2);
370            }
371        } else {
372            for i in (from..to.saturating_sub(4)).step_by(2) {
373                let i0 = i;
374                let i1 = i + 1;
375                let i2 = i + 2;
376                let i3 = i + 3;
377                self.push_triangle(i0, i1, i2);
378                self.push_triangle(i1, i3, i2);
379            }
380        }
381    }
382
383    fn push_arc(
384        &mut self,
385        center: Vector2<f32>,
386        radius: f32,
387        angles: Range<f32>,
388        subdivisions: usize,
389        thickness: f32,
390    ) {
391        let start_vertex = self.last_vertex_index();
392        self.push_arc_path_with_thickness(center, radius, angles, subdivisions, thickness);
393        let last_vertex_index = self.last_vertex_index();
394
395        self.connect_as_line(start_vertex, last_vertex_index, false)
396    }
397
398    fn push_arc_path_with_thickness(
399        &mut self,
400        center: Vector2<f32>,
401        radius: f32,
402        angles: Range<f32>,
403        subdivisions: usize,
404        thickness: f32,
405    ) {
406        let mut start_angle = math::wrap_angle(angles.start);
407        let mut end_angle = math::wrap_angle(angles.end);
408
409        if start_angle > end_angle {
410            std::mem::swap(&mut start_angle, &mut end_angle);
411        }
412
413        let d = (end_angle - start_angle) / subdivisions as f32;
414
415        let half_thickness = thickness * 0.5;
416
417        let mut angle = start_angle;
418        while angle <= end_angle {
419            let r = Vector2::new(angle.cos(), angle.sin());
420
421            let p0 = center + r.scale(radius - half_thickness);
422            self.push_vertex(p0, Default::default());
423
424            let p1 = center + r.scale(radius + half_thickness);
425            self.push_vertex(p1, Default::default());
426
427            angle += d;
428        }
429    }
430
431    fn push_arc_path(
432        &mut self,
433        center: Vector2<f32>,
434        radius: f32,
435        angles: Range<f32>,
436        subdivisions: usize,
437    ) {
438        let mut start_angle = math::wrap_angle(angles.start);
439        let mut end_angle = math::wrap_angle(angles.end);
440
441        if start_angle > end_angle {
442            std::mem::swap(&mut start_angle, &mut end_angle);
443        }
444
445        let d = (end_angle - start_angle) / subdivisions as f32;
446
447        let mut angle = start_angle;
448        while angle <= end_angle {
449            let p0 = center + Vector2::new(angle.cos() * radius, angle.sin() * radius);
450
451            self.push_vertex(p0, Default::default());
452
453            angle += d;
454        }
455    }
456
457    fn push_line_path(&mut self, a: Vector2<f32>, b: Vector2<f32>) {
458        self.push_vertex(a, Default::default());
459        self.push_vertex(b, Default::default());
460    }
461
462    fn push_line_path_with_thickness(&mut self, a: Vector2<f32>, b: Vector2<f32>, thickness: f32) {
463        let perp = get_line_thickness_vector(a, b, thickness);
464        self.push_vertex(a - perp, Vector2::new(0.0, 0.0));
465        self.push_vertex(a + perp, Vector2::new(1.0, 1.0));
466        self.push_vertex(b - perp, Vector2::new(1.0, 0.0));
467        self.push_vertex(b + perp, Vector2::new(0.0, 1.0));
468    }
469
470    fn push_rounded_rect_filled(
471        &mut self,
472        rect: &Rect<f32>,
473        mut corner_radius: f32,
474        corner_subdivisions: usize,
475    ) {
476        // Restrict corner radius in available rectangle.
477        let min_axis = rect.w().min(rect.h());
478        corner_radius = corner_radius.min(min_axis * 0.5);
479
480        let center_index = self.last_vertex_index();
481        self.push_vertex(rect.center(), Default::default());
482
483        self.push_line_path(
484            Vector2::new(rect.x(), rect.y() + rect.h() - corner_radius),
485            Vector2::new(rect.x(), rect.y() + corner_radius),
486        );
487
488        self.push_arc_path(
489            rect.position + Vector2::repeat(corner_radius),
490            corner_radius,
491            180.0f32.to_radians()..270.0f32.to_radians(),
492            corner_subdivisions,
493        );
494
495        self.push_line_path(
496            Vector2::new(rect.x() + corner_radius, rect.y()),
497            Vector2::new(rect.x() + rect.w() - corner_radius, rect.y()),
498        );
499
500        self.push_arc_path(
501            Vector2::new(
502                rect.position.x + rect.w() - corner_radius,
503                rect.position.y + corner_radius,
504            ),
505            corner_radius,
506            270.0f32.to_radians()..359.9999f32.to_radians(),
507            corner_subdivisions,
508        );
509
510        self.push_line_path(
511            Vector2::new(rect.x() + rect.w(), rect.y() + corner_radius),
512            Vector2::new(rect.x() + rect.w(), rect.y() + rect.h() - corner_radius),
513        );
514
515        self.push_arc_path(
516            Vector2::new(
517                rect.position.x + rect.w() - corner_radius,
518                rect.position.y + rect.h() - corner_radius,
519            ),
520            corner_radius,
521            0.0f32.to_radians()..90.0f32.to_radians(),
522            corner_subdivisions,
523        );
524
525        self.push_line_path(
526            Vector2::new(rect.x() + rect.w() - corner_radius, rect.y() + rect.h()),
527            Vector2::new(rect.x() + corner_radius, rect.y() + rect.h()),
528        );
529
530        self.push_arc_path(
531            Vector2::new(
532                rect.position.x + corner_radius,
533                rect.position.y + rect.h() - corner_radius,
534            ),
535            corner_radius,
536            90.0f32.to_radians()..180.0f32.to_radians(),
537            corner_subdivisions,
538        );
539
540        // Connect all vertices.
541        let first_index = center_index + 1;
542        let last_vertex_index = self.last_vertex_index().saturating_sub(1);
543        for i in first_index..last_vertex_index {
544            let next = i + 1;
545            self.push_triangle(i, next, center_index)
546        }
547
548        self.push_triangle(last_vertex_index, first_index, center_index);
549    }
550
551    fn push_rounded_rect(
552        &mut self,
553        rect: &Rect<f32>,
554        thickness: f32,
555        mut corner_radius: f32,
556        corner_subdivisions: usize,
557    ) {
558        // Restrict corner radius in available rectangle.
559        let min_axis = rect.w().min(rect.h());
560        corner_radius = corner_radius.min(min_axis * 0.5);
561
562        let half_thickness = thickness * 0.5;
563
564        let start_index = self.last_vertex_index();
565
566        self.push_line_path_with_thickness(
567            Vector2::new(
568                rect.x() + half_thickness,
569                rect.y() + rect.h() - thickness - corner_radius,
570            ),
571            Vector2::new(
572                rect.x() + half_thickness,
573                rect.y() + thickness + corner_radius,
574            ),
575            thickness,
576        );
577
578        self.push_arc_path_with_thickness(
579            rect.position + Vector2::repeat(corner_radius + half_thickness),
580            corner_radius,
581            180.0f32.to_radians()..270.0f32.to_radians(),
582            corner_subdivisions,
583            thickness,
584        );
585
586        self.push_line_path_with_thickness(
587            Vector2::new(
588                rect.x() + corner_radius + half_thickness,
589                rect.y() + half_thickness,
590            ),
591            Vector2::new(
592                rect.x() + rect.w() - corner_radius - half_thickness,
593                rect.y() + half_thickness,
594            ),
595            thickness,
596        );
597
598        self.push_arc_path_with_thickness(
599            Vector2::new(
600                rect.position.x + rect.w() - corner_radius - half_thickness,
601                rect.position.y + corner_radius + half_thickness,
602            ),
603            corner_radius,
604            270.0f32.to_radians()..359.9999f32.to_radians(),
605            corner_subdivisions,
606            thickness,
607        );
608
609        self.push_line_path_with_thickness(
610            Vector2::new(
611                rect.x() + rect.w() - half_thickness,
612                rect.y() + thickness + corner_radius,
613            ),
614            Vector2::new(
615                rect.x() + rect.w() - half_thickness,
616                rect.y() + rect.h() - thickness - corner_radius,
617            ),
618            thickness,
619        );
620
621        self.push_arc_path_with_thickness(
622            Vector2::new(
623                rect.position.x + rect.w() - corner_radius - half_thickness,
624                rect.position.y + rect.h() - corner_radius - half_thickness,
625            ),
626            corner_radius,
627            0.0f32.to_radians()..90.0f32.to_radians(),
628            corner_subdivisions,
629            thickness,
630        );
631
632        self.push_line_path_with_thickness(
633            Vector2::new(
634                rect.x() + rect.w() - corner_radius - half_thickness,
635                rect.y() + rect.h() - half_thickness,
636            ),
637            Vector2::new(
638                rect.x() + corner_radius + half_thickness,
639                rect.y() + rect.h() - half_thickness,
640            ),
641            thickness,
642        );
643
644        self.push_arc_path_with_thickness(
645            Vector2::new(
646                rect.position.x + corner_radius + half_thickness,
647                rect.position.y + rect.h() - corner_radius - half_thickness,
648            ),
649            corner_radius,
650            90.0f32.to_radians()..180.0f32.to_radians(),
651            corner_subdivisions,
652            thickness,
653        );
654
655        let last_vertex_index = self.last_vertex_index();
656        self.connect_as_line(start_index, last_vertex_index, true);
657    }
658
659    fn push_bezier(
660        &mut self,
661        p0: Vector2<f32>,
662        p1: Vector2<f32>,
663        p2: Vector2<f32>,
664        p3: Vector2<f32>,
665        subdivisions: usize,
666        thickness: f32,
667    ) {
668        fn cubic_bezier(
669            p0: Vector2<f32>,
670            p1: Vector2<f32>,
671            p2: Vector2<f32>,
672            p3: Vector2<f32>,
673            t: f32,
674        ) -> Vector2<f32> {
675            p0.scale((1.0 - t).powi(3))
676                + p1.scale(3.0 * t * (1.0 - t).powi(2))
677                + p2.scale(3.0 * t.powi(2) * (1.0 - t))
678                + p3.scale(t.powi(3))
679        }
680
681        let mut prev = cubic_bezier(p0, p1, p2, p3, 0.0);
682        for i in 0..subdivisions {
683            let t = (i + 1) as f32 / subdivisions as f32;
684            let next = cubic_bezier(p0, p1, p2, p3, t);
685            // TODO: This could give gaps between segments on sharp turns, it should be either patched
686            // or be continuous line instead of separate segments.
687            self.push_line(prev, next, thickness);
688            prev = next;
689        }
690    }
691
692    fn push_grid(&mut self, zoom: f32, cell_size: Vector2<f32>, grid_bounds: Rect<f32>) {
693        let mut local_left_bottom = grid_bounds.left_top_corner();
694        local_left_bottom.x = round_to_step(local_left_bottom.x, cell_size.x);
695        local_left_bottom.y = round_to_step(local_left_bottom.y, cell_size.y);
696
697        let mut local_right_top = grid_bounds.right_bottom_corner();
698        local_right_top.x = round_to_step(local_right_top.x, cell_size.x);
699        local_right_top.y = round_to_step(local_right_top.y, cell_size.y);
700
701        let w = (local_right_top.x - local_left_bottom.x).abs();
702        let h = (local_right_top.y - local_left_bottom.y).abs();
703
704        let nw = ((w / cell_size.x).ceil()) as usize;
705        let nh = ((h / cell_size.y).ceil()) as usize;
706
707        for ny in 0..=nh {
708            let k = ny as f32 / (nh) as f32;
709            let y = local_left_bottom.y + k * h;
710            self.push_line(
711                Vector2::new(local_left_bottom.x - cell_size.x, y),
712                Vector2::new(local_right_top.x + cell_size.x, y),
713                1.0 / zoom,
714            );
715        }
716
717        for nx in 0..=nw {
718            let k = nx as f32 / (nw) as f32;
719            let x = local_left_bottom.x + k * w;
720            self.push_line(
721                Vector2::new(x, local_left_bottom.y - cell_size.y),
722                Vector2::new(x, local_right_top.y + cell_size.y),
723                1.0 / zoom,
724            );
725        }
726    }
727}
728
729#[derive(Clone, Debug)]
730pub struct TransformStack {
731    transform: Matrix3<f32>,
732    stack: Vec<Matrix3<f32>>,
733}
734
735impl Default for TransformStack {
736    fn default() -> Self {
737        Self {
738            transform: Matrix3::identity(),
739            stack: vec![],
740        }
741    }
742}
743
744impl TransformStack {
745    #[inline]
746    pub fn push(&mut self, matrix: Matrix3<f32>) {
747        self.stack
748            .push(std::mem::replace(&mut self.transform, matrix));
749    }
750
751    /// Returns the transformation matrix that will be used to transform vertices of drawing context.
752    #[inline]
753    pub fn transform(&self) -> &Matrix3<f32> {
754        &self.transform
755    }
756
757    #[inline]
758    pub fn len(&self) -> usize {
759        self.stack.len()
760    }
761
762    pub fn is_empty(&self) -> bool {
763        self.stack.is_empty()
764    }
765
766    pub fn content(&self) -> Vec<Matrix3<f32>> {
767        self.stack.clone()
768    }
769
770    #[inline]
771    pub fn pop(&mut self) {
772        if let Some(top) = self.stack.pop() {
773            self.transform = top;
774        } else {
775            Log::err("TransformStack pop failure.")
776        }
777    }
778}
779
780#[derive(Debug, Clone)]
781pub struct DrawingContext {
782    vertex_buffer: Vec<Vertex>,
783    triangle_buffer: Vec<TriangleDefinition>,
784    command_buffer: Vec<Command>,
785    pub transform_stack: TransformStack,
786    opacity_stack: Vec<f32>,
787    triangles_to_commit: usize,
788    pub style: StyleResource,
789    /// Amount of time (in seconds) that passed from creation of the engine. Keep in mind, that
790    /// this value is **not** guaranteed to match real time. A user can change delta time with
791    /// which the engine "ticks" and this delta time affects elapsed time.
792    pub elapsed_time: f32,
793}
794
795fn get_line_thickness_vector(a: Vector2<f32>, b: Vector2<f32>, thickness: f32) -> Vector2<f32> {
796    if let Some(dir) = (b - a).try_normalize(f32::EPSILON) {
797        Vector2::new(dir.y, -dir.x).scale(thickness * 0.5)
798    } else {
799        Vector2::default()
800    }
801}
802
803impl Draw for DrawingContext {
804    #[inline(always)]
805    fn push_vertex_raw(&mut self, mut vertex: Vertex) {
806        vertex.pos = self
807            .transform_stack
808            .transform
809            .transform_point(&Point2::from(vertex.pos))
810            .coords;
811
812        self.vertex_buffer.push(vertex);
813    }
814
815    #[inline(always)]
816    fn push_triangle(&mut self, a: u32, b: u32, c: u32) {
817        self.triangle_buffer.push(TriangleDefinition([a, b, c]));
818        self.triangles_to_commit += 1;
819    }
820
821    #[inline(always)]
822    fn last_vertex_index(&self) -> u32 {
823        self.vertex_buffer.len() as u32
824    }
825}
826
827impl DrawingContext {
828    pub fn new(style: StyleResource) -> DrawingContext {
829        DrawingContext {
830            vertex_buffer: Vec::new(),
831            triangle_buffer: Vec::new(),
832            command_buffer: Vec::new(),
833            triangles_to_commit: 0,
834            opacity_stack: vec![1.0],
835            transform_stack: Default::default(),
836            style,
837            elapsed_time: 0.0,
838        }
839    }
840
841    #[inline]
842    pub fn clear(&mut self) {
843        self.vertex_buffer.clear();
844        self.triangle_buffer.clear();
845        self.command_buffer.clear();
846        self.opacity_stack.clear();
847        self.opacity_stack.push(1.0);
848        self.triangles_to_commit = 0;
849    }
850
851    #[inline]
852    pub fn get_vertices(&self) -> &[Vertex] {
853        self.vertex_buffer.as_slice()
854    }
855
856    #[inline]
857    pub fn get_triangles(&self) -> &[TriangleDefinition] {
858        self.triangle_buffer.as_slice()
859    }
860
861    #[inline]
862    pub fn get_commands(&self) -> &Vec<Command> {
863        &self.command_buffer
864    }
865
866    pub fn push_opacity(&mut self, opacity: f32) {
867        self.opacity_stack.push(opacity);
868    }
869
870    pub fn pop_opacity(&mut self) {
871        self.opacity_stack.pop().unwrap();
872    }
873
874    pub fn triangle_points(
875        &self,
876        triangle: &TriangleDefinition,
877    ) -> Option<(&Vertex, &Vertex, &Vertex)> {
878        let a = self.vertex_buffer.get(triangle[0] as usize)?;
879        let b = self.vertex_buffer.get(triangle[1] as usize)?;
880        let c = self.vertex_buffer.get(triangle[2] as usize)?;
881        Some((a, b, c))
882    }
883
884    pub fn is_command_contains_point(&self, command: &Command, pos: Vector2<f32>) -> bool {
885        for i in command.triangles.clone() {
886            if let Some(triangle) = self.triangle_buffer.get(i) {
887                if let Some((va, vb, vc)) = self.triangle_points(triangle) {
888                    if math::is_point_inside_2d_triangle(pos, va.pos, vb.pos, vc.pos) {
889                        return true;
890                    }
891                }
892            }
893        }
894
895        false
896    }
897
898    fn pending_range(&self) -> Range<usize> {
899        if self.triangle_buffer.is_empty() {
900            0..self.triangles_to_commit
901        } else {
902            (self.triangle_buffer.len() - self.triangles_to_commit)..self.triangle_buffer.len()
903        }
904    }
905
906    fn bounds_of(&self, range: Range<usize>) -> Rect<f32> {
907        let mut bounds = Rect::new(f32::MAX, f32::MAX, 0.0, 0.0);
908        for i in range {
909            for &k in self.triangle_buffer[i].as_ref() {
910                bounds.push(self.vertex_buffer[k as usize].pos);
911            }
912        }
913        bounds
914    }
915
916    pub fn commit(
917        &mut self,
918        clip_bounds: Rect<f32>,
919        brush: Brush,
920        texture: CommandTexture,
921        clipping_geometry: Option<ClippingGeometry>,
922    ) {
923        if self.triangles_to_commit > 0 {
924            let triangles = self.pending_range();
925            let bounds = self.bounds_of(triangles.clone());
926
927            let opacity = *self.opacity_stack.last().unwrap();
928            self.command_buffer.push(Command {
929                clip_bounds,
930                bounds,
931                brush,
932                texture,
933                triangles,
934                opacity,
935                clipping_geometry,
936            });
937            self.triangles_to_commit = 0;
938        }
939    }
940
941    pub fn draw_text(
942        &mut self,
943        clip_bounds: Rect<f32>,
944        position: Vector2<f32>,
945        formatted_text: &FormattedText,
946    ) {
947        let font = formatted_text.get_font();
948
949        #[inline(always)]
950        fn draw(
951            formatted_text: &FormattedText,
952            ctx: &mut DrawingContext,
953            clip_bounds: Rect<f32>,
954            position: Vector2<f32>,
955            dilation: f32,
956            offset: Vector2<f32>,
957            brush: Brush,
958            font: &FontResource,
959        ) {
960            let Some(mut current_page_index) = formatted_text
961                .get_glyphs()
962                .first()
963                .map(|g| g.atlas_page_index)
964            else {
965                return;
966            };
967
968            for element in formatted_text.get_glyphs() {
969                // If we've switched to another atlas page, commit the text and start a new batch.
970                if current_page_index != element.atlas_page_index {
971                    ctx.commit(
972                        clip_bounds,
973                        brush.clone(),
974                        CommandTexture::Font {
975                            font: font.clone(),
976                            page_index: current_page_index,
977                            // Use font size scaled by super sampling scaling to pick correct atlas
978                            // page.
979                            height: FontHeight::from(formatted_text.super_sampled_font_size()),
980                        },
981                        None,
982                    );
983                    current_page_index = element.atlas_page_index;
984                }
985
986                let bounds = element.bounds;
987
988                let final_bounds = Rect::new(
989                    position.x + bounds.x() + offset.x,
990                    position.y + bounds.y() + offset.y,
991                    bounds.w(),
992                    bounds.h(),
993                )
994                .inflate(dilation, dilation);
995
996                ctx.push_rect_filled(&final_bounds, Some(&element.tex_coords));
997            }
998
999            // Commit the rest.
1000            ctx.commit(
1001                clip_bounds,
1002                brush,
1003                CommandTexture::Font {
1004                    font: font.clone(),
1005                    page_index: current_page_index,
1006                    // Use font size scaled by super sampling scaling to pick correct atlas
1007                    // page.
1008                    height: FontHeight::from(formatted_text.super_sampled_font_size()),
1009                },
1010                None,
1011            );
1012        }
1013
1014        // Draw shadow, if any.
1015        if *formatted_text.shadow {
1016            draw(
1017                formatted_text,
1018                self,
1019                clip_bounds,
1020                position,
1021                *formatted_text.shadow_dilation,
1022                *formatted_text.shadow_offset,
1023                (*formatted_text.shadow_brush).clone(),
1024                &font,
1025            );
1026        }
1027
1028        draw(
1029            formatted_text,
1030            self,
1031            clip_bounds,
1032            position,
1033            0.0,
1034            Default::default(),
1035            formatted_text.brush(),
1036            &font,
1037        );
1038    }
1039}