1use core::f32::consts::{FRAC_PI_2, PI};
4
5use super::helpers::*;
6
7use bevy_color::Color;
8use bevy_math::{Dir2, Isometry2d, Rot2, Vec2};
9use bevy_shape::{
10 Annulus, Arc2d, Capsule2d, Circle, CircularSector, CircularSegment, Ellipse, Line2d, Plane2d,
11 Polygon, Polyline2d, Primitive2d, Rectangle, RegularPolygon, Rhombus, Segment2d, Triangle2d,
12};
13
14use crate::{gizmos::GizmoBuffer, prelude::GizmoConfigGroup};
15
16const MIN_LINE_LEN: f32 = 50.0;
18const HALF_MIN_LINE_LEN: f32 = 25.0;
19const INFINITE_LEN: f32 = 100_000.0;
21
22pub trait GizmoPrimitive2d<P: Primitive2d> {
24 type Output<'a>
26 where
27 Self: 'a;
28
29 fn primitive_2d(
31 &mut self,
32 primitive: &P,
33 isometry: impl Into<Isometry2d>,
34 color: impl Into<Color>,
35 ) -> Self::Output<'_>;
36}
37
38impl<Config, Clear> GizmoPrimitive2d<Dir2> for GizmoBuffer<Config, Clear>
41where
42 Config: GizmoConfigGroup,
43 Clear: 'static + Send + Sync,
44{
45 type Output<'a>
46 = ()
47 where
48 Self: 'a;
49
50 fn primitive_2d(
51 &mut self,
52 primitive: &Dir2,
53 isometry: impl Into<Isometry2d>,
54 color: impl Into<Color>,
55 ) -> Self::Output<'_> {
56 if !self.enabled {
57 return;
58 }
59 let isometry = isometry.into();
60 let start = Vec2::ZERO;
61 let end = *primitive * MIN_LINE_LEN;
62 self.arrow_2d(isometry * start, isometry * end, color);
63 }
64}
65
66impl<Config, Clear> GizmoPrimitive2d<Arc2d> for GizmoBuffer<Config, Clear>
69where
70 Config: GizmoConfigGroup,
71 Clear: 'static + Send + Sync,
72{
73 type Output<'a>
74 = ()
75 where
76 Self: 'a;
77
78 fn primitive_2d(
79 &mut self,
80 primitive: &Arc2d,
81 isometry: impl Into<Isometry2d>,
82 color: impl Into<Color>,
83 ) -> Self::Output<'_> {
84 if !self.enabled {
85 return;
86 }
87
88 let isometry = isometry.into();
89 let start_iso = isometry * Isometry2d::from_rotation(Rot2::radians(-primitive.half_angle));
90
91 self.arc_2d(
92 start_iso,
93 primitive.half_angle * 2.0,
94 primitive.radius,
95 color,
96 );
97 }
98}
99
100impl<Config, Clear> GizmoPrimitive2d<Circle> for GizmoBuffer<Config, Clear>
103where
104 Config: GizmoConfigGroup,
105 Clear: 'static + Send + Sync,
106{
107 type Output<'a>
108 = crate::circles::Ellipse2dBuilder<'a, Config, Clear>
109 where
110 Self: 'a;
111
112 fn primitive_2d(
113 &mut self,
114 primitive: &Circle,
115 isometry: impl Into<Isometry2d>,
116 color: impl Into<Color>,
117 ) -> Self::Output<'_> {
118 self.circle_2d(isometry, primitive.radius, color)
119 }
120}
121
122impl<Config, Clear> GizmoPrimitive2d<CircularSector> for GizmoBuffer<Config, Clear>
125where
126 Config: GizmoConfigGroup,
127 Clear: 'static + Send + Sync,
128{
129 type Output<'a>
130 = ()
131 where
132 Self: 'a;
133
134 fn primitive_2d(
135 &mut self,
136 primitive: &CircularSector,
137 isometry: impl Into<Isometry2d>,
138 color: impl Into<Color>,
139 ) -> Self::Output<'_> {
140 if !self.enabled {
141 return;
142 }
143
144 let isometry = isometry.into();
145 let color = color.into();
146
147 let start_iso =
148 isometry * Isometry2d::from_rotation(Rot2::radians(-primitive.arc.half_angle));
149 let end_iso = isometry * Isometry2d::from_rotation(Rot2::radians(primitive.arc.half_angle));
150
151 self.arc_2d(
153 start_iso,
154 primitive.arc.half_angle * 2.0,
155 primitive.arc.radius,
156 color,
157 );
158
159 let end_position = primitive.arc.radius * Vec2::Y;
160 self.line_2d(isometry * Vec2::ZERO, start_iso * end_position, color);
161 self.line_2d(isometry * Vec2::ZERO, end_iso * end_position, color);
162 }
163}
164
165impl<Config, Clear> GizmoPrimitive2d<CircularSegment> for GizmoBuffer<Config, Clear>
168where
169 Config: GizmoConfigGroup,
170 Clear: 'static + Send + Sync,
171{
172 type Output<'a>
173 = ()
174 where
175 Self: 'a;
176
177 fn primitive_2d(
178 &mut self,
179 primitive: &CircularSegment,
180 isometry: impl Into<Isometry2d>,
181 color: impl Into<Color>,
182 ) -> Self::Output<'_> {
183 if !self.enabled {
184 return;
185 }
186
187 let isometry = isometry.into();
188 let color = color.into();
189
190 let start_iso =
191 isometry * Isometry2d::from_rotation(Rot2::radians(-primitive.arc.half_angle));
192 let end_iso = isometry * Isometry2d::from_rotation(Rot2::radians(primitive.arc.half_angle));
193
194 self.arc_2d(
196 start_iso,
197 primitive.arc.half_angle * 2.0,
198 primitive.arc.radius,
199 color,
200 );
201
202 let position = primitive.arc.radius * Vec2::Y;
203 self.line_2d(start_iso * position, end_iso * position, color);
204 }
205}
206
207impl<Config, Clear> GizmoPrimitive2d<Ellipse> for GizmoBuffer<Config, Clear>
210where
211 Config: GizmoConfigGroup,
212 Clear: 'static + Send + Sync,
213{
214 type Output<'a>
215 = crate::circles::Ellipse2dBuilder<'a, Config, Clear>
216 where
217 Self: 'a;
218
219 fn primitive_2d<'a>(
220 &mut self,
221 primitive: &Ellipse,
222 isometry: impl Into<Isometry2d>,
223 color: impl Into<Color>,
224 ) -> Self::Output<'_> {
225 self.ellipse_2d(isometry, primitive.half_size, color)
226 }
227}
228
229pub struct Annulus2dBuilder<'a, Config, Clear>
233where
234 Config: GizmoConfigGroup,
235 Clear: 'static + Send + Sync,
236{
237 gizmos: &'a mut GizmoBuffer<Config, Clear>,
238 isometry: Isometry2d,
239 inner_radius: f32,
240 outer_radius: f32,
241 color: Color,
242 inner_resolution: u32,
243 outer_resolution: u32,
244}
245
246impl<Config, Clear> Annulus2dBuilder<'_, Config, Clear>
247where
248 Config: GizmoConfigGroup,
249 Clear: 'static + Send + Sync,
250{
251 pub fn resolution(mut self, resolution: u32) -> Self {
253 self.outer_resolution = resolution;
254 self.inner_resolution = resolution;
255 self
256 }
257
258 pub fn outer_resolution(mut self, resolution: u32) -> Self {
260 self.outer_resolution = resolution;
261 self
262 }
263
264 pub fn inner_resolution(mut self, resolution: u32) -> Self {
266 self.inner_resolution = resolution;
267 self
268 }
269}
270
271impl<Config, Clear> GizmoPrimitive2d<Annulus> for GizmoBuffer<Config, Clear>
272where
273 Config: GizmoConfigGroup,
274 Clear: 'static + Send + Sync,
275{
276 type Output<'a>
277 = Annulus2dBuilder<'a, Config, Clear>
278 where
279 Self: 'a;
280
281 fn primitive_2d(
282 &mut self,
283 primitive: &Annulus,
284 isometry: impl Into<Isometry2d>,
285 color: impl Into<Color>,
286 ) -> Self::Output<'_> {
287 Annulus2dBuilder {
288 gizmos: self,
289 isometry: isometry.into(),
290 inner_radius: primitive.inner_circle.radius,
291 outer_radius: primitive.outer_circle.radius,
292 color: color.into(),
293 inner_resolution: crate::circles::DEFAULT_CIRCLE_RESOLUTION,
294 outer_resolution: crate::circles::DEFAULT_CIRCLE_RESOLUTION,
295 }
296 }
297}
298
299impl<Config, Clear> Drop for Annulus2dBuilder<'_, Config, Clear>
300where
301 Config: GizmoConfigGroup,
302 Clear: 'static + Send + Sync,
303{
304 fn drop(&mut self) {
305 if !self.gizmos.enabled {
306 return;
307 }
308
309 let Annulus2dBuilder {
310 gizmos,
311 isometry,
312 inner_radius,
313 outer_radius,
314 inner_resolution,
315 outer_resolution,
316 color,
317 ..
318 } = self;
319
320 gizmos
321 .circle_2d(*isometry, *outer_radius, *color)
322 .resolution(*outer_resolution);
323 gizmos
324 .circle_2d(*isometry, *inner_radius, *color)
325 .resolution(*inner_resolution);
326 }
327}
328
329impl<Config, Clear> GizmoPrimitive2d<Rhombus> for GizmoBuffer<Config, Clear>
332where
333 Config: GizmoConfigGroup,
334 Clear: 'static + Send + Sync,
335{
336 type Output<'a>
337 = ()
338 where
339 Self: 'a;
340
341 fn primitive_2d(
342 &mut self,
343 primitive: &Rhombus,
344 isometry: impl Into<Isometry2d>,
345 color: impl Into<Color>,
346 ) -> Self::Output<'_> {
347 if !self.enabled {
348 return;
349 };
350 let isometry = isometry.into();
351 let [a, b, c, d] =
352 [(1.0, 0.0), (0.0, 1.0), (-1.0, 0.0), (0.0, -1.0)].map(|(sign_x, sign_y)| {
353 Vec2::new(
354 primitive.half_diagonals.x * sign_x,
355 primitive.half_diagonals.y * sign_y,
356 )
357 });
358 let positions = [a, b, c, d].map(|vec2| isometry * vec2);
359 self.lineloop_2d(positions, color);
360 }
361}
362
363impl<Config, Clear> GizmoPrimitive2d<Capsule2d> for GizmoBuffer<Config, Clear>
366where
367 Config: GizmoConfigGroup,
368 Clear: 'static + Send + Sync,
369{
370 type Output<'a>
371 = ()
372 where
373 Self: 'a;
374
375 fn primitive_2d(
376 &mut self,
377 primitive: &Capsule2d,
378 isometry: impl Into<Isometry2d>,
379 color: impl Into<Color>,
380 ) -> Self::Output<'_> {
381 let isometry = isometry.into();
382 let polymorphic_color: Color = color.into();
383
384 if !self.enabled {
385 return;
386 }
387
388 let [top_left, top_right, bottom_left, bottom_right, top_center, bottom_center] = [
390 [-1.0, 1.0],
391 [1.0, 1.0],
392 [-1.0, -1.0],
393 [1.0, -1.0],
394 [0.0, 1.0],
396 [0.0, -1.0],
397 ]
398 .map(|[sign_x, sign_y]| Vec2::X * sign_x + Vec2::Y * sign_y)
399 .map(|reference_point| {
400 let scaling = Vec2::X * primitive.radius + Vec2::Y * primitive.half_length;
401 reference_point * scaling
402 })
403 .map(|vec2| isometry * vec2);
404
405 self.line_2d(bottom_left, top_left, polymorphic_color);
407 self.line_2d(bottom_right, top_right, polymorphic_color);
408
409 let start_angle_top = isometry.rotation.as_radians() - FRAC_PI_2;
410 let start_angle_bottom = isometry.rotation.as_radians() + FRAC_PI_2;
411
412 self.arc_2d(
414 Isometry2d::new(top_center, Rot2::radians(start_angle_top)),
415 PI,
416 primitive.radius,
417 polymorphic_color,
418 );
419 self.arc_2d(
420 Isometry2d::new(bottom_center, Rot2::radians(start_angle_bottom)),
421 PI,
422 primitive.radius,
423 polymorphic_color,
424 );
425 }
426}
427
428pub struct Line2dBuilder<'a, Config, Clear>
432where
433 Config: GizmoConfigGroup,
434 Clear: 'static + Send + Sync,
435{
436 gizmos: &'a mut GizmoBuffer<Config, Clear>,
437
438 direction: Dir2, isometry: Isometry2d,
441 color: Color, draw_arrow: bool, }
445
446impl<Config, Clear> Line2dBuilder<'_, Config, Clear>
447where
448 Config: GizmoConfigGroup,
449 Clear: 'static + Send + Sync,
450{
451 pub fn draw_arrow(mut self, is_enabled: bool) -> Self {
453 self.draw_arrow = is_enabled;
454 self
455 }
456}
457
458impl<Config, Clear> GizmoPrimitive2d<Line2d> for GizmoBuffer<Config, Clear>
459where
460 Config: GizmoConfigGroup,
461 Clear: 'static + Send + Sync,
462{
463 type Output<'a>
464 = Line2dBuilder<'a, Config, Clear>
465 where
466 Self: 'a;
467
468 fn primitive_2d(
469 &mut self,
470 primitive: &Line2d,
471 isometry: impl Into<Isometry2d>,
472 color: impl Into<Color>,
473 ) -> Self::Output<'_> {
474 Line2dBuilder {
475 gizmos: self,
476 direction: primitive.direction,
477 isometry: isometry.into(),
478 color: color.into(),
479 draw_arrow: false,
480 }
481 }
482}
483
484impl<Config, Clear> Drop for Line2dBuilder<'_, Config, Clear>
485where
486 Config: GizmoConfigGroup,
487 Clear: 'static + Send + Sync,
488{
489 fn drop(&mut self) {
490 if !self.gizmos.enabled {
491 return;
492 }
493
494 let [start, end] = [1.0, -1.0]
495 .map(|sign| sign * INFINITE_LEN)
496 .map(|length| self.direction * length)
498 .map(|offset| self.isometry * offset);
500
501 self.gizmos.line_2d(start, end, self.color);
502
503 if self.draw_arrow {
505 self.gizmos.arrow_2d(
506 self.isometry * (-self.direction * MIN_LINE_LEN),
507 self.isometry * Vec2::ZERO,
508 self.color,
509 );
510 }
511 }
512}
513
514impl<Config, Clear> GizmoPrimitive2d<Plane2d> for GizmoBuffer<Config, Clear>
517where
518 Config: GizmoConfigGroup,
519 Clear: 'static + Send + Sync,
520{
521 type Output<'a>
522 = ()
523 where
524 Self: 'a;
525
526 fn primitive_2d(
527 &mut self,
528 primitive: &Plane2d,
529 isometry: impl Into<Isometry2d>,
530 color: impl Into<Color>,
531 ) -> Self::Output<'_> {
532 let isometry = isometry.into();
533 let polymorphic_color: Color = color.into();
534
535 if !self.enabled {
536 return;
537 }
538 let normal = primitive.normal;
540 let normal_segment = Segment2d::from_direction_and_length(normal, HALF_MIN_LINE_LEN * 2.);
541 self.primitive_2d(
542 &normal_segment,
543 Isometry2d::new(isometry * (HALF_MIN_LINE_LEN * normal), isometry.rotation),
545 polymorphic_color,
546 )
547 .draw_arrow(true);
548
549 let direction = -normal.perpendicular();
551
552 self.primitive_2d(&Line2d { direction }, isometry, polymorphic_color)
553 .draw_arrow(false);
554
555 self.arrow_2d(
558 isometry * Vec2::ZERO,
559 isometry * (MIN_LINE_LEN * direction),
560 polymorphic_color,
561 );
562 }
563}
564
565pub struct Segment2dBuilder<'a, Config, Clear>
569where
570 Config: GizmoConfigGroup,
571 Clear: 'static + Send + Sync,
572{
573 gizmos: &'a mut GizmoBuffer<Config, Clear>,
574
575 point1: Vec2, point2: Vec2, isometry: Isometry2d, color: Color, draw_arrow: bool, }
583
584impl<Config, Clear> Segment2dBuilder<'_, Config, Clear>
585where
586 Config: GizmoConfigGroup,
587 Clear: 'static + Send + Sync,
588{
589 pub fn draw_arrow(mut self, is_enabled: bool) -> Self {
591 self.draw_arrow = is_enabled;
592 self
593 }
594}
595
596impl<Config, Clear> GizmoPrimitive2d<Segment2d> for GizmoBuffer<Config, Clear>
597where
598 Config: GizmoConfigGroup,
599 Clear: 'static + Send + Sync,
600{
601 type Output<'a>
602 = Segment2dBuilder<'a, Config, Clear>
603 where
604 Self: 'a;
605
606 fn primitive_2d(
607 &mut self,
608 primitive: &Segment2d,
609 isometry: impl Into<Isometry2d>,
610 color: impl Into<Color>,
611 ) -> Self::Output<'_> {
612 Segment2dBuilder {
613 gizmos: self,
614 point1: primitive.point1(),
615 point2: primitive.point2(),
616
617 isometry: isometry.into(),
618 color: color.into(),
619
620 draw_arrow: Default::default(),
621 }
622 }
623}
624
625impl<Config, Clear> Drop for Segment2dBuilder<'_, Config, Clear>
626where
627 Config: GizmoConfigGroup,
628 Clear: 'static + Send + Sync,
629{
630 fn drop(&mut self) {
631 if !self.gizmos.enabled {
632 return;
633 }
634
635 let segment = Segment2d::new(self.point1, self.point2).transformed(self.isometry);
636
637 if self.draw_arrow {
638 self.gizmos
639 .arrow_2d(segment.point1(), segment.point2(), self.color);
640 } else {
641 self.gizmos
642 .line_2d(segment.point1(), segment.point2(), self.color);
643 }
644 }
645}
646
647impl<Config, Clear> GizmoPrimitive2d<Polyline2d> for GizmoBuffer<Config, Clear>
650where
651 Config: GizmoConfigGroup,
652 Clear: 'static + Send + Sync,
653{
654 type Output<'a>
655 = ()
656 where
657 Self: 'a;
658
659 fn primitive_2d(
660 &mut self,
661 primitive: &Polyline2d,
662 isometry: impl Into<Isometry2d>,
663 color: impl Into<Color>,
664 ) -> Self::Output<'_> {
665 if !self.enabled {
666 return;
667 }
668
669 let isometry = isometry.into();
670
671 self.linestrip_2d(
672 primitive
673 .vertices
674 .iter()
675 .copied()
676 .map(|vec2| isometry * vec2),
677 color,
678 );
679 }
680}
681
682impl<Config, Clear> GizmoPrimitive2d<Triangle2d> for GizmoBuffer<Config, Clear>
685where
686 Config: GizmoConfigGroup,
687 Clear: 'static + Send + Sync,
688{
689 type Output<'a>
690 = ()
691 where
692 Self: 'a;
693
694 fn primitive_2d(
695 &mut self,
696 primitive: &Triangle2d,
697 isometry: impl Into<Isometry2d>,
698 color: impl Into<Color>,
699 ) -> Self::Output<'_> {
700 if !self.enabled {
701 return;
702 }
703
704 let isometry = isometry.into();
705
706 let [a, b, c] = primitive.vertices;
707 let positions = [a, b, c].map(|vec2| isometry * vec2);
708 self.lineloop_2d(positions, color);
709 }
710}
711
712impl<Config, Clear> GizmoPrimitive2d<Rectangle> for GizmoBuffer<Config, Clear>
715where
716 Config: GizmoConfigGroup,
717 Clear: 'static + Send + Sync,
718{
719 type Output<'a>
720 = ()
721 where
722 Self: 'a;
723
724 fn primitive_2d(
725 &mut self,
726 primitive: &Rectangle,
727 isometry: impl Into<Isometry2d>,
728 color: impl Into<Color>,
729 ) -> Self::Output<'_> {
730 if !self.enabled {
731 return;
732 }
733
734 let isometry = isometry.into();
735
736 let [a, b, c, d] =
737 [(1.0, 1.0), (1.0, -1.0), (-1.0, -1.0), (-1.0, 1.0)].map(|(sign_x, sign_y)| {
738 Vec2::new(
739 primitive.half_size.x * sign_x,
740 primitive.half_size.y * sign_y,
741 )
742 });
743 let positions = [a, b, c, d].map(|vec2| isometry * vec2);
744 self.lineloop_2d(positions, color);
745 }
746}
747
748impl<Config, Clear> GizmoPrimitive2d<Polygon> for GizmoBuffer<Config, Clear>
751where
752 Config: GizmoConfigGroup,
753 Clear: 'static + Send + Sync,
754{
755 type Output<'a>
756 = ()
757 where
758 Self: 'a;
759
760 fn primitive_2d(
761 &mut self,
762 primitive: &Polygon,
763 isometry: impl Into<Isometry2d>,
764 color: impl Into<Color>,
765 ) -> Self::Output<'_> {
766 if !self.enabled {
767 return;
768 }
769
770 let isometry = isometry.into();
771
772 let vertices = if primitive.vertices.first() == primitive.vertices.last() {
773 &primitive.vertices[..primitive.vertices.len() - 1]
775 } else {
776 &primitive.vertices[..]
777 };
778
779 self.lineloop_2d(vertices.iter().map(|&vec2| isometry * vec2), color);
780 }
781}
782
783impl<Config, Clear> GizmoPrimitive2d<RegularPolygon> for GizmoBuffer<Config, Clear>
786where
787 Config: GizmoConfigGroup,
788 Clear: 'static + Send + Sync,
789{
790 type Output<'a>
791 = ()
792 where
793 Self: 'a;
794
795 fn primitive_2d(
796 &mut self,
797 primitive: &RegularPolygon,
798 isometry: impl Into<Isometry2d>,
799 color: impl Into<Color>,
800 ) -> Self::Output<'_> {
801 if !self.enabled {
802 return;
803 }
804
805 let isometry = isometry.into();
806
807 let points = (0..primitive.sides)
808 .map(|n| single_circle_coordinate(primitive.circumcircle.radius, primitive.sides, n))
809 .map(|vec2| isometry * vec2);
810 self.lineloop_2d(points, color);
811 }
812}