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}