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