bevy_gizmos/gizmos.rs
1//! A module for the [`Gizmos`] [`SystemParam`].
2
3use core::{
4 iter,
5 marker::PhantomData,
6 mem,
7 ops::{Deref, DerefMut},
8};
9
10use bevy_color::{Color, LinearRgba};
11use bevy_ecs::{
12 change_detection::Tick,
13 query::FilteredAccessSet,
14 resource::Resource,
15 system::{
16 Deferred, ReadOnlySystemParam, Res, SystemBuffer, SystemMeta, SystemParam,
17 SystemParamValidationError,
18 },
19 world::{unsafe_world_cell::UnsafeWorldCell, World},
20};
21use bevy_math::{bounding::Aabb3d, Isometry2d, Isometry3d, Vec2, Vec3};
22use bevy_reflect::{std_traits::ReflectDefault, Reflect};
23use bevy_transform::TransformPoint;
24use bevy_utils::default;
25
26use crate::{
27 config::{DefaultGizmoConfigGroup, GizmoConfigGroup, GizmoConfigStore},
28 prelude::GizmoConfig,
29};
30
31/// Storage of gizmo primitives.
32#[derive(Resource)]
33pub struct GizmoStorage<Config, Clear> {
34 pub(crate) list_positions: Vec<Vec3>,
35 pub(crate) list_colors: Vec<LinearRgba>,
36 pub(crate) strip_positions: Vec<Vec3>,
37 pub(crate) strip_colors: Vec<LinearRgba>,
38 marker: PhantomData<(Config, Clear)>,
39}
40
41impl<Config, Clear> Default for GizmoStorage<Config, Clear> {
42 fn default() -> Self {
43 Self {
44 list_positions: default(),
45 list_colors: default(),
46 strip_positions: default(),
47 strip_colors: default(),
48 marker: PhantomData,
49 }
50 }
51}
52
53impl<Config, Clear> GizmoStorage<Config, Clear>
54where
55 Config: GizmoConfigGroup,
56 Clear: 'static + Send + Sync,
57{
58 /// Combine the other gizmo storage with this one.
59 pub fn append_storage<OtherConfig, OtherClear>(
60 &mut self,
61 other: &GizmoStorage<OtherConfig, OtherClear>,
62 ) {
63 self.list_positions.extend(other.list_positions.iter());
64 self.list_colors.extend(other.list_colors.iter());
65 self.strip_positions.extend(other.strip_positions.iter());
66 self.strip_colors.extend(other.strip_colors.iter());
67 }
68
69 pub(crate) fn swap<OtherConfig, OtherClear>(
70 &mut self,
71 other: &mut GizmoStorage<OtherConfig, OtherClear>,
72 ) {
73 mem::swap(&mut self.list_positions, &mut other.list_positions);
74 mem::swap(&mut self.list_colors, &mut other.list_colors);
75 mem::swap(&mut self.strip_positions, &mut other.strip_positions);
76 mem::swap(&mut self.strip_colors, &mut other.strip_colors);
77 }
78
79 /// Clear this gizmo storage of any requested gizmos.
80 pub fn clear(&mut self) {
81 self.list_positions.clear();
82 self.list_colors.clear();
83 self.strip_positions.clear();
84 self.strip_colors.clear();
85 }
86}
87
88/// Swap buffer for a specific clearing context.
89///
90/// This is to stash/store the default/requested gizmos so another context can
91/// be substituted for that duration.
92pub struct Swap<Clear>(PhantomData<Clear>);
93
94/// A [`SystemParam`] for drawing gizmos.
95///
96/// They are drawn in immediate mode, which means they will be rendered only for
97/// the frames, or ticks when in [`FixedMain`](bevy_app::FixedMain), in which
98/// they are spawned.
99///
100/// A system in [`Main`](bevy_app::Main) will be cleared each rendering
101/// frame, while a system in [`FixedMain`](bevy_app::FixedMain) will be
102/// cleared each time the [`RunFixedMainLoop`](bevy_app::RunFixedMainLoop)
103/// schedule is run.
104///
105/// Gizmos should be spawned before the [`Last`](bevy_app::Last) schedule
106/// to ensure they are drawn.
107///
108/// To set up your own clearing context (useful for custom scheduling similar
109/// to [`FixedMain`](bevy_app::FixedMain)):
110///
111/// ```
112/// use bevy_gizmos::{prelude::*, *, gizmos::GizmoStorage};
113/// # use bevy_app::prelude::*;
114/// # use bevy_ecs::{schedule::ScheduleLabel, prelude::*};
115/// # #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
116/// # struct StartOfMyContext;
117/// # #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
118/// # struct EndOfMyContext;
119/// # #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
120/// # struct StartOfRun;
121/// # #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
122/// # struct EndOfRun;
123/// # struct MyContext;
124/// struct ClearContextSetup;
125/// impl Plugin for ClearContextSetup {
126/// fn build(&self, app: &mut App) {
127/// app.init_resource::<GizmoStorage<DefaultGizmoConfigGroup, MyContext>>()
128/// // Make sure this context starts/ends cleanly if inside another context. E.g. it
129/// // should start after the parent context starts and end after the parent context ends.
130/// .add_systems(StartOfMyContext, start_gizmo_context::<DefaultGizmoConfigGroup, MyContext>)
131/// // If not running multiple times, put this with [`start_gizmo_context`].
132/// .add_systems(StartOfRun, clear_gizmo_context::<DefaultGizmoConfigGroup, MyContext>)
133/// // If not running multiple times, put this with [`end_gizmo_context`].
134/// .add_systems(EndOfRun, collect_requested_gizmos::<DefaultGizmoConfigGroup, MyContext>)
135/// .add_systems(EndOfMyContext, end_gizmo_context::<DefaultGizmoConfigGroup, MyContext>)
136/// .add_systems(
137/// Last,
138/// propagate_gizmos::<DefaultGizmoConfigGroup, MyContext>.before(GizmoMeshSystems),
139/// );
140/// }
141/// }
142/// ```
143pub struct Gizmos<'w, 's, Config = DefaultGizmoConfigGroup, Clear = ()>
144where
145 Config: GizmoConfigGroup,
146 Clear: 'static + Send + Sync,
147{
148 buffer: Deferred<'s, GizmoBuffer<Config, Clear>>,
149 /// The currently used [`GizmoConfig`]
150 pub config: &'w GizmoConfig,
151 /// The currently used [`GizmoConfigGroup`]
152 pub config_ext: &'w Config,
153}
154
155impl<'w, 's, Config, Clear> Deref for Gizmos<'w, 's, Config, Clear>
156where
157 Config: GizmoConfigGroup,
158 Clear: 'static + Send + Sync,
159{
160 type Target = GizmoBuffer<Config, Clear>;
161
162 fn deref(&self) -> &Self::Target {
163 &self.buffer
164 }
165}
166
167impl<'w, 's, Config, Clear> DerefMut for Gizmos<'w, 's, Config, Clear>
168where
169 Config: GizmoConfigGroup,
170 Clear: 'static + Send + Sync,
171{
172 fn deref_mut(&mut self) -> &mut Self::Target {
173 &mut self.buffer
174 }
175}
176
177type GizmosState<Config, Clear> = (
178 Deferred<'static, GizmoBuffer<Config, Clear>>,
179 Res<'static, GizmoConfigStore>,
180);
181#[doc(hidden)]
182pub struct GizmosFetchState<Config, Clear>
183where
184 Config: GizmoConfigGroup,
185 Clear: 'static + Send + Sync,
186{
187 state: <GizmosState<Config, Clear> as SystemParam>::State,
188}
189
190#[expect(
191 unsafe_code,
192 reason = "We cannot implement SystemParam without using unsafe code."
193)]
194// SAFETY: All methods are delegated to existing `SystemParam` implementations
195unsafe impl<Config, Clear> SystemParam for Gizmos<'_, '_, Config, Clear>
196where
197 Config: GizmoConfigGroup,
198 Clear: 'static + Send + Sync,
199{
200 type State = GizmosFetchState<Config, Clear>;
201 type Item<'w, 's> = Gizmos<'w, 's, Config, Clear>;
202
203 fn init_state(world: &mut World) -> Self::State {
204 GizmosFetchState {
205 state: GizmosState::<Config, Clear>::init_state(world),
206 }
207 }
208
209 fn init_access(
210 state: &Self::State,
211 system_meta: &mut SystemMeta,
212 component_access_set: &mut FilteredAccessSet,
213 world: &mut World,
214 ) {
215 GizmosState::<Config, Clear>::init_access(
216 &state.state,
217 system_meta,
218 component_access_set,
219 world,
220 );
221 }
222
223 fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {
224 GizmosState::<Config, Clear>::apply(&mut state.state, system_meta, world);
225 }
226
227 #[inline]
228 unsafe fn validate_param(
229 state: &mut Self::State,
230 system_meta: &SystemMeta,
231 world: UnsafeWorldCell,
232 ) -> Result<(), SystemParamValidationError> {
233 // SAFETY: Delegated to existing `SystemParam` implementations.
234 unsafe {
235 GizmosState::<Config, Clear>::validate_param(&mut state.state, system_meta, world)
236 }
237 }
238
239 #[inline]
240 unsafe fn get_param<'w, 's>(
241 state: &'s mut Self::State,
242 system_meta: &SystemMeta,
243 world: UnsafeWorldCell<'w>,
244 change_tick: Tick,
245 ) -> Self::Item<'w, 's> {
246 // SAFETY: Delegated to existing `SystemParam` implementations.
247 let (mut f0, f1) = unsafe {
248 GizmosState::<Config, Clear>::get_param(
249 &mut state.state,
250 system_meta,
251 world,
252 change_tick,
253 )
254 };
255
256 // Accessing the GizmoConfigStore in every API call reduces performance significantly.
257 // Implementing SystemParam manually allows us to cache whether the config is currently enabled.
258 // Having this available allows for cheap early returns when gizmos are disabled.
259 let (config, config_ext) = f1.into_inner().config::<Config>();
260 f0.enabled = config.enabled;
261
262 Gizmos {
263 buffer: f0,
264 config,
265 config_ext,
266 }
267 }
268}
269
270#[expect(
271 unsafe_code,
272 reason = "We cannot implement ReadOnlySystemParam without using unsafe code."
273)]
274// Safety: Each field is `ReadOnlySystemParam`, and Gizmos SystemParam does not mutate world
275unsafe impl<'w, 's, Config, Clear> ReadOnlySystemParam for Gizmos<'w, 's, Config, Clear>
276where
277 Config: GizmoConfigGroup,
278 Clear: 'static + Send + Sync,
279 Deferred<'s, GizmoBuffer<Config, Clear>>: ReadOnlySystemParam,
280 Res<'w, GizmoConfigStore>: ReadOnlySystemParam,
281{
282}
283
284/// Buffer for gizmo vertex data.
285#[derive(Debug, Clone, Reflect)]
286#[reflect(Default)]
287pub struct GizmoBuffer<Config, Clear>
288where
289 Config: GizmoConfigGroup,
290 Clear: 'static + Send + Sync,
291{
292 pub(crate) enabled: bool,
293 /// The positions of line segment endpoints.
294 pub list_positions: Vec<Vec3>,
295 /// The colors of line segment endpoints.
296 pub list_colors: Vec<LinearRgba>,
297 /// The positions of line strip vertices.
298 pub strip_positions: Vec<Vec3>,
299 /// The colors of line strip vertices.
300 pub strip_colors: Vec<LinearRgba>,
301 #[reflect(ignore, clone)]
302 pub(crate) marker: PhantomData<(Config, Clear)>,
303}
304
305impl<Config, Clear> Default for GizmoBuffer<Config, Clear>
306where
307 Config: GizmoConfigGroup,
308 Clear: 'static + Send + Sync,
309{
310 fn default() -> Self {
311 GizmoBuffer::new()
312 }
313}
314
315impl<Config, Clear> GizmoBuffer<Config, Clear>
316where
317 Config: GizmoConfigGroup,
318 Clear: 'static + Send + Sync,
319{
320 /// Constructs an empty `GizmoBuffer`.
321 pub const fn new() -> Self {
322 GizmoBuffer {
323 enabled: true,
324 list_positions: Vec::new(),
325 list_colors: Vec::new(),
326 strip_positions: Vec::new(),
327 strip_colors: Vec::new(),
328 marker: PhantomData,
329 }
330 }
331}
332
333/// Read-only view into [`GizmoBuffer`] data.
334pub struct GizmoBufferView<'a> {
335 /// Vertex positions for line-list topology.
336 pub list_positions: &'a Vec<Vec3>,
337 /// Vertex colors for line-list topology.
338 pub list_colors: &'a Vec<LinearRgba>,
339 /// Vertex positions for line-strip topology.
340 pub strip_positions: &'a Vec<Vec3>,
341 /// Vertex colors for line-strip topology.
342 pub strip_colors: &'a Vec<LinearRgba>,
343}
344
345impl<Config, Clear> SystemBuffer for GizmoBuffer<Config, Clear>
346where
347 Config: GizmoConfigGroup,
348 Clear: 'static + Send + Sync,
349{
350 fn apply(&mut self, _system_meta: &SystemMeta, world: &mut World) {
351 let mut storage = world.resource_mut::<GizmoStorage<Config, Clear>>();
352 storage.list_positions.append(&mut self.list_positions);
353 storage.list_colors.append(&mut self.list_colors);
354 storage.strip_positions.append(&mut self.strip_positions);
355 storage.strip_colors.append(&mut self.strip_colors);
356 }
357}
358
359impl<Config, Clear> GizmoBuffer<Config, Clear>
360where
361 Config: GizmoConfigGroup,
362 Clear: 'static + Send + Sync,
363{
364 /// Clear all data.
365 pub fn clear(&mut self) {
366 self.list_positions.clear();
367 self.list_colors.clear();
368 self.strip_positions.clear();
369 self.strip_colors.clear();
370 }
371
372 /// Read-only view into the buffers data.
373 pub fn buffer(&self) -> GizmoBufferView<'_> {
374 let GizmoBuffer {
375 list_positions,
376 list_colors,
377 strip_positions,
378 strip_colors,
379 ..
380 } = self;
381 GizmoBufferView {
382 list_positions,
383 list_colors,
384 strip_positions,
385 strip_colors,
386 }
387 }
388 /// Draw a line in 3D from `start` to `end`.
389 ///
390 /// # Example
391 /// ```
392 /// # use bevy_gizmos::prelude::*;
393 /// # use bevy_math::prelude::*;
394 /// # use bevy_color::palettes::basic::GREEN;
395 /// fn system(mut gizmos: Gizmos) {
396 /// gizmos.line(Vec3::ZERO, Vec3::X, GREEN);
397 /// }
398 /// # bevy_ecs::system::assert_is_system(system);
399 /// ```
400 #[inline]
401 pub fn line(&mut self, start: Vec3, end: Vec3, color: impl Into<Color>) {
402 if !self.enabled {
403 return;
404 }
405 self.extend_list_positions([start, end]);
406 self.add_list_color(color, 2);
407 }
408
409 /// Draw a line in 3D with a color gradient from `start` to `end`.
410 ///
411 /// # Example
412 /// ```
413 /// # use bevy_gizmos::prelude::*;
414 /// # use bevy_math::prelude::*;
415 /// # use bevy_color::palettes::basic::{RED, GREEN};
416 /// fn system(mut gizmos: Gizmos) {
417 /// gizmos.line_gradient(Vec3::ZERO, Vec3::X, GREEN, RED);
418 /// }
419 /// # bevy_ecs::system::assert_is_system(system);
420 /// ```
421 #[inline]
422 pub fn line_gradient<C: Into<Color>>(
423 &mut self,
424 start: Vec3,
425 end: Vec3,
426 start_color: C,
427 end_color: C,
428 ) {
429 if !self.enabled {
430 return;
431 }
432 self.extend_list_positions([start, end]);
433 self.extend_list_colors([start_color, end_color]);
434 }
435
436 /// Draw a line in 3D from `start` to `start + vector`.
437 ///
438 /// # Example
439 /// ```
440 /// # use bevy_gizmos::prelude::*;
441 /// # use bevy_math::prelude::*;
442 /// # use bevy_color::palettes::basic::GREEN;
443 /// fn system(mut gizmos: Gizmos) {
444 /// gizmos.ray(Vec3::Y, Vec3::X, GREEN);
445 /// }
446 /// # bevy_ecs::system::assert_is_system(system);
447 /// ```
448 #[inline]
449 pub fn ray(&mut self, start: Vec3, vector: Vec3, color: impl Into<Color>) {
450 if !self.enabled {
451 return;
452 }
453 self.line(start, start + vector, color);
454 }
455
456 /// Draw a line in 3D with a color gradient from `start` to `start + vector`.
457 ///
458 /// # Example
459 /// ```
460 /// # use bevy_gizmos::prelude::*;
461 /// # use bevy_math::prelude::*;
462 /// # use bevy_color::palettes::basic::{RED, GREEN};
463 /// fn system(mut gizmos: Gizmos) {
464 /// gizmos.ray_gradient(Vec3::Y, Vec3::X, GREEN, RED);
465 /// }
466 /// # bevy_ecs::system::assert_is_system(system);
467 /// ```
468 #[inline]
469 pub fn ray_gradient<C: Into<Color>>(
470 &mut self,
471 start: Vec3,
472 vector: Vec3,
473 start_color: C,
474 end_color: C,
475 ) {
476 if !self.enabled {
477 return;
478 }
479 self.line_gradient(start, start + vector, start_color, end_color);
480 }
481
482 /// Draw a line in 3D made of straight segments between the points.
483 ///
484 /// # Example
485 /// ```
486 /// # use bevy_gizmos::prelude::*;
487 /// # use bevy_math::prelude::*;
488 /// # use bevy_color::palettes::basic::GREEN;
489 /// fn system(mut gizmos: Gizmos) {
490 /// gizmos.linestrip([Vec3::ZERO, Vec3::X, Vec3::Y], GREEN);
491 /// }
492 /// # bevy_ecs::system::assert_is_system(system);
493 /// ```
494 #[inline]
495 pub fn linestrip(
496 &mut self,
497 positions: impl IntoIterator<Item = Vec3>,
498 color: impl Into<Color>,
499 ) {
500 if !self.enabled {
501 return;
502 }
503 self.extend_strip_positions(positions);
504 let len = self.strip_positions.len();
505 let linear_color = LinearRgba::from(color.into());
506 self.strip_colors.resize(len - 1, linear_color);
507 self.strip_colors.push(LinearRgba::NAN);
508 }
509
510 /// Draw a line in 3D made of straight segments between the points, with the first and last connected.
511 ///
512 /// # Example
513 /// ```
514 /// # use bevy_gizmos::prelude::*;
515 /// # use bevy_math::prelude::*;
516 /// # use bevy_color::palettes::basic::GREEN;
517 /// fn system(mut gizmos: Gizmos) {
518 /// gizmos.lineloop([Vec3::ZERO, Vec3::X, Vec3::Y], GREEN);
519 /// }
520 /// # bevy_ecs::system::assert_is_system(system);
521 /// ```
522 #[inline]
523 pub fn lineloop(&mut self, positions: impl IntoIterator<Item = Vec3>, color: impl Into<Color>) {
524 if !self.enabled {
525 return;
526 }
527
528 // Loop back to the start; second is needed to ensure that
529 // the joint on the first corner is drawn.
530 let mut positions = positions.into_iter();
531 let first = positions.next();
532 let second = positions.next();
533
534 self.linestrip(
535 first
536 .into_iter()
537 .chain(second)
538 .chain(positions)
539 .chain(first)
540 .chain(second),
541 color,
542 );
543 }
544
545 /// Draw a line in 3D made of straight segments between the points, with a color gradient.
546 ///
547 /// # Example
548 /// ```
549 /// # use bevy_gizmos::prelude::*;
550 /// # use bevy_math::prelude::*;
551 /// # use bevy_color::palettes::basic::{BLUE, GREEN, RED};
552 /// fn system(mut gizmos: Gizmos) {
553 /// gizmos.linestrip_gradient([
554 /// (Vec3::ZERO, GREEN),
555 /// (Vec3::X, RED),
556 /// (Vec3::Y, BLUE)
557 /// ]);
558 /// }
559 /// # bevy_ecs::system::assert_is_system(system);
560 /// ```
561 #[inline]
562 pub fn linestrip_gradient<C: Into<Color>>(
563 &mut self,
564 points: impl IntoIterator<Item = (Vec3, C)>,
565 ) {
566 if !self.enabled {
567 return;
568 }
569 let points = points.into_iter();
570
571 let GizmoBuffer {
572 strip_positions,
573 strip_colors,
574 ..
575 } = self;
576
577 let (min, _) = points.size_hint();
578 strip_positions.reserve(min);
579 strip_colors.reserve(min);
580
581 for (position, color) in points {
582 strip_positions.push(position);
583 strip_colors.push(LinearRgba::from(color.into()));
584 }
585
586 strip_positions.push(Vec3::NAN);
587 strip_colors.push(LinearRgba::NAN);
588 }
589
590 /// Draw a wireframe rectangle in 3D with the given `isometry` applied.
591 ///
592 /// If `isometry == Isometry3d::IDENTITY` then
593 ///
594 /// - the center is at `Vec3::ZERO`
595 /// - the sizes are aligned with the `Vec3::X` and `Vec3::Y` axes.
596 ///
597 /// # Example
598 /// ```
599 /// # use bevy_gizmos::prelude::*;
600 /// # use bevy_math::prelude::*;
601 /// # use bevy_color::palettes::basic::GREEN;
602 /// fn system(mut gizmos: Gizmos) {
603 /// gizmos.rect(Isometry3d::IDENTITY, Vec2::ONE, GREEN);
604 /// }
605 /// # bevy_ecs::system::assert_is_system(system);
606 /// ```
607 #[inline]
608 pub fn rect(&mut self, isometry: impl Into<Isometry3d>, size: Vec2, color: impl Into<Color>) {
609 if !self.enabled {
610 return;
611 }
612 let isometry = isometry.into();
613 let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2.extend(0.));
614 self.lineloop([tl, tr, br, bl], color);
615 }
616
617 /// Draw a wireframe cube in 3D.
618 ///
619 /// # Example
620 /// ```
621 /// # use bevy_gizmos::prelude::*;
622 /// # use bevy_transform::prelude::*;
623 /// # use bevy_color::palettes::basic::GREEN;
624 /// fn system(mut gizmos: Gizmos) {
625 /// gizmos.cube(Transform::IDENTITY, GREEN);
626 /// }
627 /// # bevy_ecs::system::assert_is_system(system);
628 /// ```
629 #[inline]
630 pub fn cube(&mut self, transform: impl TransformPoint, color: impl Into<Color>) {
631 let polymorphic_color: Color = color.into();
632 if !self.enabled {
633 return;
634 }
635 let rect = rect_inner(Vec2::ONE);
636 // Front
637 let [tlf, trf, brf, blf] = rect.map(|vec2| transform.transform_point(vec2.extend(0.5)));
638 // Back
639 let [tlb, trb, brb, blb] = rect.map(|vec2| transform.transform_point(vec2.extend(-0.5)));
640
641 let strip_positions = [
642 tlf, trf, brf, blf, tlf, // Front
643 tlb, trb, brb, blb, tlb, // Back
644 ];
645 self.linestrip(strip_positions, polymorphic_color);
646
647 let list_positions = [
648 trf, trb, brf, brb, blf, blb, // Front to back
649 ];
650 self.extend_list_positions(list_positions);
651
652 self.add_list_color(polymorphic_color, 6);
653 }
654
655 /// Draw a wireframe aabb in 3D.
656 ///
657 /// # Example
658 /// ```
659 /// # use bevy_gizmos::prelude::*;
660 /// # use bevy_transform::prelude::*;
661 /// # use bevy_math::{bounding::Aabb3d, Vec3};
662 /// # use bevy_color::palettes::basic::GREEN;
663 /// fn system(mut gizmos: Gizmos) {
664 /// gizmos.aabb_3d(Aabb3d::new(Vec3::ZERO, Vec3::ONE), Transform::IDENTITY, GREEN);
665 /// }
666 /// # bevy_ecs::system::assert_is_system(system);
667 /// ```
668 #[inline]
669 pub fn aabb_3d(
670 &mut self,
671 aabb: impl Into<Aabb3d>,
672 transform: impl TransformPoint,
673 color: impl Into<Color>,
674 ) {
675 let polymorphic_color: Color = color.into();
676 if !self.enabled {
677 return;
678 }
679 let aabb = aabb.into();
680 let [tlf, trf, brf, blf, tlb, trb, brb, blb] = [
681 Vec3::new(aabb.min.x, aabb.max.y, aabb.max.z),
682 Vec3::new(aabb.max.x, aabb.max.y, aabb.max.z),
683 Vec3::new(aabb.max.x, aabb.min.y, aabb.max.z),
684 Vec3::new(aabb.min.x, aabb.min.y, aabb.max.z),
685 Vec3::new(aabb.min.x, aabb.max.y, aabb.min.z),
686 Vec3::new(aabb.max.x, aabb.max.y, aabb.min.z),
687 Vec3::new(aabb.max.x, aabb.min.y, aabb.min.z),
688 Vec3::new(aabb.min.x, aabb.min.y, aabb.min.z),
689 ]
690 .map(|v| transform.transform_point(v));
691
692 let strip_positions = [
693 tlf, trf, brf, blf, tlf, // Front
694 tlb, trb, brb, blb, tlb, // Back
695 ];
696 self.linestrip(strip_positions, polymorphic_color);
697
698 let list_positions = [
699 trf, trb, brf, brb, blf, blb, // Front to back
700 ];
701 self.extend_list_positions(list_positions);
702
703 self.add_list_color(polymorphic_color, 6);
704 }
705
706 /// Draw a line in 2D from `start` to `end`.
707 ///
708 /// # Example
709 /// ```
710 /// # use bevy_gizmos::prelude::*;
711 /// # use bevy_math::prelude::*;
712 /// # use bevy_color::palettes::basic::GREEN;
713 /// fn system(mut gizmos: Gizmos) {
714 /// gizmos.line_2d(Vec2::ZERO, Vec2::X, GREEN);
715 /// }
716 /// # bevy_ecs::system::assert_is_system(system);
717 /// ```
718 #[inline]
719 pub fn line_2d(&mut self, start: Vec2, end: Vec2, color: impl Into<Color>) {
720 if !self.enabled {
721 return;
722 }
723 self.line(start.extend(0.), end.extend(0.), color);
724 }
725
726 /// Draw a line in 2D with a color gradient from `start` to `end`.
727 ///
728 /// # Example
729 /// ```
730 /// # use bevy_gizmos::prelude::*;
731 /// # use bevy_math::prelude::*;
732 /// # use bevy_color::palettes::basic::{RED, GREEN};
733 /// fn system(mut gizmos: Gizmos) {
734 /// gizmos.line_gradient_2d(Vec2::ZERO, Vec2::X, GREEN, RED);
735 /// }
736 /// # bevy_ecs::system::assert_is_system(system);
737 /// ```
738 #[inline]
739 pub fn line_gradient_2d<C: Into<Color>>(
740 &mut self,
741 start: Vec2,
742 end: Vec2,
743 start_color: C,
744 end_color: C,
745 ) {
746 if !self.enabled {
747 return;
748 }
749 self.line_gradient(start.extend(0.), end.extend(0.), start_color, end_color);
750 }
751
752 /// Draw a line in 2D made of straight segments between the points.
753 ///
754 /// # Example
755 /// ```
756 /// # use bevy_gizmos::prelude::*;
757 /// # use bevy_math::prelude::*;
758 /// # use bevy_color::palettes::basic::GREEN;
759 /// fn system(mut gizmos: Gizmos) {
760 /// gizmos.linestrip_2d([Vec2::ZERO, Vec2::X, Vec2::Y], GREEN);
761 /// }
762 /// # bevy_ecs::system::assert_is_system(system);
763 /// ```
764 #[inline]
765 pub fn linestrip_2d(
766 &mut self,
767 positions: impl IntoIterator<Item = Vec2>,
768 color: impl Into<Color>,
769 ) {
770 if !self.enabled {
771 return;
772 }
773 self.linestrip(positions.into_iter().map(|vec2| vec2.extend(0.)), color);
774 }
775
776 /// Draw a line in 2D made of straight segments between the points, with the first and last connected.
777 ///
778 /// # Example
779 /// ```
780 /// # use bevy_gizmos::prelude::*;
781 /// # use bevy_math::prelude::*;
782 /// # use bevy_color::palettes::basic::GREEN;
783 /// fn system(mut gizmos: Gizmos) {
784 /// gizmos.lineloop_2d([Vec2::ZERO, Vec2::X, Vec2::Y], GREEN);
785 /// }
786 /// # bevy_ecs::system::assert_is_system(system);
787 /// ```
788 #[inline]
789 pub fn lineloop_2d(
790 &mut self,
791 positions: impl IntoIterator<Item = Vec2>,
792 color: impl Into<Color>,
793 ) {
794 if !self.enabled {
795 return;
796 }
797 self.lineloop(positions.into_iter().map(|vec2| vec2.extend(0.)), color);
798 }
799
800 /// Draw a line in 2D made of straight segments between the points, with a color gradient.
801 ///
802 /// # Example
803 /// ```
804 /// # use bevy_gizmos::prelude::*;
805 /// # use bevy_math::prelude::*;
806 /// # use bevy_color::palettes::basic::{RED, GREEN, BLUE};
807 /// fn system(mut gizmos: Gizmos) {
808 /// gizmos.linestrip_gradient_2d([
809 /// (Vec2::ZERO, GREEN),
810 /// (Vec2::X, RED),
811 /// (Vec2::Y, BLUE)
812 /// ]);
813 /// }
814 /// # bevy_ecs::system::assert_is_system(system);
815 /// ```
816 #[inline]
817 pub fn linestrip_gradient_2d<C: Into<Color>>(
818 &mut self,
819 positions: impl IntoIterator<Item = (Vec2, C)>,
820 ) {
821 if !self.enabled {
822 return;
823 }
824 self.linestrip_gradient(
825 positions
826 .into_iter()
827 .map(|(vec2, color)| (vec2.extend(0.), color)),
828 );
829 }
830
831 /// Draw a line in 2D from `start` to `start + vector`.
832 ///
833 /// # Example
834 /// ```
835 /// # use bevy_gizmos::prelude::*;
836 /// # use bevy_math::prelude::*;
837 /// # use bevy_color::palettes::basic::GREEN;
838 /// fn system(mut gizmos: Gizmos) {
839 /// gizmos.ray_2d(Vec2::Y, Vec2::X, GREEN);
840 /// }
841 /// # bevy_ecs::system::assert_is_system(system);
842 /// ```
843 #[inline]
844 pub fn ray_2d(&mut self, start: Vec2, vector: Vec2, color: impl Into<Color>) {
845 if !self.enabled {
846 return;
847 }
848 self.line_2d(start, start + vector, color);
849 }
850
851 /// Draw a line in 2D with a color gradient from `start` to `start + vector`.
852 ///
853 /// # Example
854 /// ```
855 /// # use bevy_gizmos::prelude::*;
856 /// # use bevy_math::prelude::*;
857 /// # use bevy_color::palettes::basic::{RED, GREEN};
858 /// fn system(mut gizmos: Gizmos) {
859 /// gizmos.line_gradient(Vec3::Y, Vec3::X, GREEN, RED);
860 /// }
861 /// # bevy_ecs::system::assert_is_system(system);
862 /// ```
863 #[inline]
864 pub fn ray_gradient_2d<C: Into<Color>>(
865 &mut self,
866 start: Vec2,
867 vector: Vec2,
868 start_color: C,
869 end_color: C,
870 ) {
871 if !self.enabled {
872 return;
873 }
874 self.line_gradient_2d(start, start + vector, start_color, end_color);
875 }
876
877 /// Draw a wireframe rectangle in 2D with the given `isometry` applied.
878 ///
879 /// If `isometry == Isometry2d::IDENTITY` then
880 ///
881 /// - the center is at `Vec2::ZERO`
882 /// - the sizes are aligned with the `Vec2::X` and `Vec2::Y` axes.
883 ///
884 /// # Example
885 /// ```
886 /// # use bevy_gizmos::prelude::*;
887 /// # use bevy_math::prelude::*;
888 /// # use bevy_color::palettes::basic::GREEN;
889 /// fn system(mut gizmos: Gizmos) {
890 /// gizmos.rect_2d(Isometry2d::IDENTITY, Vec2::ONE, GREEN);
891 /// }
892 /// # bevy_ecs::system::assert_is_system(system);
893 /// ```
894 #[inline]
895 pub fn rect_2d(
896 &mut self,
897 isometry: impl Into<Isometry2d>,
898 size: Vec2,
899 color: impl Into<Color>,
900 ) {
901 if !self.enabled {
902 return;
903 }
904 let isometry = isometry.into();
905 let [tl, tr, br, bl] = rect_inner(size).map(|vec2| isometry * vec2);
906 self.lineloop_2d([tl, tr, br, bl], color);
907 }
908
909 #[inline]
910 fn extend_list_positions(&mut self, positions: impl IntoIterator<Item = Vec3>) {
911 self.list_positions.extend(positions);
912 }
913
914 #[inline]
915 fn extend_list_colors(&mut self, colors: impl IntoIterator<Item = impl Into<Color>>) {
916 self.list_colors.extend(
917 colors
918 .into_iter()
919 .map(|color| LinearRgba::from(color.into())),
920 );
921 }
922
923 #[inline]
924 fn add_list_color(&mut self, color: impl Into<Color>, count: usize) {
925 let polymorphic_color: Color = color.into();
926 let linear_color = LinearRgba::from(polymorphic_color);
927
928 self.list_colors.extend(iter::repeat_n(linear_color, count));
929 }
930
931 #[inline]
932 fn extend_strip_positions(&mut self, positions: impl IntoIterator<Item = Vec3>) {
933 self.strip_positions.extend(positions);
934 self.strip_positions.push(Vec3::NAN);
935 }
936}
937
938fn rect_inner(size: Vec2) -> [Vec2; 4] {
939 let half_size = size / 2.;
940 let tl = Vec2::new(-half_size.x, half_size.y);
941 let tr = Vec2::new(half_size.x, half_size.y);
942 let bl = Vec2::new(-half_size.x, -half_size.y);
943 let br = Vec2::new(half_size.x, -half_size.y);
944 [tl, tr, br, bl]
945}