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