1use core::f32::consts::{FRAC_1_SQRT_2, FRAC_PI_2, FRAC_PI_3, PI};
2use derive_more::derive::From;
3#[cfg(feature = "alloc")]
4use thiserror::Error;
5
6use crate::{Inset, Measured2d, Primitive2d, Ray2d, WindingOrder};
7use bevy_math::{
8 ops::{self, FloatPow},
9 Dir2, InvalidDirectionError, Isometry2d, Rot2, Vec2,
10};
11
12#[cfg(feature = "alloc")]
13use super::polygon::is_polygon_simple;
14
15#[cfg(feature = "bevy_reflect")]
16use bevy_reflect::{std_traits::ReflectDefault, Reflect};
17#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
18use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
19
20#[cfg(feature = "alloc")]
21use alloc::vec::Vec;
22
23#[derive(Clone, Copy, Debug, PartialEq)]
25#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
26#[cfg_attr(
27 feature = "bevy_reflect",
28 derive(Reflect),
29 reflect(Debug, PartialEq, Default, Clone)
30)]
31#[cfg_attr(
32 all(feature = "serialize", feature = "bevy_reflect"),
33 reflect(Serialize, Deserialize)
34)]
35pub struct Circle {
36 pub radius: f32,
38}
39
40impl Primitive2d for Circle {}
41
42impl Default for Circle {
43 fn default() -> Self {
45 Self { radius: 0.5 }
46 }
47}
48
49impl Circle {
50 #[inline]
52 pub const fn new(radius: f32) -> Self {
53 Self { radius }
54 }
55
56 #[inline]
58 pub const fn diameter(&self) -> f32 {
59 2.0 * self.radius
60 }
61
62 #[inline]
67 pub fn closest_point(&self, point: Vec2) -> Vec2 {
68 let distance_squared = point.length_squared();
69
70 if distance_squared <= self.radius.squared() {
71 point
73 } else {
74 let dir_to_point = point / ops::sqrt(distance_squared);
77 self.radius * dir_to_point
78 }
79 }
80}
81
82impl Measured2d for Circle {
83 #[inline]
85 fn area(&self) -> f32 {
86 PI * self.radius.squared()
87 }
88
89 #[inline]
91 #[doc(alias = "circumference")]
92 fn perimeter(&self) -> f32 {
93 2.0 * PI * self.radius
94 }
95}
96
97#[derive(Clone, Copy, Debug, PartialEq)]
112#[doc(alias("CircularArc", "CircleArc"))]
113#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
114#[cfg_attr(
115 feature = "bevy_reflect",
116 derive(Reflect),
117 reflect(Debug, PartialEq, Default, Clone)
118)]
119#[cfg_attr(
120 all(feature = "serialize", feature = "bevy_reflect"),
121 reflect(Serialize, Deserialize)
122)]
123pub struct Arc2d {
124 pub radius: f32,
126 pub half_angle: f32,
128}
129
130impl Primitive2d for Arc2d {}
131
132impl Default for Arc2d {
133 fn default() -> Self {
135 Self {
136 radius: 0.5,
137 half_angle: 2.0 * FRAC_PI_3,
138 }
139 }
140}
141
142impl Arc2d {
143 #[inline]
145 pub const fn new(radius: f32, half_angle: f32) -> Self {
146 Self { radius, half_angle }
147 }
148
149 #[inline]
151 pub const fn from_radians(radius: f32, angle: f32) -> Self {
152 Self {
153 radius,
154 half_angle: angle / 2.0,
155 }
156 }
157
158 #[inline]
160 pub const fn from_degrees(radius: f32, angle: f32) -> Self {
161 Self {
162 radius,
163 half_angle: angle.to_radians() / 2.0,
164 }
165 }
166
167 #[inline]
171 pub const fn from_turns(radius: f32, fraction: f32) -> Self {
172 Self {
173 radius,
174 half_angle: fraction * PI,
175 }
176 }
177
178 #[inline]
180 pub const fn angle(&self) -> f32 {
181 self.half_angle * 2.0
182 }
183
184 #[inline]
186 pub const fn length(&self) -> f32 {
187 self.angle() * self.radius
188 }
189
190 #[inline]
192 pub fn right_endpoint(&self) -> Vec2 {
193 self.radius * Vec2::from_angle(FRAC_PI_2 - self.half_angle)
194 }
195
196 #[inline]
198 pub fn left_endpoint(&self) -> Vec2 {
199 self.radius * Vec2::from_angle(FRAC_PI_2 + self.half_angle)
200 }
201
202 #[inline]
204 pub fn endpoints(&self) -> [Vec2; 2] {
205 [self.left_endpoint(), self.right_endpoint()]
206 }
207
208 #[inline]
210 pub fn midpoint(&self) -> Vec2 {
211 self.radius * Vec2::Y
212 }
213
214 #[inline]
216 pub fn half_chord_length(&self) -> f32 {
217 self.radius * ops::sin(self.half_angle)
218 }
219
220 #[inline]
222 pub fn chord_length(&self) -> f32 {
223 2.0 * self.half_chord_length()
224 }
225
226 #[inline]
228 pub fn chord_midpoint(&self) -> Vec2 {
229 self.apothem() * Vec2::Y
230 }
231
232 #[inline]
238 pub fn apothem(&self) -> f32 {
242 let sign = if self.is_minor() { 1.0 } else { -1.0 };
243 sign * ops::sqrt(self.radius.squared() - self.half_chord_length().squared())
244 }
245
246 pub fn sagitta(&self) -> f32 {
252 self.radius - self.apothem()
253 }
254
255 #[inline]
259 pub const fn is_minor(&self) -> bool {
260 self.half_angle <= FRAC_PI_2
261 }
262
263 #[inline]
267 pub const fn is_major(&self) -> bool {
268 self.half_angle >= FRAC_PI_2
269 }
270}
271
272#[derive(Clone, Copy, Debug, PartialEq, From)]
281#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
282#[cfg_attr(
283 feature = "bevy_reflect",
284 derive(Reflect),
285 reflect(Debug, PartialEq, Default, Clone)
286)]
287#[cfg_attr(
288 all(feature = "serialize", feature = "bevy_reflect"),
289 reflect(Serialize, Deserialize)
290)]
291pub struct CircularSector {
292 #[cfg_attr(all(feature = "serialize", feature = "alloc"), serde(flatten))]
294 pub arc: Arc2d,
295}
296
297impl Primitive2d for CircularSector {}
298
299impl Default for CircularSector {
300 fn default() -> Self {
302 Self::from(Arc2d::default())
303 }
304}
305
306impl Measured2d for CircularSector {
307 #[inline]
308 fn area(&self) -> f32 {
309 self.arc.radius.squared() * self.arc.half_angle
310 }
311
312 #[inline]
313 fn perimeter(&self) -> f32 {
314 if self.half_angle() >= PI {
315 self.arc.radius * 2.0 * PI
316 } else {
317 2.0 * self.radius() + self.arc_length()
318 }
319 }
320}
321
322impl CircularSector {
323 #[inline]
325 pub const fn new(radius: f32, angle: f32) -> Self {
326 Self {
327 arc: Arc2d::new(radius, angle),
328 }
329 }
330
331 #[inline]
333 pub const fn from_radians(radius: f32, angle: f32) -> Self {
334 Self {
335 arc: Arc2d::from_radians(radius, angle),
336 }
337 }
338
339 #[inline]
341 pub const fn from_degrees(radius: f32, angle: f32) -> Self {
342 Self {
343 arc: Arc2d::from_degrees(radius, angle),
344 }
345 }
346
347 #[inline]
351 pub const fn from_turns(radius: f32, fraction: f32) -> Self {
352 Self {
353 arc: Arc2d::from_turns(radius, fraction),
354 }
355 }
356
357 #[inline]
359 pub const fn half_angle(&self) -> f32 {
360 self.arc.half_angle
361 }
362
363 #[inline]
365 pub const fn angle(&self) -> f32 {
366 self.arc.angle()
367 }
368
369 #[inline]
371 pub const fn radius(&self) -> f32 {
372 self.arc.radius
373 }
374
375 #[inline]
377 pub const fn arc_length(&self) -> f32 {
378 self.arc.length()
379 }
380
381 #[inline]
385 pub fn half_chord_length(&self) -> f32 {
386 self.arc.half_chord_length()
387 }
388
389 #[inline]
393 pub fn chord_length(&self) -> f32 {
394 self.arc.chord_length()
395 }
396
397 #[inline]
401 pub fn chord_midpoint(&self) -> Vec2 {
402 self.arc.chord_midpoint()
403 }
404
405 #[inline]
409 pub fn apothem(&self) -> f32 {
410 self.arc.apothem()
411 }
412
413 #[inline]
417 pub fn sagitta(&self) -> f32 {
418 self.arc.sagitta()
419 }
420}
421
422#[derive(Clone, Copy, Debug, PartialEq, From)]
433#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
434#[cfg_attr(
435 feature = "bevy_reflect",
436 derive(Reflect),
437 reflect(Debug, PartialEq, Default, Clone)
438)]
439#[cfg_attr(
440 all(feature = "serialize", feature = "bevy_reflect"),
441 reflect(Serialize, Deserialize)
442)]
443pub struct CircularSegment {
444 #[cfg_attr(all(feature = "serialize", feature = "alloc"), serde(flatten))]
446 pub arc: Arc2d,
447}
448
449impl Primitive2d for CircularSegment {}
450
451impl Default for CircularSegment {
452 fn default() -> Self {
454 Self::from(Arc2d::default())
455 }
456}
457
458impl Measured2d for CircularSegment {
459 #[inline]
460 fn area(&self) -> f32 {
461 0.5 * self.arc.radius.squared() * (self.arc.angle() - ops::sin(self.arc.angle()))
462 }
463
464 #[inline]
465 fn perimeter(&self) -> f32 {
466 self.chord_length() + self.arc_length()
467 }
468}
469
470impl CircularSegment {
471 #[inline]
473 pub const fn new(radius: f32, half_angle: f32) -> Self {
474 Self {
475 arc: Arc2d::new(radius, half_angle),
476 }
477 }
478
479 #[inline]
481 pub const fn from_radians(radius: f32, angle: f32) -> Self {
482 Self {
483 arc: Arc2d::from_radians(radius, angle),
484 }
485 }
486
487 #[inline]
489 pub const fn from_degrees(radius: f32, angle: f32) -> Self {
490 Self {
491 arc: Arc2d::from_degrees(radius, angle),
492 }
493 }
494
495 #[inline]
499 pub const fn from_turns(radius: f32, fraction: f32) -> Self {
500 Self {
501 arc: Arc2d::from_turns(radius, fraction),
502 }
503 }
504
505 #[inline]
507 pub const fn half_angle(&self) -> f32 {
508 self.arc.half_angle
509 }
510
511 #[inline]
513 pub const fn angle(&self) -> f32 {
514 self.arc.angle()
515 }
516
517 #[inline]
519 pub const fn radius(&self) -> f32 {
520 self.arc.radius
521 }
522
523 #[inline]
525 pub const fn arc_length(&self) -> f32 {
526 self.arc.length()
527 }
528
529 #[inline]
531 #[doc(alias = "half_base_length")]
532 pub fn half_chord_length(&self) -> f32 {
533 self.arc.half_chord_length()
534 }
535
536 #[inline]
538 #[doc(alias = "base_length")]
539 #[doc(alias = "base")]
540 pub fn chord_length(&self) -> f32 {
541 self.arc.chord_length()
542 }
543
544 #[inline]
546 #[doc(alias = "base_midpoint")]
547 pub fn chord_midpoint(&self) -> Vec2 {
548 self.arc.chord_midpoint()
549 }
550
551 #[inline]
556 pub fn apothem(&self) -> f32 {
557 self.arc.apothem()
558 }
559
560 #[inline]
564 #[doc(alias = "height")]
565 pub fn sagitta(&self) -> f32 {
566 self.arc.sagitta()
567 }
568}
569
570#[cfg(test)]
571mod arc_tests {
572 use core::f32::consts::FRAC_PI_4;
573 use core::f32::consts::SQRT_2;
574
575 use approx::assert_abs_diff_eq;
576
577 use super::*;
578
579 struct ArcTestCase {
580 radius: f32,
581 half_angle: f32,
582 angle: f32,
583 length: f32,
584 right_endpoint: Vec2,
585 left_endpoint: Vec2,
586 endpoints: [Vec2; 2],
587 midpoint: Vec2,
588 half_chord_length: f32,
589 chord_length: f32,
590 chord_midpoint: Vec2,
591 apothem: f32,
592 sagitta: f32,
593 is_minor: bool,
594 is_major: bool,
595 sector_area: f32,
596 sector_perimeter: f32,
597 segment_area: f32,
598 segment_perimeter: f32,
599 }
600
601 impl ArcTestCase {
602 fn check_arc(&self, arc: Arc2d) {
603 assert_abs_diff_eq!(self.radius, arc.radius);
604 assert_abs_diff_eq!(self.half_angle, arc.half_angle);
605 assert_abs_diff_eq!(self.angle, arc.angle());
606 assert_abs_diff_eq!(self.length, arc.length());
607 assert_abs_diff_eq!(self.right_endpoint, arc.right_endpoint());
608 assert_abs_diff_eq!(self.left_endpoint, arc.left_endpoint());
609 assert_abs_diff_eq!(self.endpoints[0], arc.endpoints()[0]);
610 assert_abs_diff_eq!(self.endpoints[1], arc.endpoints()[1]);
611 assert_abs_diff_eq!(self.midpoint, arc.midpoint());
612 assert_abs_diff_eq!(self.half_chord_length, arc.half_chord_length());
613 assert_abs_diff_eq!(self.chord_length, arc.chord_length(), epsilon = 0.00001);
614 assert_abs_diff_eq!(self.chord_midpoint, arc.chord_midpoint());
615 assert_abs_diff_eq!(self.apothem, arc.apothem());
616 assert_abs_diff_eq!(self.sagitta, arc.sagitta());
617 assert_eq!(self.is_minor, arc.is_minor());
618 assert_eq!(self.is_major, arc.is_major());
619 }
620
621 fn check_sector(&self, sector: CircularSector) {
622 assert_abs_diff_eq!(self.radius, sector.radius());
623 assert_abs_diff_eq!(self.half_angle, sector.half_angle());
624 assert_abs_diff_eq!(self.angle, sector.angle());
625 assert_abs_diff_eq!(self.half_chord_length, sector.half_chord_length());
626 assert_abs_diff_eq!(self.chord_length, sector.chord_length(), epsilon = 0.00001);
627 assert_abs_diff_eq!(self.chord_midpoint, sector.chord_midpoint());
628 assert_abs_diff_eq!(self.apothem, sector.apothem());
629 assert_abs_diff_eq!(self.sagitta, sector.sagitta());
630 assert_abs_diff_eq!(self.sector_area, sector.area());
631 assert_abs_diff_eq!(self.sector_perimeter, sector.perimeter());
632 }
633
634 fn check_segment(&self, segment: CircularSegment) {
635 assert_abs_diff_eq!(self.radius, segment.radius());
636 assert_abs_diff_eq!(self.half_angle, segment.half_angle());
637 assert_abs_diff_eq!(self.angle, segment.angle());
638 assert_abs_diff_eq!(self.half_chord_length, segment.half_chord_length());
639 assert_abs_diff_eq!(self.chord_length, segment.chord_length(), epsilon = 0.00001);
640 assert_abs_diff_eq!(self.chord_midpoint, segment.chord_midpoint());
641 assert_abs_diff_eq!(self.apothem, segment.apothem());
642 assert_abs_diff_eq!(self.sagitta, segment.sagitta());
643 assert_abs_diff_eq!(self.segment_area, segment.area());
644 assert_abs_diff_eq!(self.segment_perimeter, segment.perimeter());
645 }
646 }
647
648 #[test]
649 fn zero_angle() {
650 let tests = ArcTestCase {
651 radius: 1.0,
652 half_angle: 0.0,
653 angle: 0.0,
654 length: 0.0,
655 left_endpoint: Vec2::Y,
656 right_endpoint: Vec2::Y,
657 endpoints: [Vec2::Y, Vec2::Y],
658 midpoint: Vec2::Y,
659 half_chord_length: 0.0,
660 chord_length: 0.0,
661 chord_midpoint: Vec2::Y,
662 apothem: 1.0,
663 sagitta: 0.0,
664 is_minor: true,
665 is_major: false,
666 sector_area: 0.0,
667 sector_perimeter: 2.0,
668 segment_area: 0.0,
669 segment_perimeter: 0.0,
670 };
671
672 tests.check_arc(Arc2d::new(1.0, 0.0));
673 tests.check_sector(CircularSector::new(1.0, 0.0));
674 tests.check_segment(CircularSegment::new(1.0, 0.0));
675 }
676
677 #[test]
678 fn zero_radius() {
679 let tests = ArcTestCase {
680 radius: 0.0,
681 half_angle: FRAC_PI_4,
682 angle: FRAC_PI_2,
683 length: 0.0,
684 left_endpoint: Vec2::ZERO,
685 right_endpoint: Vec2::ZERO,
686 endpoints: [Vec2::ZERO, Vec2::ZERO],
687 midpoint: Vec2::ZERO,
688 half_chord_length: 0.0,
689 chord_length: 0.0,
690 chord_midpoint: Vec2::ZERO,
691 apothem: 0.0,
692 sagitta: 0.0,
693 is_minor: true,
694 is_major: false,
695 sector_area: 0.0,
696 sector_perimeter: 0.0,
697 segment_area: 0.0,
698 segment_perimeter: 0.0,
699 };
700
701 tests.check_arc(Arc2d::new(0.0, FRAC_PI_4));
702 tests.check_sector(CircularSector::new(0.0, FRAC_PI_4));
703 tests.check_segment(CircularSegment::new(0.0, FRAC_PI_4));
704 }
705
706 #[test]
707 fn quarter_circle() {
708 let sqrt_half: f32 = ops::sqrt(0.5);
709 let tests = ArcTestCase {
710 radius: 1.0,
711 half_angle: FRAC_PI_4,
712 angle: FRAC_PI_2,
713 length: FRAC_PI_2,
714 left_endpoint: Vec2::new(-sqrt_half, sqrt_half),
715 right_endpoint: Vec2::splat(sqrt_half),
716 endpoints: [Vec2::new(-sqrt_half, sqrt_half), Vec2::splat(sqrt_half)],
717 midpoint: Vec2::Y,
718 half_chord_length: sqrt_half,
719 chord_length: ops::sqrt(2.0),
720 chord_midpoint: Vec2::new(0.0, sqrt_half),
721 apothem: sqrt_half,
722 sagitta: 1.0 - sqrt_half,
723 is_minor: true,
724 is_major: false,
725 sector_area: FRAC_PI_4,
726 sector_perimeter: FRAC_PI_2 + 2.0,
727 segment_area: FRAC_PI_4 - 0.5,
728 segment_perimeter: FRAC_PI_2 + SQRT_2,
729 };
730
731 tests.check_arc(Arc2d::from_turns(1.0, 0.25));
732 tests.check_sector(CircularSector::from_turns(1.0, 0.25));
733 tests.check_segment(CircularSegment::from_turns(1.0, 0.25));
734 }
735
736 #[test]
737 fn half_circle() {
738 let tests = ArcTestCase {
739 radius: 1.0,
740 half_angle: FRAC_PI_2,
741 angle: PI,
742 length: PI,
743 left_endpoint: Vec2::NEG_X,
744 right_endpoint: Vec2::X,
745 endpoints: [Vec2::NEG_X, Vec2::X],
746 midpoint: Vec2::Y,
747 half_chord_length: 1.0,
748 chord_length: 2.0,
749 chord_midpoint: Vec2::ZERO,
750 apothem: 0.0,
751 sagitta: 1.0,
752 is_minor: true,
753 is_major: true,
754 sector_area: FRAC_PI_2,
755 sector_perimeter: PI + 2.0,
756 segment_area: FRAC_PI_2,
757 segment_perimeter: PI + 2.0,
758 };
759
760 tests.check_arc(Arc2d::from_radians(1.0, PI));
761 tests.check_sector(CircularSector::from_radians(1.0, PI));
762 tests.check_segment(CircularSegment::from_radians(1.0, PI));
763 }
764
765 #[test]
766 fn full_circle() {
767 let tests = ArcTestCase {
768 radius: 1.0,
769 half_angle: PI,
770 angle: 2.0 * PI,
771 length: 2.0 * PI,
772 left_endpoint: Vec2::NEG_Y,
773 right_endpoint: Vec2::NEG_Y,
774 endpoints: [Vec2::NEG_Y, Vec2::NEG_Y],
775 midpoint: Vec2::Y,
776 half_chord_length: 0.0,
777 chord_length: 0.0,
778 chord_midpoint: Vec2::NEG_Y,
779 apothem: -1.0,
780 sagitta: 2.0,
781 is_minor: false,
782 is_major: true,
783 sector_area: PI,
784 sector_perimeter: 2.0 * PI,
785 segment_area: PI,
786 segment_perimeter: 2.0 * PI,
787 };
788
789 tests.check_arc(Arc2d::from_degrees(1.0, 360.0));
790 tests.check_sector(CircularSector::from_degrees(1.0, 360.0));
791 tests.check_segment(CircularSegment::from_degrees(1.0, 360.0));
792 }
793}
794
795#[derive(Clone, Copy, Debug, PartialEq)]
800#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
801#[cfg_attr(
802 feature = "bevy_reflect",
803 derive(Reflect),
804 reflect(Debug, PartialEq, Default, Clone)
805)]
806#[cfg_attr(
807 all(feature = "serialize", feature = "bevy_reflect"),
808 reflect(Serialize, Deserialize)
809)]
810pub struct Ellipse {
811 pub half_size: Vec2,
815}
816
817impl Primitive2d for Ellipse {}
818
819impl Default for Ellipse {
820 fn default() -> Self {
822 Self {
823 half_size: Vec2::new(1.0, 0.5),
824 }
825 }
826}
827
828impl Ellipse {
829 #[inline]
833 pub const fn new(half_width: f32, half_height: f32) -> Self {
834 Self {
835 half_size: Vec2::new(half_width, half_height),
836 }
837 }
838
839 #[inline]
843 pub const fn from_size(size: Vec2) -> Self {
844 Self {
845 half_size: Vec2::new(size.x / 2.0, size.y / 2.0),
846 }
847 }
848
849 #[inline]
850 pub fn eccentricity(&self) -> f32 {
855 let a = self.semi_major();
856 let b = self.semi_minor();
857
858 ops::sqrt(a * a - b * b) / a
859 }
860
861 #[inline]
862 pub fn focal_length(&self) -> f32 {
866 let a = self.semi_major();
867 let b = self.semi_minor();
868
869 ops::sqrt(a * a - b * b)
870 }
871
872 #[inline]
874 pub fn semi_major(&self) -> f32 {
875 self.half_size.max_element()
876 }
877
878 #[inline]
880 pub fn semi_minor(&self) -> f32 {
881 self.half_size.min_element()
882 }
883}
884
885impl Measured2d for Ellipse {
886 #[inline]
888 fn area(&self) -> f32 {
889 PI * self.half_size.x * self.half_size.y
890 }
891
892 #[inline]
893 fn perimeter(&self) -> f32 {
897 let a = self.semi_major();
898 let b = self.semi_minor();
899
900 if a / b - 1. < 1e-5 {
902 return PI * (a + b);
903 };
904
905 if a / b > 1e4 {
907 return 4. * a;
908 };
909
910 const BINOMIAL_COEFFICIENTS: [f32; 21] = [
914 1.,
915 0.25,
916 0.015625,
917 0.00390625,
918 0.0015258789,
919 0.00074768066,
920 0.00042057037,
921 0.00025963783,
922 0.00017140154,
923 0.000119028846,
924 0.00008599834,
925 0.00006414339,
926 0.000049109784,
927 0.000038430585,
928 0.000030636627,
929 0.000024815668,
930 0.000020380836,
931 0.000016942893,
932 0.000014236736,
933 0.000012077564,
934 0.000010333865,
935 ];
936
937 let h = ((a - b) / (a + b)).squared();
941
942 PI * (a + b)
943 * (0..=20)
944 .map(|i| BINOMIAL_COEFFICIENTS[i] * ops::powf(h, i as f32))
945 .sum::<f32>()
946 }
947}
948
949#[derive(Clone, Copy, Debug, PartialEq)]
951#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
952#[cfg_attr(
953 feature = "bevy_reflect",
954 derive(Reflect),
955 reflect(Debug, PartialEq, Default, Clone)
956)]
957#[cfg_attr(
958 all(feature = "serialize", feature = "bevy_reflect"),
959 reflect(Serialize, Deserialize)
960)]
961#[doc(alias = "Ring")]
962pub struct Annulus {
963 pub inner_circle: Circle,
965 pub outer_circle: Circle,
967}
968
969impl Primitive2d for Annulus {}
970
971impl Default for Annulus {
972 fn default() -> Self {
974 Self {
975 inner_circle: Circle::new(0.5),
976 outer_circle: Circle::new(1.0),
977 }
978 }
979}
980
981impl Annulus {
982 #[inline]
984 pub const fn new(inner_radius: f32, outer_radius: f32) -> Self {
985 Self {
986 inner_circle: Circle::new(inner_radius),
987 outer_circle: Circle::new(outer_radius),
988 }
989 }
990
991 #[inline]
993 pub const fn diameter(&self) -> f32 {
994 self.outer_circle.diameter()
995 }
996
997 #[inline]
999 pub const fn thickness(&self) -> f32 {
1000 self.outer_circle.radius - self.inner_circle.radius
1001 }
1002
1003 #[inline]
1009 pub fn closest_point(&self, point: Vec2) -> Vec2 {
1010 let distance_squared = point.length_squared();
1011
1012 if self.inner_circle.radius.squared() <= distance_squared {
1013 if distance_squared <= self.outer_circle.radius.squared() {
1014 point
1016 } else {
1017 let dir_to_point = point / ops::sqrt(distance_squared);
1020 self.outer_circle.radius * dir_to_point
1021 }
1022 } else {
1023 let dir_to_point = point / ops::sqrt(distance_squared);
1026 self.inner_circle.radius * dir_to_point
1027 }
1028 }
1029}
1030
1031impl Measured2d for Annulus {
1032 #[inline]
1034 fn area(&self) -> f32 {
1035 PI * (self.outer_circle.radius.squared() - self.inner_circle.radius.squared())
1036 }
1037
1038 #[inline]
1041 #[doc(alias = "circumference")]
1042 fn perimeter(&self) -> f32 {
1043 2.0 * PI * (self.outer_circle.radius + self.inner_circle.radius)
1044 }
1045}
1046
1047#[derive(Clone, Copy, Debug, PartialEq)]
1051#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1052#[cfg_attr(
1053 feature = "bevy_reflect",
1054 derive(Reflect),
1055 reflect(Debug, PartialEq, Default, Clone)
1056)]
1057#[cfg_attr(
1058 all(feature = "serialize", feature = "bevy_reflect"),
1059 reflect(Serialize, Deserialize)
1060)]
1061#[doc(alias = "Diamond")]
1062pub struct Rhombus {
1063 pub half_diagonals: Vec2,
1065}
1066
1067impl Primitive2d for Rhombus {}
1068
1069impl Default for Rhombus {
1070 fn default() -> Self {
1072 Self {
1073 half_diagonals: Vec2::splat(0.5),
1074 }
1075 }
1076}
1077
1078impl Rhombus {
1079 #[inline]
1081 pub const fn new(horizontal_diagonal: f32, vertical_diagonal: f32) -> Self {
1082 Self {
1083 half_diagonals: Vec2::new(horizontal_diagonal / 2.0, vertical_diagonal / 2.0),
1084 }
1085 }
1086
1087 #[inline]
1089 pub const fn from_side(side: f32) -> Self {
1090 Self {
1091 half_diagonals: Vec2::splat(side * FRAC_1_SQRT_2),
1092 }
1093 }
1094
1095 #[inline]
1097 pub const fn from_inradius(inradius: f32) -> Self {
1098 let half_diagonal = inradius * 2.0 / core::f32::consts::SQRT_2;
1099 Self {
1100 half_diagonals: Vec2::new(half_diagonal, half_diagonal),
1101 }
1102 }
1103
1104 #[inline]
1106 pub fn side(&self) -> f32 {
1107 self.half_diagonals.length()
1108 }
1109
1110 #[inline]
1113 pub const fn circumradius(&self) -> f32 {
1114 self.half_diagonals.x.max(self.half_diagonals.y)
1115 }
1116
1117 #[inline]
1120 #[doc(alias = "apothem")]
1121 pub fn inradius(&self) -> f32 {
1122 let side = self.side();
1123 if side == 0.0 {
1124 0.0
1125 } else {
1126 (self.half_diagonals.x * self.half_diagonals.y) / side
1127 }
1128 }
1129
1130 #[inline]
1135 pub fn closest_point(&self, point: Vec2) -> Vec2 {
1136 let point_abs = point.abs();
1138 let half_diagonals = self.half_diagonals.abs(); let normal = Vec2::new(half_diagonals.y, half_diagonals.x);
1142 let normal_magnitude_squared = normal.length_squared();
1143 if normal_magnitude_squared == 0.0 {
1144 return Vec2::ZERO; }
1146
1147 let distance_unnormalised = normal.dot(point_abs) - half_diagonals.x * half_diagonals.y;
1149
1150 if distance_unnormalised <= 0.0 {
1152 return point;
1153 }
1154
1155 let mut result = point_abs - normal * distance_unnormalised / normal_magnitude_squared;
1157
1158 if result.x <= 0.0 {
1161 result = Vec2::new(0.0, half_diagonals.y);
1162 } else if result.y <= 0.0 {
1163 result = Vec2::new(half_diagonals.x, 0.0);
1164 }
1165
1166 result.copysign(point)
1168 }
1169}
1170
1171impl Measured2d for Rhombus {
1172 #[inline]
1174 fn area(&self) -> f32 {
1175 2.0 * self.half_diagonals.x * self.half_diagonals.y
1176 }
1177
1178 #[inline]
1180 fn perimeter(&self) -> f32 {
1181 4.0 * self.side()
1182 }
1183}
1184
1185#[derive(Clone, Copy, Debug, PartialEq)]
1188#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1189#[cfg_attr(
1190 feature = "bevy_reflect",
1191 derive(Reflect),
1192 reflect(Debug, PartialEq, Default, Clone)
1193)]
1194#[cfg_attr(
1195 all(feature = "serialize", feature = "bevy_reflect"),
1196 reflect(Serialize, Deserialize)
1197)]
1198pub struct Plane2d {
1199 pub normal: Dir2,
1201}
1202
1203impl Primitive2d for Plane2d {}
1204
1205impl Default for Plane2d {
1206 fn default() -> Self {
1208 Self { normal: Dir2::Y }
1209 }
1210}
1211
1212impl Plane2d {
1213 #[inline]
1219 pub fn new(normal: Vec2) -> Self {
1220 Self {
1221 normal: Dir2::new(normal).expect("normal must be nonzero and finite"),
1222 }
1223 }
1224}
1225
1226#[derive(Clone, Copy, Debug, PartialEq)]
1230#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1231#[cfg_attr(
1232 feature = "bevy_reflect",
1233 derive(Reflect),
1234 reflect(Debug, PartialEq, Clone)
1235)]
1236#[cfg_attr(
1237 all(feature = "serialize", feature = "bevy_reflect"),
1238 reflect(Serialize, Deserialize)
1239)]
1240pub struct Line2d {
1241 pub direction: Dir2,
1244}
1245
1246impl Primitive2d for Line2d {}
1247
1248#[derive(Clone, Copy, Debug, PartialEq)]
1250#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1251#[cfg_attr(
1252 feature = "bevy_reflect",
1253 derive(Reflect),
1254 reflect(Debug, PartialEq, Clone)
1255)]
1256#[cfg_attr(
1257 all(feature = "serialize", feature = "bevy_reflect"),
1258 reflect(Serialize, Deserialize)
1259)]
1260#[doc(alias = "LineSegment2d")]
1261pub struct Segment2d {
1262 pub vertices: [Vec2; 2],
1264}
1265
1266impl Primitive2d for Segment2d {}
1267
1268impl Default for Segment2d {
1269 fn default() -> Self {
1270 Self {
1271 vertices: [Vec2::new(-0.5, 0.0), Vec2::new(0.5, 0.0)],
1272 }
1273 }
1274}
1275
1276impl Segment2d {
1277 #[inline]
1279 pub const fn new(point1: Vec2, point2: Vec2) -> Self {
1280 Self {
1281 vertices: [point1, point2],
1282 }
1283 }
1284
1285 #[inline]
1289 pub fn from_direction_and_length(direction: Dir2, length: f32) -> Self {
1290 let endpoint = 0.5 * length * direction;
1291 Self {
1292 vertices: [-endpoint, endpoint],
1293 }
1294 }
1295
1296 #[inline]
1301 pub fn from_scaled_direction(scaled_direction: Vec2) -> Self {
1302 let endpoint = 0.5 * scaled_direction;
1303 Self {
1304 vertices: [-endpoint, endpoint],
1305 }
1306 }
1307
1308 #[inline]
1313 pub fn from_ray_and_length(ray: Ray2d, length: f32) -> Self {
1314 Self {
1315 vertices: [ray.origin, ray.get_point(length)],
1316 }
1317 }
1318
1319 #[inline]
1321 pub const fn point1(&self) -> Vec2 {
1322 self.vertices[0]
1323 }
1324
1325 #[inline]
1327 pub const fn point2(&self) -> Vec2 {
1328 self.vertices[1]
1329 }
1330
1331 #[inline]
1333 #[doc(alias = "midpoint")]
1334 pub fn center(&self) -> Vec2 {
1335 self.point1().midpoint(self.point2())
1336 }
1337
1338 #[inline]
1340 pub fn length(&self) -> f32 {
1341 self.point1().distance(self.point2())
1342 }
1343
1344 #[inline]
1346 pub fn length_squared(&self) -> f32 {
1347 self.point1().distance_squared(self.point2())
1348 }
1349
1350 #[inline]
1358 pub fn direction(&self) -> Dir2 {
1359 self.try_direction().unwrap_or_else(|err| {
1360 panic!("Failed to compute the direction of a line segment: {err}")
1361 })
1362 }
1363
1364 #[inline]
1369 pub fn try_direction(&self) -> Result<Dir2, InvalidDirectionError> {
1370 Dir2::new(self.scaled_direction())
1371 }
1372
1373 #[inline]
1375 pub fn scaled_direction(&self) -> Vec2 {
1376 self.point2() - self.point1()
1377 }
1378
1379 #[inline]
1387 pub fn left_normal(&self) -> Dir2 {
1388 self.try_left_normal().unwrap_or_else(|err| {
1389 panic!("Failed to compute the left-hand side normal of a line segment: {err}")
1390 })
1391 }
1392
1393 #[inline]
1398 pub fn try_left_normal(&self) -> Result<Dir2, InvalidDirectionError> {
1399 Dir2::new(self.scaled_left_normal())
1400 }
1401
1402 #[inline]
1406 pub fn scaled_left_normal(&self) -> Vec2 {
1407 let scaled_direction = self.scaled_direction();
1408 Vec2::new(-scaled_direction.y, scaled_direction.x)
1409 }
1410
1411 #[inline]
1419 pub fn right_normal(&self) -> Dir2 {
1420 self.try_right_normal().unwrap_or_else(|err| {
1421 panic!("Failed to compute the right-hand side normal of a line segment: {err}")
1422 })
1423 }
1424
1425 #[inline]
1430 pub fn try_right_normal(&self) -> Result<Dir2, InvalidDirectionError> {
1431 Dir2::new(self.scaled_right_normal())
1432 }
1433
1434 #[inline]
1438 pub fn scaled_right_normal(&self) -> Vec2 {
1439 let scaled_direction = self.scaled_direction();
1440 Vec2::new(scaled_direction.y, -scaled_direction.x)
1441 }
1442
1443 #[inline]
1445 pub fn transformed(&self, isometry: impl Into<Isometry2d>) -> Self {
1446 let isometry: Isometry2d = isometry.into();
1447 Self::new(
1448 isometry.transform_point(self.point1()),
1449 isometry.transform_point(self.point2()),
1450 )
1451 }
1452
1453 #[inline]
1455 pub fn translated(&self, translation: Vec2) -> Segment2d {
1456 Self::new(self.point1() + translation, self.point2() + translation)
1457 }
1458
1459 #[inline]
1461 pub fn rotated(&self, rotation: Rot2) -> Segment2d {
1462 Segment2d::new(rotation * self.point1(), rotation * self.point2())
1463 }
1464
1465 #[inline]
1467 pub fn rotated_around(&self, rotation: Rot2, point: Vec2) -> Segment2d {
1468 let offset = self.translated(-point);
1470 let rotated = offset.rotated(rotation);
1471 rotated.translated(point)
1472 }
1473
1474 #[inline]
1476 pub fn rotated_around_center(&self, rotation: Rot2) -> Segment2d {
1477 self.rotated_around(rotation, self.center())
1478 }
1479
1480 #[inline]
1482 pub fn centered(&self) -> Segment2d {
1483 let center = self.center();
1484 self.translated(-center)
1485 }
1486
1487 #[inline]
1489 pub fn resized(&self, length: f32) -> Segment2d {
1490 let offset_from_origin = self.center();
1491 let centered = self.translated(-offset_from_origin);
1492 let ratio = length / self.length();
1493 let segment = Segment2d::new(centered.point1() * ratio, centered.point2() * ratio);
1494 segment.translated(offset_from_origin)
1495 }
1496
1497 #[inline]
1499 pub const fn reverse(&mut self) {
1500 let [point1, point2] = &mut self.vertices;
1501 core::mem::swap(point1, point2);
1502 }
1503
1504 #[inline]
1506 #[must_use]
1507 pub fn reversed(mut self) -> Self {
1508 self.reverse();
1509 self
1510 }
1511
1512 #[inline]
1514 pub fn closest_point(&self, point: Vec2) -> Vec2 {
1515 let segment_vector = self.vertices[1] - self.vertices[0];
1524 let offset = point - self.vertices[0];
1525 let projection_scaled = segment_vector.dot(offset);
1527
1528 if projection_scaled <= 0.0 {
1530 return self.vertices[0];
1531 }
1532
1533 let length_squared = segment_vector.length_squared();
1534 if projection_scaled >= length_squared {
1536 return self.vertices[1];
1537 }
1538
1539 let t = projection_scaled / length_squared;
1541 self.vertices[0] + t * segment_vector
1542 }
1543}
1544
1545impl From<[Vec2; 2]> for Segment2d {
1546 #[inline]
1547 fn from(vertices: [Vec2; 2]) -> Self {
1548 Self { vertices }
1549 }
1550}
1551
1552impl From<(Vec2, Vec2)> for Segment2d {
1553 #[inline]
1554 fn from((point1, point2): (Vec2, Vec2)) -> Self {
1555 Self::new(point1, point2)
1556 }
1557}
1558
1559#[cfg(feature = "alloc")]
1561#[derive(Clone, Debug, PartialEq)]
1562#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1563#[cfg_attr(
1564 feature = "bevy_reflect",
1565 derive(Reflect),
1566 reflect(Debug, PartialEq, Clone)
1567)]
1568#[cfg_attr(
1569 all(feature = "serialize", feature = "bevy_reflect"),
1570 reflect(Serialize, Deserialize)
1571)]
1572pub struct Polyline2d {
1573 pub vertices: Vec<Vec2>,
1575}
1576
1577#[cfg(feature = "alloc")]
1578impl Primitive2d for Polyline2d {}
1579
1580#[cfg(feature = "alloc")]
1581impl FromIterator<Vec2> for Polyline2d {
1582 fn from_iter<I: IntoIterator<Item = Vec2>>(iter: I) -> Self {
1583 Self {
1584 vertices: iter.into_iter().collect(),
1585 }
1586 }
1587}
1588
1589#[cfg(feature = "alloc")]
1590impl Default for Polyline2d {
1591 fn default() -> Self {
1592 Self {
1593 vertices: Vec::from([Vec2::new(-0.5, 0.0), Vec2::new(0.5, 0.0)]),
1594 }
1595 }
1596}
1597
1598#[cfg(feature = "alloc")]
1599impl Polyline2d {
1600 pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Self {
1602 Self::from_iter(vertices)
1603 }
1604
1605 pub fn with_subdivisions(start: Vec2, end: Vec2, subdivisions: usize) -> Self {
1609 let total_vertices = subdivisions + 2;
1610 let mut vertices = Vec::with_capacity(total_vertices);
1611
1612 let step = (end - start) / (subdivisions + 1) as f32;
1613 for i in 0..total_vertices {
1614 vertices.push(start + step * i as f32);
1615 }
1616
1617 Self { vertices }
1618 }
1619}
1620
1621#[derive(Clone, Copy, Debug, PartialEq)]
1623#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1624#[cfg_attr(
1625 feature = "bevy_reflect",
1626 derive(Reflect),
1627 reflect(Debug, PartialEq, Default, Clone)
1628)]
1629#[cfg_attr(
1630 all(feature = "serialize", feature = "bevy_reflect"),
1631 reflect(Serialize, Deserialize)
1632)]
1633pub struct Triangle2d {
1634 pub vertices: [Vec2; 3],
1636}
1637
1638impl Primitive2d for Triangle2d {}
1639
1640impl Default for Triangle2d {
1641 fn default() -> Self {
1643 Self {
1644 vertices: [Vec2::Y * 0.5, Vec2::new(-0.5, -0.5), Vec2::new(0.5, -0.5)],
1645 }
1646 }
1647}
1648
1649impl Triangle2d {
1650 #[inline]
1652 pub const fn new(a: Vec2, b: Vec2, c: Vec2) -> Self {
1653 Self {
1654 vertices: [a, b, c],
1655 }
1656 }
1657
1658 #[inline]
1660 #[doc(alias = "orientation")]
1661 pub fn winding_order(&self) -> WindingOrder {
1662 let [a, b, c] = self.vertices;
1663 let area = (b - a).perp_dot(c - a);
1664 if area > f32::EPSILON {
1665 WindingOrder::CounterClockwise
1666 } else if area < -f32::EPSILON {
1667 WindingOrder::Clockwise
1668 } else {
1669 WindingOrder::Invalid
1670 }
1671 }
1672
1673 pub fn circumcircle(&self) -> (Circle, Vec2) {
1676 let a = self.vertices[0];
1690 let (b, c) = (self.vertices[1] - a, self.vertices[2] - a);
1691 let b_length_sq = b.length_squared();
1692 let c_length_sq = c.length_squared();
1693
1694 let inv_d = (2.0 * (b.x * c.y - b.y * c.x)).recip();
1696 let ux = inv_d * (c.y * b_length_sq - b.y * c_length_sq);
1697 let uy = inv_d * (b.x * c_length_sq - c.x * b_length_sq);
1698 let u = Vec2::new(ux, uy);
1699
1700 let center = u + a;
1703 let radius = u.length();
1704
1705 (Circle { radius }, center)
1706 }
1707
1708 #[inline]
1713 pub fn is_degenerate(&self) -> bool {
1714 let [a, b, c] = self.vertices;
1715 let ab = (b - a).extend(0.);
1716 let ac = (c - a).extend(0.);
1717 ab.cross(ac).length() < 10e-7
1718 }
1719
1720 #[inline]
1722 pub fn is_acute(&self) -> bool {
1723 let [a, b, c] = self.vertices;
1724 let ab = b - a;
1725 let bc = c - b;
1726 let ca = a - c;
1727
1728 let side_lengths = [
1730 ab.length_squared(),
1731 bc.length_squared(),
1732 ca.length_squared(),
1733 ];
1734 let sum = side_lengths[0] + side_lengths[1] + side_lengths[2];
1735 let max = side_lengths[0].max(side_lengths[1]).max(side_lengths[2]);
1736 sum - max > max
1737 }
1738
1739 #[inline]
1741 pub fn is_obtuse(&self) -> bool {
1742 let [a, b, c] = self.vertices;
1743 let ab = b - a;
1744 let bc = c - b;
1745 let ca = a - c;
1746
1747 let side_lengths = [
1749 ab.length_squared(),
1750 bc.length_squared(),
1751 ca.length_squared(),
1752 ];
1753 let sum = side_lengths[0] + side_lengths[1] + side_lengths[2];
1754 let max = side_lengths[0].max(side_lengths[1]).max(side_lengths[2]);
1755 sum - max < max
1756 }
1757
1758 #[inline]
1761 pub const fn reverse(&mut self) {
1762 self.vertices.swap(0, 2);
1763 }
1764
1765 #[inline]
1767 #[must_use]
1768 pub fn reversed(mut self) -> Self {
1769 self.reverse();
1770 self
1771 }
1772}
1773
1774impl Measured2d for Triangle2d {
1775 #[inline]
1777 fn area(&self) -> f32 {
1778 let [a, b, c] = self.vertices;
1779 ops::abs(a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) / 2.0
1780 }
1781
1782 #[inline]
1784 fn perimeter(&self) -> f32 {
1785 let [a, b, c] = self.vertices;
1786
1787 let ab = a.distance(b);
1788 let bc = b.distance(c);
1789 let ca = c.distance(a);
1790
1791 ab + bc + ca
1792 }
1793}
1794
1795#[derive(Clone, Copy, Debug, PartialEq)]
1797#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1798#[cfg_attr(
1799 feature = "bevy_reflect",
1800 derive(Reflect),
1801 reflect(Debug, PartialEq, Default, Clone)
1802)]
1803#[cfg_attr(
1804 all(feature = "serialize", feature = "bevy_reflect"),
1805 reflect(Serialize, Deserialize)
1806)]
1807#[doc(alias = "Quad")]
1808pub struct Rectangle {
1809 pub half_size: Vec2,
1811}
1812
1813impl Primitive2d for Rectangle {}
1814
1815impl Default for Rectangle {
1816 fn default() -> Self {
1818 Self {
1819 half_size: Vec2::splat(0.5),
1820 }
1821 }
1822}
1823
1824impl Rectangle {
1825 #[inline]
1827 pub const fn new(width: f32, height: f32) -> Self {
1828 Self::from_size(Vec2::new(width, height))
1829 }
1830
1831 #[inline]
1833 pub const fn from_size(size: Vec2) -> Self {
1834 Self {
1835 half_size: Vec2::new(size.x / 2.0, size.y / 2.0),
1836 }
1837 }
1838
1839 #[inline]
1841 pub fn from_corners(point1: Vec2, point2: Vec2) -> Self {
1842 Self {
1843 half_size: (point2 - point1).abs() / 2.0,
1844 }
1845 }
1846
1847 #[inline]
1850 pub const fn from_length(length: f32) -> Self {
1851 Self {
1852 half_size: Vec2::splat(length / 2.0),
1853 }
1854 }
1855
1856 #[inline]
1858 pub fn size(&self) -> Vec2 {
1859 2.0 * self.half_size
1860 }
1861
1862 #[inline]
1867 pub fn closest_point(&self, point: Vec2) -> Vec2 {
1868 point.clamp(-self.half_size, self.half_size)
1870 }
1871}
1872
1873impl Measured2d for Rectangle {
1874 #[inline]
1876 fn area(&self) -> f32 {
1877 4.0 * self.half_size.x * self.half_size.y
1878 }
1879
1880 #[inline]
1882 fn perimeter(&self) -> f32 {
1883 4.0 * (self.half_size.x + self.half_size.y)
1884 }
1885}
1886
1887#[cfg(feature = "alloc")]
1889#[derive(Clone, Debug, PartialEq)]
1890#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1891#[cfg_attr(
1892 feature = "bevy_reflect",
1893 derive(Reflect),
1894 reflect(Debug, PartialEq, Clone)
1895)]
1896#[cfg_attr(
1897 all(feature = "serialize", feature = "bevy_reflect"),
1898 reflect(Serialize, Deserialize)
1899)]
1900pub struct Polygon {
1901 pub vertices: Vec<Vec2>,
1903}
1904
1905#[cfg(feature = "alloc")]
1906impl Primitive2d for Polygon {}
1907
1908#[cfg(feature = "alloc")]
1909impl FromIterator<Vec2> for Polygon {
1910 fn from_iter<I: IntoIterator<Item = Vec2>>(iter: I) -> Self {
1911 Self {
1912 vertices: iter.into_iter().collect(),
1913 }
1914 }
1915}
1916
1917#[cfg(feature = "alloc")]
1918impl Polygon {
1919 pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Self {
1921 Self::from_iter(vertices)
1922 }
1923
1924 #[cfg(feature = "alloc")]
1929 pub fn is_simple(&self) -> bool {
1930 is_polygon_simple(&self.vertices)
1931 }
1932}
1933
1934#[cfg(feature = "alloc")]
1935impl From<ConvexPolygon> for Polygon {
1936 fn from(val: ConvexPolygon) -> Self {
1937 Polygon {
1938 vertices: val.vertices,
1939 }
1940 }
1941}
1942
1943#[cfg(feature = "alloc")]
1945#[derive(Clone, Debug, PartialEq)]
1946#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1947#[cfg_attr(
1948 feature = "bevy_reflect",
1949 derive(Reflect),
1950 reflect(Debug, PartialEq, Clone)
1951)]
1952#[cfg_attr(
1953 all(feature = "serialize", feature = "bevy_reflect"),
1954 reflect(Serialize, Deserialize)
1955)]
1956pub struct ConvexPolygon {
1957 vertices: Vec<Vec2>,
1959}
1960
1961#[cfg(feature = "alloc")]
1962impl Primitive2d for ConvexPolygon {}
1963
1964#[cfg(feature = "alloc")]
1966#[derive(Error, Debug, Clone)]
1967pub enum ConvexPolygonError {
1968 #[error("The created polygon is not convex")]
1970 Concave,
1971}
1972
1973#[cfg(feature = "alloc")]
1974impl ConvexPolygon {
1975 fn triangle_winding_order(
1976 &self,
1977 a_index: usize,
1978 b_index: usize,
1979 c_index: usize,
1980 ) -> WindingOrder {
1981 let a = self.vertices[a_index];
1982 let b = self.vertices[b_index];
1983 let c = self.vertices[c_index];
1984 Triangle2d::new(a, b, c).winding_order()
1985 }
1986
1987 pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Result<Self, ConvexPolygonError> {
1993 let polygon = Self::new_unchecked(vertices);
1994 let len = polygon.vertices.len();
1995 let ref_winding_order = polygon.triangle_winding_order(len - 1, 0, 1);
1996 for i in 1..len {
1997 let winding_order = polygon.triangle_winding_order(i - 1, i, (i + 1) % len);
1998 if winding_order != ref_winding_order {
1999 return Err(ConvexPolygonError::Concave);
2000 }
2001 }
2002 Ok(polygon)
2003 }
2004
2005 #[inline]
2008 pub fn new_unchecked(vertices: impl IntoIterator<Item = Vec2>) -> Self {
2009 Self {
2010 vertices: vertices.into_iter().collect(),
2011 }
2012 }
2013
2014 #[inline]
2016 pub fn vertices(&self) -> &[Vec2] {
2017 &self.vertices
2018 }
2019}
2020
2021#[cfg(feature = "alloc")]
2022impl TryFrom<Polygon> for ConvexPolygon {
2023 type Error = ConvexPolygonError;
2024
2025 fn try_from(val: Polygon) -> Result<Self, Self::Error> {
2026 ConvexPolygon::new(val.vertices)
2027 }
2028}
2029
2030#[derive(Clone, Copy, Debug, PartialEq)]
2032#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2033#[cfg_attr(
2034 feature = "bevy_reflect",
2035 derive(Reflect),
2036 reflect(Debug, PartialEq, Default, Clone)
2037)]
2038#[cfg_attr(
2039 all(feature = "serialize", feature = "bevy_reflect"),
2040 reflect(Serialize, Deserialize)
2041)]
2042pub struct RegularPolygon {
2043 pub circumcircle: Circle,
2045 pub sides: u32,
2047}
2048
2049impl Primitive2d for RegularPolygon {}
2050
2051impl Default for RegularPolygon {
2052 fn default() -> Self {
2054 Self {
2055 circumcircle: Circle { radius: 0.5 },
2056 sides: 6,
2057 }
2058 }
2059}
2060
2061impl RegularPolygon {
2062 #[inline]
2069 pub const fn new(circumradius: f32, sides: u32) -> Self {
2070 assert!(
2071 circumradius.is_sign_positive(),
2072 "polygon has a negative radius"
2073 );
2074 assert!(sides > 2, "polygon has less than 3 sides");
2075
2076 Self {
2077 circumcircle: Circle {
2078 radius: circumradius,
2079 },
2080 sides,
2081 }
2082 }
2083
2084 #[inline]
2087 pub const fn circumradius(&self) -> f32 {
2088 self.circumcircle.radius
2089 }
2090
2091 #[inline]
2095 #[doc(alias = "apothem")]
2096 pub fn inradius(&self) -> f32 {
2097 self.circumradius() * ops::cos(PI / self.sides as f32)
2098 }
2099
2100 #[inline]
2102 pub fn side_length(&self) -> f32 {
2103 2.0 * self.circumradius() * ops::sin(PI / self.sides as f32)
2104 }
2105
2106 #[inline]
2111 pub const fn internal_angle_degrees(&self) -> f32 {
2112 (self.sides - 2) as f32 / self.sides as f32 * 180.0
2113 }
2114
2115 #[inline]
2120 pub const fn internal_angle_radians(&self) -> f32 {
2121 (self.sides - 2) as f32 * PI / self.sides as f32
2122 }
2123
2124 #[inline]
2129 pub const fn external_angle_degrees(&self) -> f32 {
2130 360.0 / self.sides as f32
2131 }
2132
2133 #[inline]
2138 pub const fn external_angle_radians(&self) -> f32 {
2139 2.0 * PI / self.sides as f32
2140 }
2141
2142 pub fn vertices(self, rotation: f32) -> impl IntoIterator<Item = Vec2> {
2147 let start_angle = rotation + FRAC_PI_2;
2149 let step = core::f32::consts::TAU / self.sides as f32;
2150
2151 (0..self.sides).map(move |i| {
2152 let theta = start_angle + i as f32 * step;
2153 let (sin, cos) = ops::sin_cos(theta);
2154 Vec2::new(cos, sin) * self.circumcircle.radius
2155 })
2156 }
2157}
2158
2159impl Measured2d for RegularPolygon {
2160 #[inline]
2162 fn area(&self) -> f32 {
2163 let angle: f32 = 2.0 * PI / (self.sides as f32);
2164 (self.sides as f32) * self.circumradius().squared() * ops::sin(angle) / 2.0
2165 }
2166
2167 #[inline]
2170 fn perimeter(&self) -> f32 {
2171 self.sides as f32 * self.side_length()
2172 }
2173}
2174
2175#[derive(Clone, Copy, Debug, PartialEq)]
2179#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2180#[cfg_attr(
2181 feature = "bevy_reflect",
2182 derive(Reflect),
2183 reflect(Debug, PartialEq, Default, Clone)
2184)]
2185#[cfg_attr(
2186 all(feature = "serialize", feature = "bevy_reflect"),
2187 reflect(Serialize, Deserialize)
2188)]
2189#[doc(alias = "stadium", alias = "pill")]
2190pub struct Capsule2d {
2191 pub radius: f32,
2193 pub half_length: f32,
2195}
2196
2197impl Primitive2d for Capsule2d {}
2198
2199impl Default for Capsule2d {
2200 fn default() -> Self {
2203 Self {
2204 radius: 0.5,
2205 half_length: 0.5,
2206 }
2207 }
2208}
2209
2210impl Capsule2d {
2211 pub const fn new(radius: f32, length: f32) -> Self {
2213 Self {
2214 radius,
2215 half_length: length / 2.0,
2216 }
2217 }
2218
2219 #[inline]
2221 pub const fn to_inner_rectangle(&self) -> Rectangle {
2222 Rectangle::new(self.radius * 2.0, self.half_length * 2.0)
2223 }
2224}
2225
2226impl Measured2d for Capsule2d {
2227 #[inline]
2229 fn area(&self) -> f32 {
2230 PI * self.radius.squared() + self.to_inner_rectangle().area()
2232 }
2233
2234 #[inline]
2236 fn perimeter(&self) -> f32 {
2237 2.0 * PI * self.radius + 4.0 * self.half_length
2239 }
2240}
2241
2242#[derive(Clone, Copy, Debug, PartialEq)]
2257#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2258pub struct Ring<P: Primitive2d> {
2259 pub outer_shape: P,
2261 pub inner_shape: P,
2263}
2264
2265impl<P: Primitive2d> Ring<P> {
2266 pub const fn new(outer_shape: P, inner_shape: P) -> Self {
2270 Self {
2271 outer_shape,
2272 inner_shape,
2273 }
2274 }
2275}
2276
2277impl<T: Primitive2d> Primitive2d for Ring<T> {}
2278
2279impl<P: Primitive2d + Clone + Inset> Ring<P> {
2280 pub fn from_primitive_and_thickness(primitive: P, thickness: f32) -> Self {
2282 let hollow = primitive.clone().inset(thickness);
2283 Ring::new(primitive, hollow)
2284 }
2285}
2286
2287impl<P: Primitive2d + Measured2d> Measured2d for Ring<P> {
2288 #[inline]
2289 fn area(&self) -> f32 {
2290 self.outer_shape.area() - self.inner_shape.area()
2291 }
2292
2293 #[inline]
2294 fn perimeter(&self) -> f32 {
2295 self.outer_shape.perimeter() + self.inner_shape.perimeter()
2296 }
2297}
2298
2299pub trait ToRing: Primitive2d + Inset
2303where
2304 Self: Sized,
2305{
2306 fn to_ring(self, thickness: f32) -> Ring<Self>;
2308}
2309
2310impl<P> ToRing for P
2311where
2312 P: Primitive2d + Clone + Inset,
2313{
2314 fn to_ring(self, thickness: f32) -> Ring<Self> {
2315 Ring::from_primitive_and_thickness(self, thickness)
2316 }
2317}
2318
2319#[cfg(test)]
2320mod tests {
2321 use super::*;
2324 use approx::{assert_abs_diff_eq, assert_relative_eq};
2325
2326 #[test]
2327 fn rectangle_closest_point() {
2328 let rectangle = Rectangle::new(2.0, 2.0);
2329 assert_eq!(rectangle.closest_point(Vec2::X * 10.0), Vec2::X);
2330 assert_eq!(rectangle.closest_point(Vec2::NEG_ONE * 10.0), Vec2::NEG_ONE);
2331 assert_eq!(
2332 rectangle.closest_point(Vec2::new(0.25, 0.1)),
2333 Vec2::new(0.25, 0.1)
2334 );
2335 }
2336
2337 #[test]
2338 fn circle_closest_point() {
2339 let circle = Circle { radius: 1.0 };
2340 assert_eq!(circle.closest_point(Vec2::X * 10.0), Vec2::X);
2341 assert_eq!(
2342 circle.closest_point(Vec2::NEG_ONE * 10.0),
2343 Vec2::NEG_ONE.normalize()
2344 );
2345 assert_eq!(
2346 circle.closest_point(Vec2::new(0.25, 0.1)),
2347 Vec2::new(0.25, 0.1)
2348 );
2349 }
2350
2351 #[test]
2352 fn annulus_closest_point() {
2353 let annulus = Annulus::new(1.5, 2.0);
2354 assert_eq!(annulus.closest_point(Vec2::X * 10.0), Vec2::X * 2.0);
2355 assert_eq!(
2356 annulus.closest_point(Vec2::NEG_ONE),
2357 Vec2::NEG_ONE.normalize() * 1.5
2358 );
2359 assert_eq!(
2360 annulus.closest_point(Vec2::new(1.55, 0.85)),
2361 Vec2::new(1.55, 0.85)
2362 );
2363 }
2364
2365 #[test]
2366 fn rhombus_closest_point() {
2367 let rhombus = Rhombus::new(2.0, 1.0);
2368 assert_eq!(rhombus.closest_point(Vec2::X * 10.0), Vec2::X);
2369 assert_eq!(
2370 rhombus.closest_point(Vec2::NEG_ONE * 0.2),
2371 Vec2::NEG_ONE * 0.2
2372 );
2373 assert_eq!(
2374 rhombus.closest_point(Vec2::new(-0.55, 0.35)),
2375 Vec2::new(-0.5, 0.25)
2376 );
2377
2378 let rhombus = Rhombus::new(0.0, 0.0);
2379 assert_eq!(rhombus.closest_point(Vec2::X * 10.0), Vec2::ZERO);
2380 assert_eq!(rhombus.closest_point(Vec2::NEG_ONE * 0.2), Vec2::ZERO);
2381 assert_eq!(rhombus.closest_point(Vec2::new(-0.55, 0.35)), Vec2::ZERO);
2382 }
2383
2384 #[test]
2385 fn segment_closest_point() {
2386 assert_eq!(
2387 Segment2d::new(Vec2::new(0.0, 0.0), Vec2::new(3.0, 0.0))
2388 .closest_point(Vec2::new(1.0, 6.0)),
2389 Vec2::new(1.0, 0.0)
2390 );
2391
2392 let segments = [
2393 Segment2d::new(Vec2::new(0.0, 0.0), Vec2::new(0.0, 0.0)),
2394 Segment2d::new(Vec2::new(0.0, 0.0), Vec2::new(1.0, 0.0)),
2395 Segment2d::new(Vec2::new(1.0, 0.0), Vec2::new(0.0, 1.0)),
2396 Segment2d::new(Vec2::new(1.0, 0.0), Vec2::new(1.0, 5.0 * f32::EPSILON)),
2397 ];
2398 let points = [
2399 Vec2::new(0.0, 0.0),
2400 Vec2::new(1.0, 0.0),
2401 Vec2::new(-1.0, 1.0),
2402 Vec2::new(1.0, 1.0),
2403 Vec2::new(-1.0, 0.0),
2404 Vec2::new(5.0, -1.0),
2405 Vec2::new(1.0, f32::EPSILON),
2406 ];
2407
2408 for point in points.iter() {
2409 for segment in segments.iter() {
2410 let closest = segment.closest_point(*point);
2411 assert!(
2412 point.distance_squared(closest) <= point.distance_squared(segment.point1()),
2413 "Closest point must always be at least as close as either vertex."
2414 );
2415 assert!(
2416 point.distance_squared(closest) <= point.distance_squared(segment.point2()),
2417 "Closest point must always be at least as close as either vertex."
2418 );
2419 assert!(
2420 point.distance_squared(closest) <= point.distance_squared(segment.center()),
2421 "Closest point must always be at least as close as the center."
2422 );
2423 let closest_to_closest = segment.closest_point(closest);
2424 assert_relative_eq!(closest_to_closest, closest);
2426 }
2427 }
2428 }
2429
2430 #[test]
2431 fn circle_math() {
2432 let circle = Circle { radius: 3.0 };
2433 assert_eq!(circle.diameter(), 6.0, "incorrect diameter");
2434 assert_eq!(circle.area(), 28.274334, "incorrect area");
2435 assert_eq!(circle.perimeter(), 18.849556, "incorrect perimeter");
2436 }
2437
2438 #[test]
2439 fn capsule_math() {
2440 let capsule = Capsule2d::new(2.0, 9.0);
2441 assert_eq!(
2442 capsule.to_inner_rectangle(),
2443 Rectangle::new(4.0, 9.0),
2444 "rectangle wasn't created correctly from a capsule"
2445 );
2446 assert_eq!(capsule.area(), 48.566371, "incorrect area");
2447 assert_eq!(capsule.perimeter(), 30.566371, "incorrect perimeter");
2448 }
2449
2450 #[test]
2451 fn annulus_math() {
2452 let annulus = Annulus::new(2.5, 3.5);
2453 assert_eq!(annulus.diameter(), 7.0, "incorrect diameter");
2454 assert_eq!(annulus.thickness(), 1.0, "incorrect thickness");
2455 assert_eq!(annulus.area(), 18.849556, "incorrect area");
2456 assert_eq!(annulus.perimeter(), 37.699112, "incorrect perimeter");
2457 }
2458
2459 #[test]
2460 fn rhombus_math() {
2461 let rhombus = Rhombus::new(3.0, 4.0);
2462 assert_eq!(rhombus.area(), 6.0, "incorrect area");
2463 assert_eq!(rhombus.perimeter(), 10.0, "incorrect perimeter");
2464 assert_eq!(rhombus.side(), 2.5, "incorrect side");
2465 assert_eq!(rhombus.inradius(), 1.2, "incorrect inradius");
2466 assert_eq!(rhombus.circumradius(), 2.0, "incorrect circumradius");
2467 let rhombus = Rhombus::new(0.0, 0.0);
2468 assert_eq!(rhombus.area(), 0.0, "incorrect area");
2469 assert_eq!(rhombus.perimeter(), 0.0, "incorrect perimeter");
2470 assert_eq!(rhombus.side(), 0.0, "incorrect side");
2471 assert_eq!(rhombus.inradius(), 0.0, "incorrect inradius");
2472 assert_eq!(rhombus.circumradius(), 0.0, "incorrect circumradius");
2473 let rhombus = Rhombus::from_side(core::f32::consts::SQRT_2);
2474 assert_abs_diff_eq!(rhombus.half_diagonals, Vec2::new(1.0, 1.0));
2475 assert_abs_diff_eq!(
2476 rhombus.half_diagonals,
2477 Rhombus::from_inradius(FRAC_1_SQRT_2).half_diagonals
2478 );
2479 }
2480
2481 #[test]
2482 fn ellipse_math() {
2483 let ellipse = Ellipse::new(3.0, 1.0);
2484 assert_eq!(ellipse.area(), 9.424778, "incorrect area");
2485
2486 assert_eq!(ellipse.eccentricity(), 0.94280905, "incorrect eccentricity");
2487
2488 let line = Ellipse::new(1., 0.);
2489 assert_eq!(line.eccentricity(), 1., "incorrect line eccentricity");
2490
2491 let circle = Ellipse::new(2., 2.);
2492 assert_eq!(circle.eccentricity(), 0., "incorrect circle eccentricity");
2493 }
2494
2495 #[test]
2496 fn ellipse_perimeter() {
2497 let circle = Ellipse::new(1., 1.);
2498 assert_relative_eq!(circle.perimeter(), 6.2831855);
2499
2500 let line = Ellipse::new(75_000., 0.5);
2501 assert_relative_eq!(line.perimeter(), 300_000.);
2502
2503 let ellipse = Ellipse::new(0.5, 2.);
2504 assert_relative_eq!(ellipse.perimeter(), 8.578423);
2505
2506 let ellipse = Ellipse::new(5., 3.);
2507 assert_relative_eq!(ellipse.perimeter(), 25.526999);
2508 }
2509
2510 #[test]
2511 fn triangle_math() {
2512 let triangle = Triangle2d::new(
2513 Vec2::new(-2.0, -1.0),
2514 Vec2::new(1.0, 4.0),
2515 Vec2::new(7.0, 0.0),
2516 );
2517 assert_eq!(triangle.area(), 21.0, "incorrect area");
2518 assert_eq!(triangle.perimeter(), 22.097439, "incorrect perimeter");
2519
2520 let degenerate_triangle =
2521 Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(0., 0.), Vec2::new(1., 0.));
2522 assert!(degenerate_triangle.is_degenerate());
2523
2524 let acute_triangle =
2525 Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(1., 0.), Vec2::new(0., 5.));
2526 let obtuse_triangle =
2527 Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(1., 0.), Vec2::new(0., 0.5));
2528
2529 assert!(acute_triangle.is_acute());
2530 assert!(!acute_triangle.is_obtuse());
2531 assert!(!obtuse_triangle.is_acute());
2532 assert!(obtuse_triangle.is_obtuse());
2533 }
2534
2535 #[test]
2536 fn triangle_winding_order() {
2537 let mut cw_triangle = Triangle2d::new(
2538 Vec2::new(0.0, 2.0),
2539 Vec2::new(-0.5, -1.2),
2540 Vec2::new(-1.0, -1.0),
2541 );
2542 assert_eq!(cw_triangle.winding_order(), WindingOrder::Clockwise);
2543
2544 let ccw_triangle = Triangle2d::new(
2545 Vec2::new(-1.0, -1.0),
2546 Vec2::new(-0.5, -1.2),
2547 Vec2::new(0.0, 2.0),
2548 );
2549 assert_eq!(ccw_triangle.winding_order(), WindingOrder::CounterClockwise);
2550
2551 cw_triangle.reverse();
2554 assert_eq!(cw_triangle, ccw_triangle);
2555
2556 let invalid_triangle = Triangle2d::new(
2557 Vec2::new(0.0, 2.0),
2558 Vec2::new(0.0, -1.0),
2559 Vec2::new(0.0, -1.2),
2560 );
2561 assert_eq!(invalid_triangle.winding_order(), WindingOrder::Invalid);
2562 }
2563
2564 #[test]
2565 fn rectangle_math() {
2566 let rectangle = Rectangle::new(3.0, 7.0);
2567 assert_eq!(
2568 rectangle,
2569 Rectangle::from_corners(Vec2::new(-1.5, -3.5), Vec2::new(1.5, 3.5))
2570 );
2571 assert_eq!(rectangle.area(), 21.0, "incorrect area");
2572 assert_eq!(rectangle.perimeter(), 20.0, "incorrect perimeter");
2573 }
2574
2575 #[test]
2576 fn regular_polygon_math() {
2577 let polygon = RegularPolygon::new(3.0, 6);
2578 assert_eq!(polygon.inradius(), 2.598076, "incorrect inradius");
2579 assert_eq!(polygon.side_length(), 3.0, "incorrect side length");
2580 assert_relative_eq!(polygon.area(), 23.38268, epsilon = 0.00001);
2581 assert_eq!(polygon.perimeter(), 18.0, "incorrect perimeter");
2582 assert_eq!(
2583 polygon.internal_angle_degrees(),
2584 120.0,
2585 "incorrect internal angle"
2586 );
2587 assert_eq!(
2588 polygon.internal_angle_radians(),
2589 120_f32.to_radians(),
2590 "incorrect internal angle"
2591 );
2592 assert_eq!(
2593 polygon.external_angle_degrees(),
2594 60.0,
2595 "incorrect external angle"
2596 );
2597 assert_eq!(
2598 polygon.external_angle_radians(),
2599 60_f32.to_radians(),
2600 "incorrect external angle"
2601 );
2602 }
2603
2604 #[test]
2605 fn triangle_circumcenter() {
2606 let triangle = Triangle2d::new(
2607 Vec2::new(10.0, 2.0),
2608 Vec2::new(-5.0, -3.0),
2609 Vec2::new(2.0, -1.0),
2610 );
2611 let (Circle { radius }, circumcenter) = triangle.circumcircle();
2612
2613 assert_eq!(radius, 98.34887);
2615 assert_eq!(circumcenter, Vec2::new(-28.5, 92.5));
2616 }
2617
2618 #[test]
2619 fn regular_polygon_vertices() {
2620 let polygon = RegularPolygon::new(1.0, 4);
2621
2622 let mut vertices = polygon.vertices(0.0).into_iter();
2624 assert!((vertices.next().unwrap() - Vec2::Y).length() < 1e-7);
2625
2626 let mut rotated_vertices = polygon.vertices(core::f32::consts::FRAC_PI_4).into_iter();
2628
2629 let side_distance = FRAC_1_SQRT_2;
2631 assert!(
2632 (rotated_vertices.next().unwrap() - Vec2::new(-side_distance, side_distance)).length()
2633 < 1e-7,
2634 );
2635 }
2636}