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