bevy_gizmos/gizmos.rs
1//! A module for the [`Gizmos`] [`SystemParam`].
2
3use core::{
4 iter,
5 marker::PhantomData,
6 mem,
7 ops::{Deref, DerefMut},
8};
9
10use bevy_color::{Color, LinearRgba};
11use bevy_ecs::{
12 component::Tick,
13 resource::Resource,
14 system::{
15 Deferred, ReadOnlySystemParam, Res, SystemBuffer, SystemMeta, SystemParam,
16 SystemParamValidationError,
17 },
18 world::{unsafe_world_cell::UnsafeWorldCell, World},
19};
20use bevy_math::{Isometry2d, Isometry3d, Vec2, Vec3};
21use bevy_reflect::{std_traits::ReflectDefault, Reflect};
22use bevy_transform::TransformPoint;
23use bevy_utils::default;
24
25use crate::{
26 config::{DefaultGizmoConfigGroup, GizmoConfigGroup, GizmoConfigStore},
27 prelude::GizmoConfig,
28};
29
30/// Storage of gizmo primitives.
31#[derive(Resource)]
32pub struct GizmoStorage<Config, Clear> {
33 pub(crate) list_positions: Vec<Vec3>,
34 pub(crate) list_colors: Vec<LinearRgba>,
35 pub(crate) strip_positions: Vec<Vec3>,
36 pub(crate) strip_colors: Vec<LinearRgba>,
37 marker: PhantomData<(Config, Clear)>,
38}
39
40impl<Config, Clear> Default for GizmoStorage<Config, Clear> {
41 fn default() -> Self {
42 Self {
43 list_positions: default(),
44 list_colors: default(),
45 strip_positions: default(),
46 strip_colors: default(),
47 marker: PhantomData,
48 }
49 }
50}
51
52impl<Config, Clear> GizmoStorage<Config, Clear>
53where
54 Config: GizmoConfigGroup,
55 Clear: 'static + Send + Sync,
56{
57 /// Combine the other gizmo storage with this one.
58 pub fn append_storage<OtherConfig, OtherClear>(
59 &mut self,
60 other: &GizmoStorage<OtherConfig, OtherClear>,
61 ) {
62 self.list_positions.extend(other.list_positions.iter());
63 self.list_colors.extend(other.list_colors.iter());
64 self.strip_positions.extend(other.strip_positions.iter());
65 self.strip_colors.extend(other.strip_colors.iter());
66 }
67
68 pub(crate) fn swap<OtherConfig, OtherClear>(
69 &mut self,
70 other: &mut GizmoStorage<OtherConfig, OtherClear>,
71 ) {
72 mem::swap(&mut self.list_positions, &mut other.list_positions);
73 mem::swap(&mut self.list_colors, &mut other.list_colors);
74 mem::swap(&mut self.strip_positions, &mut other.strip_positions);
75 mem::swap(&mut self.strip_colors, &mut other.strip_colors);
76 }
77
78 /// Clear this gizmo storage of any requested gizmos.
79 pub fn clear(&mut self) {
80 self.list_positions.clear();
81 self.list_colors.clear();
82 self.strip_positions.clear();
83 self.strip_colors.clear();
84 }
85}
86
87/// Swap buffer for a specific clearing context.
88///
89/// This is to stash/store the default/requested gizmos so another context can
90/// be substituted for that duration.
91pub struct Swap<Clear>(PhantomData<Clear>);
92
93/// A [`SystemParam`] for drawing gizmos.
94///
95/// They are drawn in immediate mode, which means they will be rendered only for
96/// the frames, or ticks when in [`FixedMain`](bevy_app::FixedMain), in which
97/// they are spawned.
98///
99/// A system in [`Main`](bevy_app::Main) will be cleared each rendering
100/// frame, while a system in [`FixedMain`](bevy_app::FixedMain) will be
101/// cleared each time the [`RunFixedMainLoop`](bevy_app::RunFixedMainLoop)
102/// schedule is run.
103///
104/// Gizmos should be spawned before the [`Last`](bevy_app::Last) schedule
105/// to ensure they are drawn.
106///
107/// To set up your own clearing context (useful for custom scheduling similar
108/// to [`FixedMain`](bevy_app::FixedMain)):
109///
110/// ```
111/// use bevy_gizmos::{prelude::*, *, gizmos::GizmoStorage};
112/// # use bevy_app::prelude::*;
113/// # use bevy_ecs::{schedule::ScheduleLabel, prelude::*};
114/// # #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
115/// # struct StartOfMyContext;
116/// # #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
117/// # struct EndOfMyContext;
118/// # #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
119/// # struct StartOfRun;
120/// # #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
121/// # struct EndOfRun;
122/// # struct MyContext;
123/// struct ClearContextSetup;
124/// impl Plugin for ClearContextSetup {
125/// fn build(&self, app: &mut App) {
126/// app.init_resource::<GizmoStorage<DefaultGizmoConfigGroup, MyContext>>()
127/// // Make sure this context starts/ends cleanly if inside another context. E.g. it
128/// // should start after the parent context starts and end after the parent context ends.
129/// .add_systems(StartOfMyContext, start_gizmo_context::<DefaultGizmoConfigGroup, MyContext>)
130/// // If not running multiple times, put this with [`start_gizmo_context`].
131/// .add_systems(StartOfRun, clear_gizmo_context::<DefaultGizmoConfigGroup, MyContext>)
132/// // If not running multiple times, put this with [`end_gizmo_context`].
133/// .add_systems(EndOfRun, collect_requested_gizmos::<DefaultGizmoConfigGroup, MyContext>)
134/// .add_systems(EndOfMyContext, end_gizmo_context::<DefaultGizmoConfigGroup, MyContext>)
135/// .add_systems(
136/// Last,
137/// propagate_gizmos::<DefaultGizmoConfigGroup, MyContext>.before(UpdateGizmoMeshes),
138/// );
139/// }
140/// }
141/// ```
142pub struct Gizmos<'w, 's, Config = DefaultGizmoConfigGroup, Clear = ()>
143where
144 Config: GizmoConfigGroup,
145 Clear: 'static + Send + Sync,
146{
147 buffer: Deferred<'s, GizmoBuffer<Config, Clear>>,
148 /// The currently used [`GizmoConfig`]
149 pub config: &'w GizmoConfig,
150 /// The currently used [`GizmoConfigGroup`]
151 pub config_ext: &'w Config,
152}
153
154impl<'w, 's, Config, Clear> Deref for Gizmos<'w, 's, Config, Clear>
155where
156 Config: GizmoConfigGroup,
157 Clear: 'static + Send + Sync,
158{
159 type Target = GizmoBuffer<Config, Clear>;
160
161 fn deref(&self) -> &Self::Target {
162 &self.buffer
163 }
164}
165
166impl<'w, 's, Config, Clear> DerefMut for Gizmos<'w, 's, Config, Clear>
167where
168 Config: GizmoConfigGroup,
169 Clear: 'static + Send + Sync,
170{
171 fn deref_mut(&mut self) -> &mut Self::Target {
172 &mut self.buffer
173 }
174}
175
176type GizmosState<Config, Clear> = (
177 Deferred<'static, GizmoBuffer<Config, Clear>>,
178 Res<'static, GizmoConfigStore>,
179);
180#[doc(hidden)]
181pub struct GizmosFetchState<Config, Clear>
182where
183 Config: GizmoConfigGroup,
184 Clear: 'static + Send + Sync,
185{
186 state: <GizmosState<Config, Clear> as SystemParam>::State,
187}
188
189#[expect(
190 unsafe_code,
191 reason = "We cannot implement SystemParam without using unsafe code."
192)]
193// SAFETY: All methods are delegated to existing `SystemParam` implementations
194unsafe impl<Config, Clear> SystemParam for Gizmos<'_, '_, Config, Clear>
195where
196 Config: GizmoConfigGroup,
197 Clear: 'static + Send + Sync,
198{
199 type State = GizmosFetchState<Config, Clear>;
200 type Item<'w, 's> = Gizmos<'w, 's, Config, Clear>;
201
202 fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State {
203 GizmosFetchState {
204 state: GizmosState::<Config, Clear>::init_state(world, system_meta),
205 }
206 }
207
208 unsafe fn new_archetype(
209 state: &mut Self::State,
210 archetype: &bevy_ecs::archetype::Archetype,
211 system_meta: &mut SystemMeta,
212 ) {
213 // SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`.
214 unsafe {
215 GizmosState::<Config, Clear>::new_archetype(&mut state.state, archetype, system_meta);
216 };
217 }
218
219 fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {
220 GizmosState::<Config, Clear>::apply(&mut state.state, system_meta, world);
221 }
222
223 #[inline]
224 unsafe fn validate_param(
225 state: &Self::State,
226 system_meta: &SystemMeta,
227 world: UnsafeWorldCell,
228 ) -> Result<(), SystemParamValidationError> {
229 // SAFETY: Delegated to existing `SystemParam` implementations.
230 unsafe { GizmosState::<Config, Clear>::validate_param(&state.state, system_meta, world) }
231 }
232
233 #[inline]
234 unsafe fn get_param<'w, 's>(
235 state: &'s mut Self::State,
236 system_meta: &SystemMeta,
237 world: UnsafeWorldCell<'w>,
238 change_tick: Tick,
239 ) -> Self::Item<'w, 's> {
240 // SAFETY: Delegated to existing `SystemParam` implementations.
241 let (mut f0, f1) = unsafe {
242 GizmosState::<Config, Clear>::get_param(
243 &mut state.state,
244 system_meta,
245 world,
246 change_tick,
247 )
248 };
249
250 // Accessing the GizmoConfigStore in every API call reduces performance significantly.
251 // Implementing SystemParam manually allows us to cache whether the config is currently enabled.
252 // Having this available allows for cheap early returns when gizmos are disabled.
253 let (config, config_ext) = f1.into_inner().config::<Config>();
254 f0.enabled = config.enabled;
255
256 Gizmos {
257 buffer: f0,
258 config,
259 config_ext,
260 }
261 }
262}
263
264#[expect(
265 unsafe_code,
266 reason = "We cannot implement ReadOnlySystemParam without using unsafe code."
267)]
268// Safety: Each field is `ReadOnlySystemParam`, and Gizmos SystemParam does not mutate world
269unsafe impl<'w, 's, Config, Clear> ReadOnlySystemParam for Gizmos<'w, 's, Config, Clear>
270where
271 Config: GizmoConfigGroup,
272 Clear: 'static + Send + Sync,
273 Deferred<'s, GizmoBuffer<Config, Clear>>: ReadOnlySystemParam,
274 Res<'w, GizmoConfigStore>: ReadOnlySystemParam,
275{
276}
277
278/// Buffer for gizmo vertex data.
279#[derive(Debug, Clone, Reflect)]
280#[reflect(Default)]
281pub struct GizmoBuffer<Config, Clear>
282where
283 Config: GizmoConfigGroup,
284 Clear: 'static + Send + Sync,
285{
286 pub(crate) enabled: bool,
287 pub(crate) list_positions: Vec<Vec3>,
288 pub(crate) list_colors: Vec<LinearRgba>,
289 pub(crate) strip_positions: Vec<Vec3>,
290 pub(crate) strip_colors: Vec<LinearRgba>,
291 #[reflect(ignore, clone)]
292 pub(crate) marker: PhantomData<(Config, Clear)>,
293}
294
295impl<Config, Clear> Default for GizmoBuffer<Config, Clear>
296where
297 Config: GizmoConfigGroup,
298 Clear: 'static + Send + Sync,
299{
300 fn default() -> Self {
301 GizmoBuffer {
302 enabled: true,
303 list_positions: Vec::new(),
304 list_colors: Vec::new(),
305 strip_positions: Vec::new(),
306 strip_colors: Vec::new(),
307 marker: PhantomData,
308 }
309 }
310}
311
312/// Read-only view into [`GizmoBuffer`] data.
313pub struct GizmoBufferView<'a> {
314 /// Vertex positions for line-list topology.
315 pub list_positions: &'a Vec<Vec3>,
316 /// Vertex colors for line-list topology.
317 pub list_colors: &'a Vec<LinearRgba>,
318 /// Vertex positions for line-strip topology.
319 pub strip_positions: &'a Vec<Vec3>,
320 /// Vertex colors for line-strip topology.
321 pub strip_colors: &'a Vec<LinearRgba>,
322}
323
324impl<Config, Clear> SystemBuffer for GizmoBuffer<Config, Clear>
325where
326 Config: GizmoConfigGroup,
327 Clear: 'static + Send + Sync,
328{
329 fn apply(&mut self, _system_meta: &SystemMeta, world: &mut World) {
330 let mut storage = world.resource_mut::<GizmoStorage<Config, Clear>>();
331 storage.list_positions.append(&mut self.list_positions);
332 storage.list_colors.append(&mut self.list_colors);
333 storage.strip_positions.append(&mut self.strip_positions);
334 storage.strip_colors.append(&mut self.strip_colors);
335 }
336}
337
338impl<Config, Clear> GizmoBuffer<Config, Clear>
339where
340 Config: GizmoConfigGroup,
341 Clear: 'static + Send + Sync,
342{
343 /// Clear all data.
344 pub fn clear(&mut self) {
345 self.list_positions.clear();
346 self.list_colors.clear();
347 self.strip_positions.clear();
348 self.strip_colors.clear();
349 }
350
351 /// Read-only view into the buffers data.
352 pub fn buffer(&self) -> GizmoBufferView {
353 let GizmoBuffer {
354 list_positions,
355 list_colors,
356 strip_positions,
357 strip_colors,
358 ..
359 } = self;
360 GizmoBufferView {
361 list_positions,
362 list_colors,
363 strip_positions,
364 strip_colors,
365 }
366 }
367 /// Draw a line in 3D from `start` to `end`.
368 ///
369 /// This should be called for each frame the line needs to be rendered.
370 ///
371 /// # Example
372 /// ```
373 /// # use bevy_gizmos::prelude::*;
374 /// # use bevy_math::prelude::*;
375 /// # use bevy_color::palettes::basic::GREEN;
376 /// fn system(mut gizmos: Gizmos) {
377 /// gizmos.line(Vec3::ZERO, Vec3::X, GREEN);
378 /// }
379 /// # bevy_ecs::system::assert_is_system(system);
380 /// ```
381 #[inline]
382 pub fn line(&mut self, start: Vec3, end: Vec3, color: impl Into<Color>) {
383 if !self.enabled {
384 return;
385 }
386 self.extend_list_positions([start, end]);
387 self.add_list_color(color, 2);
388 }
389
390 /// Draw a line in 3D with a color gradient from `start` to `end`.
391 ///
392 /// This should be called for each frame the line needs to be rendered.
393 ///
394 /// # Example
395 /// ```
396 /// # use bevy_gizmos::prelude::*;
397 /// # use bevy_math::prelude::*;
398 /// # use bevy_color::palettes::basic::{RED, GREEN};
399 /// fn system(mut gizmos: Gizmos) {
400 /// gizmos.line_gradient(Vec3::ZERO, Vec3::X, GREEN, RED);
401 /// }
402 /// # bevy_ecs::system::assert_is_system(system);
403 /// ```
404 #[inline]
405 pub fn line_gradient<C: Into<Color>>(
406 &mut self,
407 start: Vec3,
408 end: Vec3,
409 start_color: C,
410 end_color: C,
411 ) {
412 if !self.enabled {
413 return;
414 }
415 self.extend_list_positions([start, end]);
416 self.extend_list_colors([start_color, end_color]);
417 }
418
419 /// Draw a line in 3D from `start` to `start + vector`.
420 ///
421 /// This should be called for each frame the line needs to be rendered.
422 ///
423 /// # Example
424 /// ```
425 /// # use bevy_gizmos::prelude::*;
426 /// # use bevy_math::prelude::*;
427 /// # use bevy_color::palettes::basic::GREEN;
428 /// fn system(mut gizmos: Gizmos) {
429 /// gizmos.ray(Vec3::Y, Vec3::X, GREEN);
430 /// }
431 /// # bevy_ecs::system::assert_is_system(system);
432 /// ```
433 #[inline]
434 pub fn ray(&mut self, start: Vec3, vector: Vec3, color: impl Into<Color>) {
435 if !self.enabled {
436 return;
437 }
438 self.line(start, start + vector, color);
439 }
440
441 /// Draw a line in 3D with a color gradient from `start` to `start + vector`.
442 ///
443 /// This should be called for each frame the line needs to be rendered.
444 ///
445 /// # Example
446 /// ```
447 /// # use bevy_gizmos::prelude::*;
448 /// # use bevy_math::prelude::*;
449 /// # use bevy_color::palettes::basic::{RED, GREEN};
450 /// fn system(mut gizmos: Gizmos) {
451 /// gizmos.ray_gradient(Vec3::Y, Vec3::X, GREEN, RED);
452 /// }
453 /// # bevy_ecs::system::assert_is_system(system);
454 /// ```
455 #[inline]
456 pub fn ray_gradient<C: Into<Color>>(
457 &mut self,
458 start: Vec3,
459 vector: Vec3,
460 start_color: C,
461 end_color: C,
462 ) {
463 if !self.enabled {
464 return;
465 }
466 self.line_gradient(start, start + vector, start_color, end_color);
467 }
468
469 /// Draw a line in 3D made of straight segments between the points.
470 ///
471 /// This should be called for each frame the line needs to be rendered.
472 ///
473 /// # Example
474 /// ```
475 /// # use bevy_gizmos::prelude::*;
476 /// # use bevy_math::prelude::*;
477 /// # use bevy_color::palettes::basic::GREEN;
478 /// fn system(mut gizmos: Gizmos) {
479 /// gizmos.linestrip([Vec3::ZERO, Vec3::X, Vec3::Y], GREEN);
480 /// }
481 /// # bevy_ecs::system::assert_is_system(system);
482 /// ```
483 #[inline]
484 pub fn linestrip(
485 &mut self,
486 positions: impl IntoIterator<Item = Vec3>,
487 color: impl Into<Color>,
488 ) {
489 if !self.enabled {
490 return;
491 }
492 self.extend_strip_positions(positions);
493 let len = self.strip_positions.len();
494 let linear_color = LinearRgba::from(color.into());
495 self.strip_colors.resize(len - 1, linear_color);
496 self.strip_colors.push(LinearRgba::NAN);
497 }
498
499 /// Draw a line in 3D made of straight segments between the points, with a color gradient.
500 ///
501 /// This should be called for each frame the lines need to be rendered.
502 ///
503 /// # Example
504 /// ```
505 /// # use bevy_gizmos::prelude::*;
506 /// # use bevy_math::prelude::*;
507 /// # use bevy_color::palettes::basic::{BLUE, GREEN, RED};
508 /// fn system(mut gizmos: Gizmos) {
509 /// gizmos.linestrip_gradient([
510 /// (Vec3::ZERO, GREEN),
511 /// (Vec3::X, RED),
512 /// (Vec3::Y, BLUE)
513 /// ]);
514 /// }
515 /// # bevy_ecs::system::assert_is_system(system);
516 /// ```
517 #[inline]
518 pub fn linestrip_gradient<C: Into<Color>>(
519 &mut self,
520 points: impl IntoIterator<Item = (Vec3, C)>,
521 ) {
522 if !self.enabled {
523 return;
524 }
525 let points = points.into_iter();
526
527 let GizmoBuffer {
528 strip_positions,
529 strip_colors,
530 ..
531 } = self;
532
533 let (min, _) = points.size_hint();
534 strip_positions.reserve(min);
535 strip_colors.reserve(min);
536
537 for (position, color) in points {
538 strip_positions.push(position);
539 strip_colors.push(LinearRgba::from(color.into()));
540 }
541
542 strip_positions.push(Vec3::NAN);
543 strip_colors.push(LinearRgba::NAN);
544 }
545
546 /// Draw a wireframe rectangle in 3D with the given `isometry` applied.
547 ///
548 /// If `isometry == Isometry3d::IDENTITY` then
549 ///
550 /// - the center is at `Vec3::ZERO`
551 /// - the sizes are aligned with the `Vec3::X` and `Vec3::Y` axes.
552 ///
553 /// This should be called for each frame the rectangle needs to be rendered.
554 ///
555 /// # Example
556 /// ```
557 /// # use bevy_gizmos::prelude::*;
558 /// # use bevy_math::prelude::*;
559 /// # use bevy_color::palettes::basic::GREEN;
560 /// fn system(mut gizmos: Gizmos) {
561 /// gizmos.rect(Isometry3d::IDENTITY, Vec2::ONE, GREEN);
562 /// }
563 /// # bevy_ecs::system::assert_is_system(system);
564 /// ```
565 #[inline]
566 pub fn rect(&mut self, isometry: impl Into<Isometry3d>, size: Vec2, color: impl Into<Color>) {
567 if !self.enabled {
568 return;
569 }
570 let isometry = isometry.into();
571 let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2.extend(0.));
572 self.linestrip([tl, tr, br, bl, tl], color);
573 }
574
575 /// Draw a wireframe cube in 3D.
576 ///
577 /// This should be called for each frame the cube needs to be rendered.
578 ///
579 /// # Example
580 /// ```
581 /// # use bevy_gizmos::prelude::*;
582 /// # use bevy_transform::prelude::*;
583 /// # use bevy_color::palettes::basic::GREEN;
584 /// fn system(mut gizmos: Gizmos) {
585 /// gizmos.cuboid(Transform::IDENTITY, GREEN);
586 /// }
587 /// # bevy_ecs::system::assert_is_system(system);
588 /// ```
589 #[inline]
590 pub fn cuboid(&mut self, transform: impl TransformPoint, color: impl Into<Color>) {
591 let polymorphic_color: Color = color.into();
592 if !self.enabled {
593 return;
594 }
595 let rect = rect_inner(Vec2::ONE);
596 // Front
597 let [tlf, trf, brf, blf] = rect.map(|vec2| transform.transform_point(vec2.extend(0.5)));
598 // Back
599 let [tlb, trb, brb, blb] = rect.map(|vec2| transform.transform_point(vec2.extend(-0.5)));
600
601 let strip_positions = [
602 tlf, trf, brf, blf, tlf, // Front
603 tlb, trb, brb, blb, tlb, // Back
604 ];
605 self.linestrip(strip_positions, polymorphic_color);
606
607 let list_positions = [
608 trf, trb, brf, brb, blf, blb, // Front to back
609 ];
610 self.extend_list_positions(list_positions);
611
612 self.add_list_color(polymorphic_color, 6);
613 }
614
615 /// Draw a line in 2D from `start` to `end`.
616 ///
617 /// This should be called for each frame the line needs to be rendered.
618 ///
619 /// # Example
620 /// ```
621 /// # use bevy_gizmos::prelude::*;
622 /// # use bevy_math::prelude::*;
623 /// # use bevy_color::palettes::basic::GREEN;
624 /// fn system(mut gizmos: Gizmos) {
625 /// gizmos.line_2d(Vec2::ZERO, Vec2::X, GREEN);
626 /// }
627 /// # bevy_ecs::system::assert_is_system(system);
628 /// ```
629 #[inline]
630 pub fn line_2d(&mut self, start: Vec2, end: Vec2, color: impl Into<Color>) {
631 if !self.enabled {
632 return;
633 }
634 self.line(start.extend(0.), end.extend(0.), color);
635 }
636
637 /// Draw a line in 2D with a color gradient from `start` to `end`.
638 ///
639 /// This should be called for each frame the line needs to be rendered.
640 ///
641 /// # Example
642 /// ```
643 /// # use bevy_gizmos::prelude::*;
644 /// # use bevy_math::prelude::*;
645 /// # use bevy_color::palettes::basic::{RED, GREEN};
646 /// fn system(mut gizmos: Gizmos) {
647 /// gizmos.line_gradient_2d(Vec2::ZERO, Vec2::X, GREEN, RED);
648 /// }
649 /// # bevy_ecs::system::assert_is_system(system);
650 /// ```
651 #[inline]
652 pub fn line_gradient_2d<C: Into<Color>>(
653 &mut self,
654 start: Vec2,
655 end: Vec2,
656 start_color: C,
657 end_color: C,
658 ) {
659 if !self.enabled {
660 return;
661 }
662 self.line_gradient(start.extend(0.), end.extend(0.), start_color, end_color);
663 }
664
665 /// Draw a line in 2D made of straight segments between the points.
666 ///
667 /// This should be called for each frame the line needs to be rendered.
668 ///
669 /// # Example
670 /// ```
671 /// # use bevy_gizmos::prelude::*;
672 /// # use bevy_math::prelude::*;
673 /// # use bevy_color::palettes::basic::GREEN;
674 /// fn system(mut gizmos: Gizmos) {
675 /// gizmos.linestrip_2d([Vec2::ZERO, Vec2::X, Vec2::Y], GREEN);
676 /// }
677 /// # bevy_ecs::system::assert_is_system(system);
678 /// ```
679 #[inline]
680 pub fn linestrip_2d(
681 &mut self,
682 positions: impl IntoIterator<Item = Vec2>,
683 color: impl Into<Color>,
684 ) {
685 if !self.enabled {
686 return;
687 }
688 self.linestrip(positions.into_iter().map(|vec2| vec2.extend(0.)), color);
689 }
690
691 /// Draw a line in 2D made of straight segments between the points, with a color gradient.
692 ///
693 /// This should be called for each frame the line needs to be rendered.
694 ///
695 /// # Example
696 /// ```
697 /// # use bevy_gizmos::prelude::*;
698 /// # use bevy_math::prelude::*;
699 /// # use bevy_color::palettes::basic::{RED, GREEN, BLUE};
700 /// fn system(mut gizmos: Gizmos) {
701 /// gizmos.linestrip_gradient_2d([
702 /// (Vec2::ZERO, GREEN),
703 /// (Vec2::X, RED),
704 /// (Vec2::Y, BLUE)
705 /// ]);
706 /// }
707 /// # bevy_ecs::system::assert_is_system(system);
708 /// ```
709 #[inline]
710 pub fn linestrip_gradient_2d<C: Into<Color>>(
711 &mut self,
712 positions: impl IntoIterator<Item = (Vec2, C)>,
713 ) {
714 if !self.enabled {
715 return;
716 }
717 self.linestrip_gradient(
718 positions
719 .into_iter()
720 .map(|(vec2, color)| (vec2.extend(0.), color)),
721 );
722 }
723
724 /// Draw a line in 2D from `start` to `start + vector`.
725 ///
726 /// This should be called for each frame the line needs to be rendered.
727 ///
728 /// # Example
729 /// ```
730 /// # use bevy_gizmos::prelude::*;
731 /// # use bevy_math::prelude::*;
732 /// # use bevy_color::palettes::basic::GREEN;
733 /// fn system(mut gizmos: Gizmos) {
734 /// gizmos.ray_2d(Vec2::Y, Vec2::X, GREEN);
735 /// }
736 /// # bevy_ecs::system::assert_is_system(system);
737 /// ```
738 #[inline]
739 pub fn ray_2d(&mut self, start: Vec2, vector: Vec2, color: impl Into<Color>) {
740 if !self.enabled {
741 return;
742 }
743 self.line_2d(start, start + vector, color);
744 }
745
746 /// Draw a line in 2D with a color gradient from `start` to `start + vector`.
747 ///
748 /// This should be called for each frame the line needs to be rendered.
749 ///
750 /// # Example
751 /// ```
752 /// # use bevy_gizmos::prelude::*;
753 /// # use bevy_math::prelude::*;
754 /// # use bevy_color::palettes::basic::{RED, GREEN};
755 /// fn system(mut gizmos: Gizmos) {
756 /// gizmos.line_gradient(Vec3::Y, Vec3::X, GREEN, RED);
757 /// }
758 /// # bevy_ecs::system::assert_is_system(system);
759 /// ```
760 #[inline]
761 pub fn ray_gradient_2d<C: Into<Color>>(
762 &mut self,
763 start: Vec2,
764 vector: Vec2,
765 start_color: C,
766 end_color: C,
767 ) {
768 if !self.enabled {
769 return;
770 }
771 self.line_gradient_2d(start, start + vector, start_color, end_color);
772 }
773
774 /// Draw a wireframe rectangle in 2D with the given `isometry` applied.
775 ///
776 /// If `isometry == Isometry2d::IDENTITY` then
777 ///
778 /// - the center is at `Vec2::ZERO`
779 /// - the sizes are aligned with the `Vec2::X` and `Vec2::Y` axes.
780 ///
781 /// This should be called for each frame the rectangle needs to be rendered.
782 ///
783 /// # Example
784 /// ```
785 /// # use bevy_gizmos::prelude::*;
786 /// # use bevy_math::prelude::*;
787 /// # use bevy_color::palettes::basic::GREEN;
788 /// fn system(mut gizmos: Gizmos) {
789 /// gizmos.rect_2d(Isometry2d::IDENTITY, Vec2::ONE, GREEN);
790 /// }
791 /// # bevy_ecs::system::assert_is_system(system);
792 /// ```
793 #[inline]
794 pub fn rect_2d(
795 &mut self,
796 isometry: impl Into<Isometry2d>,
797 size: Vec2,
798 color: impl Into<Color>,
799 ) {
800 if !self.enabled {
801 return;
802 }
803 let isometry = isometry.into();
804 let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2);
805 self.linestrip_2d([tl, tr, br, bl, tl], color);
806 }
807
808 #[inline]
809 fn extend_list_positions(&mut self, positions: impl IntoIterator<Item = Vec3>) {
810 self.list_positions.extend(positions);
811 }
812
813 #[inline]
814 fn extend_list_colors(&mut self, colors: impl IntoIterator<Item = impl Into<Color>>) {
815 self.list_colors.extend(
816 colors
817 .into_iter()
818 .map(|color| LinearRgba::from(color.into())),
819 );
820 }
821
822 #[inline]
823 fn add_list_color(&mut self, color: impl Into<Color>, count: usize) {
824 let polymorphic_color: Color = color.into();
825 let linear_color = LinearRgba::from(polymorphic_color);
826
827 self.list_colors.extend(iter::repeat_n(linear_color, count));
828 }
829
830 #[inline]
831 fn extend_strip_positions(&mut self, positions: impl IntoIterator<Item = Vec3>) {
832 self.strip_positions.extend(positions);
833 self.strip_positions.push(Vec3::NAN);
834 }
835}
836
837fn rect_inner(size: Vec2) -> [Vec2; 4] {
838 let half_size = size / 2.;
839 let tl = Vec2::new(-half_size.x, half_size.y);
840 let tr = Vec2::new(half_size.x, half_size.y);
841 let bl = Vec2::new(-half_size.x, -half_size.y);
842 let br = Vec2::new(half_size.x, -half_size.y);
843 [tl, tr, br, bl]
844}