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