rg3d_ui/
vector_image.rs

1use crate::{
2    core::{algebra::Vector2, color::Color, math::Vector2Ext, pool::Handle},
3    draw::{CommandTexture, Draw, DrawingContext},
4    message::UiMessage,
5    widget::{Widget, WidgetBuilder},
6    BuildContext, Control, UiNode, UserInterface,
7};
8use std::{
9    any::{Any, TypeId},
10    ops::{Deref, DerefMut},
11};
12
13#[derive(Clone, Debug)]
14pub enum Primitive {
15    Triangle {
16        points: [Vector2<f32>; 3],
17    },
18    Line {
19        begin: Vector2<f32>,
20        end: Vector2<f32>,
21        thickness: f32,
22    },
23    Circle {
24        center: Vector2<f32>,
25        radius: f32,
26        segments: usize,
27    },
28}
29
30fn line_thickness_vector(a: Vector2<f32>, b: Vector2<f32>, thickness: f32) -> Vector2<f32> {
31    if let Some(dir) = (b - a).try_normalize(f32::EPSILON) {
32        Vector2::new(dir.y, -dir.x).scale(thickness * 0.5)
33    } else {
34        Vector2::default()
35    }
36}
37
38impl Primitive {
39    pub fn bounds(&self) -> (Vector2<f32>, Vector2<f32>) {
40        match self {
41            Primitive::Triangle { points } => {
42                let min = points[0]
43                    .per_component_min(&points[1])
44                    .per_component_min(&points[2]);
45                let max = points[0]
46                    .per_component_max(&points[1])
47                    .per_component_max(&points[2]);
48                (min, max)
49            }
50            Primitive::Line {
51                begin,
52                end,
53                thickness,
54            } => {
55                let tv = line_thickness_vector(*begin, *end, *thickness);
56                let mut min = begin + tv;
57                let mut max = min;
58                for v in &[begin - tv, end + tv, end - tv] {
59                    min = min.per_component_min(v);
60                    max = max.per_component_max(v);
61                }
62                (min, max)
63            }
64            Primitive::Circle { radius, center, .. } => {
65                let radius = Vector2::new(*radius, *radius);
66                (center - radius, center + radius)
67            }
68        }
69    }
70}
71
72#[derive(Clone)]
73pub struct VectorImage {
74    widget: Widget,
75    primitives: Vec<Primitive>,
76}
77
78crate::define_widget_deref!(VectorImage);
79
80impl Control for VectorImage {
81    fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
82        if type_id == TypeId::of::<Self>() {
83            Some(self)
84        } else {
85            None
86        }
87    }
88
89    fn measure_override(&self, _ui: &UserInterface, _available_size: Vector2<f32>) -> Vector2<f32> {
90        if self.primitives.is_empty() {
91            Default::default()
92        } else {
93            let mut max = Vector2::new(-f32::MAX, -f32::MAX);
94            let mut min = Vector2::new(f32::MAX, f32::MAX);
95
96            for primitive in self.primitives.iter() {
97                let (pmin, pmax) = primitive.bounds();
98                min = min.per_component_min(&pmin);
99                max = max.per_component_max(&pmax);
100            }
101
102            max - min
103        }
104    }
105
106    fn draw(&self, drawing_context: &mut DrawingContext) {
107        let bounds = self.widget.screen_bounds();
108
109        for primitive in self.primitives.iter() {
110            match primitive {
111                Primitive::Triangle { points } => {
112                    let pts = [
113                        bounds.position + points[0],
114                        bounds.position + points[1],
115                        bounds.position + points[2],
116                    ];
117
118                    drawing_context.push_triangle_filled(pts);
119                }
120                Primitive::Line {
121                    begin,
122                    end,
123                    thickness,
124                } => {
125                    drawing_context.push_line(
126                        bounds.position + *begin,
127                        bounds.position + *end,
128                        *thickness,
129                    );
130                }
131                Primitive::Circle {
132                    center,
133                    radius,
134                    segments,
135                } => drawing_context.push_circle(
136                    bounds.position + *center,
137                    *radius,
138                    *segments,
139                    Color::WHITE,
140                ),
141            }
142        }
143        drawing_context.commit(
144            self.clip_bounds(),
145            self.widget.foreground(),
146            CommandTexture::None,
147            None,
148        );
149    }
150
151    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
152        self.widget.handle_routed_message(ui, message);
153    }
154}
155
156pub struct VectorImageBuilder {
157    widget_builder: WidgetBuilder,
158    primitives: Vec<Primitive>,
159}
160
161impl VectorImageBuilder {
162    pub fn new(widget_builder: WidgetBuilder) -> Self {
163        Self {
164            widget_builder,
165            primitives: Default::default(),
166        }
167    }
168
169    pub fn with_primitives(mut self, primitives: Vec<Primitive>) -> Self {
170        self.primitives = primitives;
171        self
172    }
173
174    pub fn build_node(self) -> UiNode {
175        let image = VectorImage {
176            widget: self.widget_builder.build(),
177            primitives: self.primitives,
178        };
179        UiNode::new(image)
180    }
181
182    pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
183        ctx.add_node(self.build_node())
184    }
185}