bevy_gizmos/arrows.rs
1//! Additional [`GizmoBuffer`] Functions -- Arrows
2//!
3//! Includes the implementation of [`GizmoBuffer::arrow`] and [`GizmoBuffer::arrow_2d`],
4//! and assorted support items.
5
6use crate::{gizmos::GizmoBuffer, prelude::GizmoConfigGroup};
7use bevy_color::{
8 palettes::basic::{BLUE, GREEN, RED},
9 Color,
10};
11use bevy_math::{Quat, Vec2, Vec3, Vec3Swizzles};
12use bevy_transform::TransformPoint;
13
14/// A builder returned by [`GizmoBuffer::arrow`] and [`GizmoBuffer::arrow_2d`]
15pub struct ArrowBuilder<'a, Config, Clear>
16where
17 Config: GizmoConfigGroup,
18 Clear: 'static + Send + Sync,
19{
20 gizmos: &'a mut GizmoBuffer<Config, Clear>,
21 start: Vec3,
22 end: Vec3,
23 color: Color,
24 double_ended: bool,
25 tip_length: f32,
26}
27
28impl<Config, Clear> ArrowBuilder<'_, Config, Clear>
29where
30 Config: GizmoConfigGroup,
31 Clear: 'static + Send + Sync,
32{
33 /// Change the length of the tips to be `length`.
34 /// The default tip length is [length of the arrow]/10.
35 ///
36 /// # Example
37 /// ```
38 /// # use bevy_gizmos::prelude::*;
39 /// # use bevy_math::prelude::*;
40 /// # use bevy_color::palettes::basic::GREEN;
41 /// fn system(mut gizmos: Gizmos) {
42 /// gizmos.arrow(Vec3::ZERO, Vec3::ONE, GREEN)
43 /// .with_tip_length(3.);
44 /// }
45 /// # bevy_ecs::system::assert_is_system(system);
46 /// ```
47 #[doc(alias = "arrow_head_length")]
48 pub fn with_tip_length(mut self, length: f32) -> Self {
49 self.tip_length = length;
50 self
51 }
52
53 /// Adds another tip to the arrow, appended in the start point.
54 /// the default is only one tip at the end point.
55 pub fn with_double_end(mut self) -> Self {
56 self.double_ended = true;
57 self
58 }
59}
60
61impl<Config, Clear> Drop for ArrowBuilder<'_, Config, Clear>
62where
63 Config: GizmoConfigGroup,
64 Clear: 'static + Send + Sync,
65{
66 /// Draws the arrow, by drawing lines with the stored [`GizmoBuffer`]
67 fn drop(&mut self) {
68 if !self.gizmos.enabled {
69 return;
70 }
71
72 // first, draw the body (main line) of the arrow
73 if self.double_ended {
74 let center = (self.start + self.end) / 2.0;
75 // drawing two lines for double ended arrows looks better for animated gizmos
76 self.gizmos.line(center, self.start, self.color);
77 self.gizmos.line(center, self.end, self.color);
78 } else {
79 self.gizmos.line(self.start, self.end, self.color);
80 }
81 // now the hard part is to draw the head in a sensible way
82 // put us in a coordinate system where the arrow is pointing towards +x and ends at the origin
83 let pointing_end = (self.end - self.start).normalize();
84 let rotation_end = Quat::from_rotation_arc(Vec3::X, pointing_end);
85 let tips = [
86 Vec3::new(-1., 1., 0.),
87 Vec3::new(-1., 0., 1.),
88 Vec3::new(-1., -1., 0.),
89 Vec3::new(-1., 0., -1.),
90 ];
91 // - extend the vectors so their length is `tip_length`
92 // - rotate the world so +x is facing in the same direction as the arrow
93 // - translate over to the tip of the arrow
94 let tips_end = tips.map(|v| rotation_end * (v.normalize() * self.tip_length) + self.end);
95 for v in tips_end {
96 // then actually draw the tips
97 self.gizmos.line(self.end, v, self.color);
98 }
99 if self.double_ended {
100 let pointing_start = (self.start - self.end).normalize();
101 let rotation_start = Quat::from_rotation_arc(Vec3::X, pointing_start);
102 let tips_start =
103 tips.map(|v| rotation_start * (v.normalize() * self.tip_length) + self.start);
104 for v in tips_start {
105 // draw the start points tips
106 self.gizmos.line(self.start, v, self.color);
107 }
108 }
109 }
110}
111
112impl<Config, Clear> GizmoBuffer<Config, Clear>
113where
114 Config: GizmoConfigGroup,
115 Clear: 'static + Send + Sync,
116{
117 /// Draw an arrow in 3D, from `start` to `end`. Has four tips for convenient viewing from any direction.
118 ///
119 /// # Example
120 /// ```
121 /// # use bevy_gizmos::prelude::*;
122 /// # use bevy_math::prelude::*;
123 /// # use bevy_color::palettes::basic::GREEN;
124 /// fn system(mut gizmos: Gizmos) {
125 /// gizmos.arrow(Vec3::ZERO, Vec3::ONE, GREEN);
126 /// }
127 /// # bevy_ecs::system::assert_is_system(system);
128 /// ```
129 pub fn arrow(
130 &mut self,
131 start: Vec3,
132 end: Vec3,
133 color: impl Into<Color>,
134 ) -> ArrowBuilder<'_, Config, Clear> {
135 let length = (end - start).length();
136 ArrowBuilder {
137 gizmos: self,
138 start,
139 end,
140 color: color.into(),
141 double_ended: false,
142 tip_length: length / 10.,
143 }
144 }
145
146 /// Draw an arrow in 2D (on the xy plane), from `start` to `end`.
147 ///
148 /// # Example
149 /// ```
150 /// # use bevy_gizmos::prelude::*;
151 /// # use bevy_math::prelude::*;
152 /// # use bevy_color::palettes::basic::GREEN;
153 /// fn system(mut gizmos: Gizmos) {
154 /// gizmos.arrow_2d(Vec2::ZERO, Vec2::X, GREEN);
155 /// }
156 /// # bevy_ecs::system::assert_is_system(system);
157 /// ```
158 pub fn arrow_2d(
159 &mut self,
160 start: Vec2,
161 end: Vec2,
162 color: impl Into<Color>,
163 ) -> ArrowBuilder<'_, Config, Clear> {
164 self.arrow(start.extend(0.), end.extend(0.), color)
165 }
166}
167
168impl<Config, Clear> GizmoBuffer<Config, Clear>
169where
170 Config: GizmoConfigGroup,
171 Clear: 'static + Send + Sync,
172{
173 /// Draw a set of axes local to the given transform (`transform`), with length scaled by a factor
174 /// of `base_length`.
175 ///
176 /// X is red
177 /// Y is green
178 /// Z is blue
179 ///
180 /// # Example
181 /// ```
182 /// # use bevy_gizmos::prelude::*;
183 /// # use bevy_ecs::prelude::*;
184 /// # use bevy_transform::components::Transform;
185 /// # #[derive(Component)]
186 /// # struct MyComponent;
187 /// fn draw_axes(
188 /// mut gizmos: Gizmos,
189 /// query: Query<&Transform, With<MyComponent>>,
190 /// ) {
191 /// for &transform in &query {
192 /// gizmos.axes(transform, 1.);
193 /// }
194 /// }
195 /// # bevy_ecs::system::assert_is_system(draw_axes);
196 /// ```
197 pub fn axes(&mut self, transform: impl TransformPoint, base_length: f32) {
198 let start = transform.transform_point(Vec3::ZERO);
199 let end_x = transform.transform_point(base_length * Vec3::X);
200 let end_y = transform.transform_point(base_length * Vec3::Y);
201 let end_z = transform.transform_point(base_length * Vec3::Z);
202
203 self.arrow(start, end_x, RED);
204 self.arrow(start, end_y, GREEN);
205 self.arrow(start, end_z, BLUE);
206 }
207
208 /// Draw a set of axes local to the given transform (`transform`), with length scaled by a factor
209 /// of `base_length`.
210 ///
211 /// # Example
212 /// ```
213 /// # use bevy_gizmos::prelude::*;
214 /// # use bevy_ecs::prelude::*;
215 /// # use bevy_transform::components::Transform;
216 /// # #[derive(Component)]
217 /// # struct AxesComponent;
218 /// fn draw_axes_2d(
219 /// mut gizmos: Gizmos,
220 /// query: Query<&Transform, With<AxesComponent>>,
221 /// ) {
222 /// for &transform in &query {
223 /// gizmos.axes_2d(transform, 1.);
224 /// }
225 /// }
226 /// # bevy_ecs::system::assert_is_system(draw_axes_2d);
227 /// ```
228 pub fn axes_2d(&mut self, transform: impl TransformPoint, base_length: f32) {
229 let start = transform.transform_point(Vec3::ZERO);
230 let end_x = transform.transform_point(base_length * Vec3::X);
231 let end_y = transform.transform_point(base_length * Vec3::Y);
232
233 self.arrow_2d(start.xy(), end_x.xy(), RED);
234 self.arrow_2d(start.xy(), end_y.xy(), GREEN);
235 }
236}