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}