Skip to main content

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    change_detection::Tick,
13    resource::Resource,
14    system::{
15        Deferred, ReadOnlySystemParam, Res, SystemAccess, SystemBuffer, SystemMeta, SystemParam,
16        SystemParamValidationError,
17    },
18    world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
19};
20use bevy_math::{Isometry2d, Isometry3d, Vec2, Vec3};
21use bevy_reflect::{std_traits::ReflectDefault, Reflect};
22use bevy_shape::Aabb3d;
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        system_access: &mut SystemAccess,
213        world: &mut World,
214    ) {
215        GizmosState::<Config, Clear>::init_access(&state.state, system_meta, system_access, world);
216    }
217
218    fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {
219        GizmosState::<Config, Clear>::apply(&mut state.state, system_meta, world);
220    }
221
222    fn queue(state: &mut Self::State, system_meta: &SystemMeta, world: DeferredWorld) {
223        GizmosState::<Config, Clear>::queue(&mut state.state, system_meta, world);
224    }
225
226    #[inline]
227    unsafe fn get_param<'w, 's>(
228        state: &'s mut Self::State,
229        system_meta: &SystemMeta,
230        world: UnsafeWorldCell<'w>,
231        change_tick: Tick,
232    ) -> Result<Self::Item<'w, 's>, SystemParamValidationError> {
233        // SAFETY: Delegated to existing `SystemParam` implementations.
234        let (mut f0, f1) = unsafe {
235            GizmosState::<Config, Clear>::get_param(
236                &mut state.state,
237                system_meta,
238                world,
239                change_tick,
240            )?
241        };
242
243        // Accessing the GizmoConfigStore in every API call reduces performance significantly.
244        // Implementing SystemParam manually allows us to cache whether the config is currently enabled.
245        // Having this available allows for cheap early returns when gizmos are disabled.
246        //
247        // We use `get_config` instead of `config` to accommodate `Option<Gizmos>`:
248        // the user may decide not to initialize a gizmo group, so its config will not exist.
249        let (config, config_ext) = f1.into_inner().get_config::<Config>().ok_or_else(|| {
250            SystemParamValidationError::invalid::<Self>(
251                format!("Requested config {} does not exist in `GizmoConfigStore`! Did you forget to add it using `app.init_gizmo_group<T>()`?", 
252                Config::type_path()))
253        })?;
254        f0.enabled = config.enabled;
255
256        Ok(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    /// The positions of line segment endpoints.
288    pub list_positions: Vec<Vec3>,
289    /// The colors of line segment endpoints.
290    pub list_colors: Vec<LinearRgba>,
291    /// The positions of line strip vertices.
292    pub strip_positions: Vec<Vec3>,
293    /// The colors of line strip vertices.
294    pub strip_colors: Vec<LinearRgba>,
295    #[reflect(ignore, clone)]
296    pub(crate) marker: PhantomData<(Config, Clear)>,
297}
298
299impl<Config, Clear> Default for GizmoBuffer<Config, Clear>
300where
301    Config: GizmoConfigGroup,
302    Clear: 'static + Send + Sync,
303{
304    fn default() -> Self {
305        GizmoBuffer::new()
306    }
307}
308
309impl<Config, Clear> GizmoBuffer<Config, Clear>
310where
311    Config: GizmoConfigGroup,
312    Clear: 'static + Send + Sync,
313{
314    /// Constructs an empty `GizmoBuffer`.
315    pub const fn new() -> Self {
316        GizmoBuffer {
317            enabled: true,
318            list_positions: Vec::new(),
319            list_colors: Vec::new(),
320            strip_positions: Vec::new(),
321            strip_colors: Vec::new(),
322            marker: PhantomData,
323        }
324    }
325}
326
327/// Read-only view into [`GizmoBuffer`] data.
328pub struct GizmoBufferView<'a> {
329    /// Vertex positions for line-list topology.
330    pub list_positions: &'a Vec<Vec3>,
331    /// Vertex colors for line-list topology.
332    pub list_colors: &'a Vec<LinearRgba>,
333    /// Vertex positions for line-strip topology.
334    pub strip_positions: &'a Vec<Vec3>,
335    /// Vertex colors for line-strip topology.
336    pub strip_colors: &'a Vec<LinearRgba>,
337}
338
339impl<Config, Clear> SystemBuffer for GizmoBuffer<Config, Clear>
340where
341    Config: GizmoConfigGroup,
342    Clear: 'static + Send + Sync,
343{
344    fn queue(&mut self, _system_meta: &SystemMeta, mut world: DeferredWorld) {
345        if let Some(mut storage) = world.get_resource_mut::<GizmoStorage<Config, Clear>>() {
346            storage.list_positions.append(&mut self.list_positions);
347            storage.list_colors.append(&mut self.list_colors);
348            storage.strip_positions.append(&mut self.strip_positions);
349            storage.strip_colors.append(&mut self.strip_colors);
350        } else {
351            // Prevent the buffer from growing indefinitely if GizmoStorage
352            // for the config group has not been initialized
353            self.list_positions.clear();
354            self.list_colors.clear();
355            self.strip_positions.clear();
356            self.strip_colors.clear();
357        }
358    }
359}
360
361impl<Config, Clear> GizmoBuffer<Config, Clear>
362where
363    Config: GizmoConfigGroup,
364    Clear: 'static + Send + Sync,
365{
366    /// Clear all data.
367    pub fn clear(&mut self) {
368        self.list_positions.clear();
369        self.list_colors.clear();
370        self.strip_positions.clear();
371        self.strip_colors.clear();
372    }
373
374    /// Read-only view into the buffers data.
375    pub fn buffer(&self) -> GizmoBufferView<'_> {
376        let GizmoBuffer {
377            list_positions,
378            list_colors,
379            strip_positions,
380            strip_colors,
381            ..
382        } = self;
383        GizmoBufferView {
384            list_positions,
385            list_colors,
386            strip_positions,
387            strip_colors,
388        }
389    }
390    /// Draw a line in 3D from `start` to `end`.
391    ///
392    /// # Example
393    /// ```
394    /// # use bevy_gizmos::prelude::*;
395    /// # use bevy_math::prelude::*;
396    /// # use bevy_color::palettes::basic::GREEN;
397    /// fn system(mut gizmos: Gizmos) {
398    ///     gizmos.line(Vec3::ZERO, Vec3::X, GREEN);
399    /// }
400    /// # bevy_ecs::system::assert_is_system(system);
401    /// ```
402    #[inline]
403    pub fn line(&mut self, start: Vec3, end: Vec3, color: impl Into<Color>) {
404        if !self.enabled {
405            return;
406        }
407        self.extend_list_positions([start, end]);
408        self.add_list_color(color, 2);
409    }
410
411    /// Draw a line in 3D with a color gradient from `start` to `end`.
412    ///
413    /// # Example
414    /// ```
415    /// # use bevy_gizmos::prelude::*;
416    /// # use bevy_math::prelude::*;
417    /// # use bevy_color::palettes::basic::{RED, GREEN};
418    /// fn system(mut gizmos: Gizmos) {
419    ///     gizmos.line_gradient(Vec3::ZERO, Vec3::X, GREEN, RED);
420    /// }
421    /// # bevy_ecs::system::assert_is_system(system);
422    /// ```
423    #[inline]
424    pub fn line_gradient<C: Into<Color>>(
425        &mut self,
426        start: Vec3,
427        end: Vec3,
428        start_color: C,
429        end_color: C,
430    ) {
431        if !self.enabled {
432            return;
433        }
434        self.extend_list_positions([start, end]);
435        self.extend_list_colors([start_color, end_color]);
436    }
437
438    /// Draw a line in 3D from `start` to `start + vector`.
439    ///
440    /// # Example
441    /// ```
442    /// # use bevy_gizmos::prelude::*;
443    /// # use bevy_math::prelude::*;
444    /// # use bevy_color::palettes::basic::GREEN;
445    /// fn system(mut gizmos: Gizmos) {
446    ///     gizmos.ray(Vec3::Y, Vec3::X, GREEN);
447    /// }
448    /// # bevy_ecs::system::assert_is_system(system);
449    /// ```
450    #[inline]
451    pub fn ray(&mut self, start: Vec3, vector: Vec3, color: impl Into<Color>) {
452        if !self.enabled {
453            return;
454        }
455        self.line(start, start + vector, color);
456    }
457
458    /// Draw a line in 3D with a color gradient from `start` to `start + vector`.
459    ///
460    /// # Example
461    /// ```
462    /// # use bevy_gizmos::prelude::*;
463    /// # use bevy_math::prelude::*;
464    /// # use bevy_color::palettes::basic::{RED, GREEN};
465    /// fn system(mut gizmos: Gizmos) {
466    ///     gizmos.ray_gradient(Vec3::Y, Vec3::X, GREEN, RED);
467    /// }
468    /// # bevy_ecs::system::assert_is_system(system);
469    /// ```
470    #[inline]
471    pub fn ray_gradient<C: Into<Color>>(
472        &mut self,
473        start: Vec3,
474        vector: Vec3,
475        start_color: C,
476        end_color: C,
477    ) {
478        if !self.enabled {
479            return;
480        }
481        self.line_gradient(start, start + vector, start_color, end_color);
482    }
483
484    /// Draw a line in 3D made of straight segments between the points.
485    ///
486    /// # Example
487    /// ```
488    /// # use bevy_gizmos::prelude::*;
489    /// # use bevy_math::prelude::*;
490    /// # use bevy_color::palettes::basic::GREEN;
491    /// fn system(mut gizmos: Gizmos) {
492    ///     gizmos.linestrip([Vec3::ZERO, Vec3::X, Vec3::Y], GREEN);
493    /// }
494    /// # bevy_ecs::system::assert_is_system(system);
495    /// ```
496    #[inline]
497    pub fn linestrip(
498        &mut self,
499        positions: impl IntoIterator<Item = Vec3>,
500        color: impl Into<Color>,
501    ) {
502        if !self.enabled {
503            return;
504        }
505        self.extend_strip_positions(positions);
506        let len = self.strip_positions.len();
507        let linear_color = LinearRgba::from(color.into());
508        self.strip_colors.resize(len - 1, linear_color);
509        self.strip_colors.push(LinearRgba::NAN);
510    }
511
512    /// Draw a line in 3D made of straight segments between the points, with the first and last connected.
513    ///
514    /// # Example
515    /// ```
516    /// # use bevy_gizmos::prelude::*;
517    /// # use bevy_math::prelude::*;
518    /// # use bevy_color::palettes::basic::GREEN;
519    /// fn system(mut gizmos: Gizmos) {
520    ///     gizmos.lineloop([Vec3::ZERO, Vec3::X, Vec3::Y], GREEN);
521    /// }
522    /// # bevy_ecs::system::assert_is_system(system);
523    /// ```
524    #[inline]
525    pub fn lineloop(&mut self, positions: impl IntoIterator<Item = Vec3>, color: impl Into<Color>) {
526        if !self.enabled {
527            return;
528        }
529
530        // Loop back to the start; second is needed to ensure that
531        // the joint on the first corner is drawn.
532        let mut positions = positions.into_iter();
533        let first = positions.next();
534        let second = positions.next();
535
536        self.linestrip(
537            first
538                .into_iter()
539                .chain(second)
540                .chain(positions)
541                .chain(first)
542                .chain(second),
543            color,
544        );
545    }
546
547    /// Draw a line in 3D made of straight segments between the points, with a color gradient.
548    ///
549    /// # Example
550    /// ```
551    /// # use bevy_gizmos::prelude::*;
552    /// # use bevy_math::prelude::*;
553    /// # use bevy_color::palettes::basic::{BLUE, GREEN, RED};
554    /// fn system(mut gizmos: Gizmos) {
555    ///     gizmos.linestrip_gradient([
556    ///         (Vec3::ZERO, GREEN),
557    ///         (Vec3::X, RED),
558    ///         (Vec3::Y, BLUE)
559    ///     ]);
560    /// }
561    /// # bevy_ecs::system::assert_is_system(system);
562    /// ```
563    #[inline]
564    pub fn linestrip_gradient<C: Into<Color>>(
565        &mut self,
566        points: impl IntoIterator<Item = (Vec3, C)>,
567    ) {
568        if !self.enabled {
569            return;
570        }
571        let points = points.into_iter();
572
573        let GizmoBuffer {
574            strip_positions,
575            strip_colors,
576            ..
577        } = self;
578
579        let (min, _) = points.size_hint();
580        strip_positions.reserve(min);
581        strip_colors.reserve(min);
582
583        for (position, color) in points {
584            strip_positions.push(position);
585            strip_colors.push(LinearRgba::from(color.into()));
586        }
587
588        strip_positions.push(Vec3::NAN);
589        strip_colors.push(LinearRgba::NAN);
590    }
591
592    /// Draw a wireframe rectangle in 3D with the given `isometry` applied.
593    ///
594    /// If `isometry == Isometry3d::IDENTITY` then
595    ///
596    /// - the center is at `Vec3::ZERO`
597    /// - the sizes are aligned with the `Vec3::X` and `Vec3::Y` axes.
598    ///
599    /// # Example
600    /// ```
601    /// # use bevy_gizmos::prelude::*;
602    /// # use bevy_math::prelude::*;
603    /// # use bevy_color::palettes::basic::GREEN;
604    /// fn system(mut gizmos: Gizmos) {
605    ///     gizmos.rect(Isometry3d::IDENTITY, Vec2::ONE, GREEN);
606    /// }
607    /// # bevy_ecs::system::assert_is_system(system);
608    /// ```
609    #[inline]
610    pub fn rect(&mut self, isometry: impl Into<Isometry3d>, size: Vec2, color: impl Into<Color>) {
611        if !self.enabled {
612            return;
613        }
614        let isometry = isometry.into();
615        let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2.extend(0.));
616        self.lineloop([tl, tr, br, bl], color);
617    }
618
619    /// Draw a wireframe cube in 3D.
620    ///
621    /// # Example
622    /// ```
623    /// # use bevy_gizmos::prelude::*;
624    /// # use bevy_transform::prelude::*;
625    /// # use bevy_color::palettes::basic::GREEN;
626    /// fn system(mut gizmos: Gizmos) {
627    ///     gizmos.cube(Transform::IDENTITY, GREEN);
628    /// }
629    /// # bevy_ecs::system::assert_is_system(system);
630    /// ```
631    #[inline]
632    pub fn cube(&mut self, transform: impl TransformPoint, color: impl Into<Color>) {
633        let polymorphic_color: Color = color.into();
634        if !self.enabled {
635            return;
636        }
637        let rect = rect_inner(Vec2::ONE);
638        // Front
639        let [tlf, trf, brf, blf] = rect.map(|vec2| transform.transform_point(vec2.extend(0.5)));
640        // Back
641        let [tlb, trb, brb, blb] = rect.map(|vec2| transform.transform_point(vec2.extend(-0.5)));
642
643        let strip_positions = [
644            tlf, trf, brf, blf, tlf, // Front
645            tlb, trb, brb, blb, tlb, // Back
646        ];
647        self.linestrip(strip_positions, polymorphic_color);
648
649        let list_positions = [
650            trf, trb, brf, brb, blf, blb, // Front to back
651        ];
652        self.extend_list_positions(list_positions);
653
654        self.add_list_color(polymorphic_color, 6);
655    }
656
657    /// Draw a wireframe aabb in 3D.
658    ///
659    /// # Example
660    /// ```
661    /// # use bevy_gizmos::prelude::*;
662    /// # use bevy_transform::prelude::*;
663    /// # use bevy_math::Vec3;
664    /// # use bevy_shape::Aabb3d;
665    /// # use bevy_color::palettes::basic::GREEN;
666    /// fn system(mut gizmos: Gizmos) {
667    ///     gizmos.aabb_3d(Aabb3d::new(Vec3::ZERO, Vec3::ONE), Transform::IDENTITY, GREEN);
668    /// }
669    /// # bevy_ecs::system::assert_is_system(system);
670    /// ```
671    #[inline]
672    pub fn aabb_3d(
673        &mut self,
674        aabb: impl Into<Aabb3d>,
675        transform: impl TransformPoint,
676        color: impl Into<Color>,
677    ) {
678        let polymorphic_color: Color = color.into();
679        if !self.enabled {
680            return;
681        }
682        let aabb = aabb.into();
683        let [tlf, trf, brf, blf, tlb, trb, brb, blb] = [
684            Vec3::new(aabb.min.x, aabb.max.y, aabb.max.z),
685            Vec3::new(aabb.max.x, aabb.max.y, aabb.max.z),
686            Vec3::new(aabb.max.x, aabb.min.y, aabb.max.z),
687            Vec3::new(aabb.min.x, aabb.min.y, aabb.max.z),
688            Vec3::new(aabb.min.x, aabb.max.y, aabb.min.z),
689            Vec3::new(aabb.max.x, aabb.max.y, aabb.min.z),
690            Vec3::new(aabb.max.x, aabb.min.y, aabb.min.z),
691            Vec3::new(aabb.min.x, aabb.min.y, aabb.min.z),
692        ]
693        .map(|v| transform.transform_point(v));
694
695        let strip_positions = [
696            tlf, trf, brf, blf, tlf, // Front
697            tlb, trb, brb, blb, tlb, // Back
698        ];
699        self.linestrip(strip_positions, polymorphic_color);
700
701        let list_positions = [
702            trf, trb, brf, brb, blf, blb, // Front to back
703        ];
704        self.extend_list_positions(list_positions);
705
706        self.add_list_color(polymorphic_color, 6);
707    }
708
709    /// Draw a line in 2D from `start` to `end`.
710    ///
711    /// # Example
712    /// ```
713    /// # use bevy_gizmos::prelude::*;
714    /// # use bevy_math::prelude::*;
715    /// # use bevy_color::palettes::basic::GREEN;
716    /// fn system(mut gizmos: Gizmos) {
717    ///     gizmos.line_2d(Vec2::ZERO, Vec2::X, GREEN);
718    /// }
719    /// # bevy_ecs::system::assert_is_system(system);
720    /// ```
721    #[inline]
722    pub fn line_2d(&mut self, start: Vec2, end: Vec2, color: impl Into<Color>) {
723        if !self.enabled {
724            return;
725        }
726        self.line(start.extend(0.), end.extend(0.), color);
727    }
728
729    /// Draw a line in 2D with a color gradient from `start` to `end`.
730    ///
731    /// # Example
732    /// ```
733    /// # use bevy_gizmos::prelude::*;
734    /// # use bevy_math::prelude::*;
735    /// # use bevy_color::palettes::basic::{RED, GREEN};
736    /// fn system(mut gizmos: Gizmos) {
737    ///     gizmos.line_gradient_2d(Vec2::ZERO, Vec2::X, GREEN, RED);
738    /// }
739    /// # bevy_ecs::system::assert_is_system(system);
740    /// ```
741    #[inline]
742    pub fn line_gradient_2d<C: Into<Color>>(
743        &mut self,
744        start: Vec2,
745        end: Vec2,
746        start_color: C,
747        end_color: C,
748    ) {
749        if !self.enabled {
750            return;
751        }
752        self.line_gradient(start.extend(0.), end.extend(0.), start_color, end_color);
753    }
754
755    /// Draw a line in 2D made of straight segments between the points.
756    ///
757    /// # Example
758    /// ```
759    /// # use bevy_gizmos::prelude::*;
760    /// # use bevy_math::prelude::*;
761    /// # use bevy_color::palettes::basic::GREEN;
762    /// fn system(mut gizmos: Gizmos) {
763    ///     gizmos.linestrip_2d([Vec2::ZERO, Vec2::X, Vec2::Y], GREEN);
764    /// }
765    /// # bevy_ecs::system::assert_is_system(system);
766    /// ```
767    #[inline]
768    pub fn linestrip_2d(
769        &mut self,
770        positions: impl IntoIterator<Item = Vec2>,
771        color: impl Into<Color>,
772    ) {
773        if !self.enabled {
774            return;
775        }
776        self.linestrip(positions.into_iter().map(|vec2| vec2.extend(0.)), color);
777    }
778
779    /// Draw a line in 2D made of straight segments between the points, with the first and last connected.
780    ///
781    /// # Example
782    /// ```
783    /// # use bevy_gizmos::prelude::*;
784    /// # use bevy_math::prelude::*;
785    /// # use bevy_color::palettes::basic::GREEN;
786    /// fn system(mut gizmos: Gizmos) {
787    ///     gizmos.lineloop_2d([Vec2::ZERO, Vec2::X, Vec2::Y], GREEN);
788    /// }
789    /// # bevy_ecs::system::assert_is_system(system);
790    /// ```
791    #[inline]
792    pub fn lineloop_2d(
793        &mut self,
794        positions: impl IntoIterator<Item = Vec2>,
795        color: impl Into<Color>,
796    ) {
797        if !self.enabled {
798            return;
799        }
800        self.lineloop(positions.into_iter().map(|vec2| vec2.extend(0.)), color);
801    }
802
803    /// Draw a line in 2D made of straight segments between the points, with a color gradient.
804    ///
805    /// # Example
806    /// ```
807    /// # use bevy_gizmos::prelude::*;
808    /// # use bevy_math::prelude::*;
809    /// # use bevy_color::palettes::basic::{RED, GREEN, BLUE};
810    /// fn system(mut gizmos: Gizmos) {
811    ///     gizmos.linestrip_gradient_2d([
812    ///         (Vec2::ZERO, GREEN),
813    ///         (Vec2::X, RED),
814    ///         (Vec2::Y, BLUE)
815    ///     ]);
816    /// }
817    /// # bevy_ecs::system::assert_is_system(system);
818    /// ```
819    #[inline]
820    pub fn linestrip_gradient_2d<C: Into<Color>>(
821        &mut self,
822        positions: impl IntoIterator<Item = (Vec2, C)>,
823    ) {
824        if !self.enabled {
825            return;
826        }
827        self.linestrip_gradient(
828            positions
829                .into_iter()
830                .map(|(vec2, color)| (vec2.extend(0.), color)),
831        );
832    }
833
834    /// Draw a line in 2D from `start` to `start + vector`.
835    ///
836    /// # Example
837    /// ```
838    /// # use bevy_gizmos::prelude::*;
839    /// # use bevy_math::prelude::*;
840    /// # use bevy_color::palettes::basic::GREEN;
841    /// fn system(mut gizmos: Gizmos) {
842    ///     gizmos.ray_2d(Vec2::Y, Vec2::X, GREEN);
843    /// }
844    /// # bevy_ecs::system::assert_is_system(system);
845    /// ```
846    #[inline]
847    pub fn ray_2d(&mut self, start: Vec2, vector: Vec2, color: impl Into<Color>) {
848        if !self.enabled {
849            return;
850        }
851        self.line_2d(start, start + vector, color);
852    }
853
854    /// Draw a line in 2D with a color gradient from `start` to `start + vector`.
855    ///
856    /// # Example
857    /// ```
858    /// # use bevy_gizmos::prelude::*;
859    /// # use bevy_math::prelude::*;
860    /// # use bevy_color::palettes::basic::{RED, GREEN};
861    /// fn system(mut gizmos: Gizmos) {
862    ///     gizmos.line_gradient(Vec3::Y, Vec3::X, GREEN, RED);
863    /// }
864    /// # bevy_ecs::system::assert_is_system(system);
865    /// ```
866    #[inline]
867    pub fn ray_gradient_2d<C: Into<Color>>(
868        &mut self,
869        start: Vec2,
870        vector: Vec2,
871        start_color: C,
872        end_color: C,
873    ) {
874        if !self.enabled {
875            return;
876        }
877        self.line_gradient_2d(start, start + vector, start_color, end_color);
878    }
879
880    /// Draw a wireframe rectangle in 2D with the given `isometry` applied.
881    ///
882    /// If `isometry == Isometry2d::IDENTITY` then
883    ///
884    /// - the center is at `Vec2::ZERO`
885    /// - the sizes are aligned with the `Vec2::X` and `Vec2::Y` axes.
886    ///
887    /// # Example
888    /// ```
889    /// # use bevy_gizmos::prelude::*;
890    /// # use bevy_math::prelude::*;
891    /// # use bevy_color::palettes::basic::GREEN;
892    /// fn system(mut gizmos: Gizmos) {
893    ///     gizmos.rect_2d(Isometry2d::IDENTITY, Vec2::ONE, GREEN);
894    /// }
895    /// # bevy_ecs::system::assert_is_system(system);
896    /// ```
897    #[inline]
898    pub fn rect_2d(
899        &mut self,
900        isometry: impl Into<Isometry2d>,
901        size: Vec2,
902        color: impl Into<Color>,
903    ) {
904        if !self.enabled {
905            return;
906        }
907        let isometry = isometry.into();
908        let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2);
909        self.lineloop_2d([tl, tr, br, bl], color);
910    }
911
912    #[inline]
913    fn extend_list_positions(&mut self, positions: impl IntoIterator<Item = Vec3>) {
914        self.list_positions.extend(positions);
915    }
916
917    #[inline]
918    fn extend_list_colors(&mut self, colors: impl IntoIterator<Item = impl Into<Color>>) {
919        self.list_colors.extend(
920            colors
921                .into_iter()
922                .map(|color| LinearRgba::from(color.into())),
923        );
924    }
925
926    #[inline]
927    fn add_list_color(&mut self, color: impl Into<Color>, count: usize) {
928        let polymorphic_color: Color = color.into();
929        let linear_color = LinearRgba::from(polymorphic_color);
930
931        self.list_colors.extend(iter::repeat_n(linear_color, count));
932    }
933
934    #[inline]
935    fn extend_strip_positions(&mut self, positions: impl IntoIterator<Item = Vec3>) {
936        self.strip_positions.extend(positions);
937        self.strip_positions.push(Vec3::NAN);
938    }
939}
940
941fn rect_inner(size: Vec2) -> [Vec2; 4] {
942    let half_size = size / 2.;
943    let tl = Vec2::new(-half_size.x, half_size.y);
944    let tr = Vec2::new(half_size.x, half_size.y);
945    let bl = Vec2::new(-half_size.x, -half_size.y);
946    let br = Vec2::new(half_size.x, -half_size.y);
947    [tl, tr, br, bl]
948}