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}