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