1use core::f32::consts::{FRAC_1_SQRT_2, FRAC_PI_2, FRAC_PI_3, PI};
2use derive_more::derive::{Display, Error, From};
3
4use super::{Measured2d, Primitive2d, WindingOrder};
5use crate::{
6 ops::{self, FloatPow},
7 Dir2, Vec2,
8};
9
10#[cfg(feature = "bevy_reflect")]
11use bevy_reflect::{std_traits::ReflectDefault, Reflect};
12#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
13use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
14
15#[derive(Clone, Copy, Debug, PartialEq)]
17#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
18#[cfg_attr(
19 feature = "bevy_reflect",
20 derive(Reflect),
21 reflect(Debug, PartialEq, Default)
22)]
23#[cfg_attr(
24 all(feature = "serialize", feature = "bevy_reflect"),
25 reflect(Serialize, Deserialize)
26)]
27pub struct Circle {
28 pub radius: f32,
30}
31impl Primitive2d for Circle {}
32
33impl Default for Circle {
34 fn default() -> Self {
36 Self { radius: 0.5 }
37 }
38}
39
40impl Circle {
41 #[inline(always)]
43 pub const fn new(radius: f32) -> Self {
44 Self { radius }
45 }
46
47 #[inline(always)]
49 pub fn diameter(&self) -> f32 {
50 2.0 * self.radius
51 }
52
53 #[inline(always)]
58 pub fn closest_point(&self, point: Vec2) -> Vec2 {
59 let distance_squared = point.length_squared();
60
61 if distance_squared <= self.radius.squared() {
62 point
64 } else {
65 let dir_to_point = point / distance_squared.sqrt();
68 self.radius * dir_to_point
69 }
70 }
71}
72
73impl Measured2d for Circle {
74 #[inline(always)]
76 fn area(&self) -> f32 {
77 PI * self.radius.squared()
78 }
79
80 #[inline(always)]
82 #[doc(alias = "circumference")]
83 fn perimeter(&self) -> f32 {
84 2.0 * PI * self.radius
85 }
86}
87
88#[derive(Clone, Copy, Debug, PartialEq)]
103#[doc(alias("CircularArc", "CircleArc"))]
104#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
105#[cfg_attr(
106 feature = "bevy_reflect",
107 derive(Reflect),
108 reflect(Debug, PartialEq, Default)
109)]
110#[cfg_attr(
111 all(feature = "serialize", feature = "bevy_reflect"),
112 reflect(Serialize, Deserialize)
113)]
114pub struct Arc2d {
115 pub radius: f32,
117 pub half_angle: f32,
119}
120impl Primitive2d for Arc2d {}
121
122impl Default for Arc2d {
123 fn default() -> Self {
125 Self {
126 radius: 0.5,
127 half_angle: 2.0 * FRAC_PI_3,
128 }
129 }
130}
131
132impl Arc2d {
133 #[inline(always)]
135 pub fn new(radius: f32, half_angle: f32) -> Self {
136 Self { radius, half_angle }
137 }
138
139 #[inline(always)]
141 pub fn from_radians(radius: f32, angle: f32) -> Self {
142 Self {
143 radius,
144 half_angle: angle / 2.0,
145 }
146 }
147
148 #[inline(always)]
150 pub fn from_degrees(radius: f32, angle: f32) -> Self {
151 Self {
152 radius,
153 half_angle: angle.to_radians() / 2.0,
154 }
155 }
156
157 #[inline(always)]
161 pub fn from_turns(radius: f32, fraction: f32) -> Self {
162 Self {
163 radius,
164 half_angle: fraction * PI,
165 }
166 }
167
168 #[inline(always)]
170 pub fn angle(&self) -> f32 {
171 self.half_angle * 2.0
172 }
173
174 #[inline(always)]
176 pub fn length(&self) -> f32 {
177 self.angle() * self.radius
178 }
179
180 #[inline(always)]
182 pub fn right_endpoint(&self) -> Vec2 {
183 self.radius * Vec2::from_angle(FRAC_PI_2 - self.half_angle)
184 }
185
186 #[inline(always)]
188 pub fn left_endpoint(&self) -> Vec2 {
189 self.radius * Vec2::from_angle(FRAC_PI_2 + self.half_angle)
190 }
191
192 #[inline(always)]
194 pub fn endpoints(&self) -> [Vec2; 2] {
195 [self.left_endpoint(), self.right_endpoint()]
196 }
197
198 #[inline]
200 pub fn midpoint(&self) -> Vec2 {
201 self.radius * Vec2::Y
202 }
203
204 #[inline(always)]
206 pub fn half_chord_length(&self) -> f32 {
207 self.radius * ops::sin(self.half_angle)
208 }
209
210 #[inline(always)]
212 pub fn chord_length(&self) -> f32 {
213 2.0 * self.half_chord_length()
214 }
215
216 #[inline(always)]
218 pub fn chord_midpoint(&self) -> Vec2 {
219 self.apothem() * Vec2::Y
220 }
221
222 #[inline(always)]
228 pub fn apothem(&self) -> f32 {
232 let sign = if self.is_minor() { 1.0 } else { -1.0 };
233 sign * f32::sqrt(self.radius.squared() - self.half_chord_length().squared())
234 }
235
236 pub fn sagitta(&self) -> f32 {
242 self.radius - self.apothem()
243 }
244
245 #[inline(always)]
249 pub fn is_minor(&self) -> bool {
250 self.half_angle <= FRAC_PI_2
251 }
252
253 #[inline(always)]
257 pub fn is_major(&self) -> bool {
258 self.half_angle >= FRAC_PI_2
259 }
260}
261
262#[derive(Clone, Copy, Debug, PartialEq, From)]
271#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
272#[cfg_attr(
273 feature = "bevy_reflect",
274 derive(Reflect),
275 reflect(Debug, PartialEq, Default)
276)]
277#[cfg_attr(
278 all(feature = "serialize", feature = "bevy_reflect"),
279 reflect(Serialize, Deserialize)
280)]
281pub struct CircularSector {
282 #[cfg_attr(feature = "serialize", serde(flatten))]
284 pub arc: Arc2d,
285}
286impl Primitive2d for CircularSector {}
287
288impl Default for CircularSector {
289 fn default() -> Self {
291 Self::from(Arc2d::default())
292 }
293}
294
295impl Measured2d for CircularSector {
296 #[inline(always)]
297 fn area(&self) -> f32 {
298 self.arc.radius.squared() * self.arc.half_angle
299 }
300
301 #[inline(always)]
302 fn perimeter(&self) -> f32 {
303 if self.half_angle() >= PI {
304 self.arc.radius * 2.0 * PI
305 } else {
306 2.0 * self.radius() + self.arc_length()
307 }
308 }
309}
310
311impl CircularSector {
312 #[inline(always)]
314 pub fn new(radius: f32, angle: f32) -> Self {
315 Self::from(Arc2d::new(radius, angle))
316 }
317
318 #[inline(always)]
320 pub fn from_radians(radius: f32, angle: f32) -> Self {
321 Self::from(Arc2d::from_radians(radius, angle))
322 }
323
324 #[inline(always)]
326 pub fn from_degrees(radius: f32, angle: f32) -> Self {
327 Self::from(Arc2d::from_degrees(radius, angle))
328 }
329
330 #[inline(always)]
334 pub fn from_turns(radius: f32, fraction: f32) -> Self {
335 Self::from(Arc2d::from_turns(radius, fraction))
336 }
337
338 #[inline(always)]
340 pub fn half_angle(&self) -> f32 {
341 self.arc.half_angle
342 }
343
344 #[inline(always)]
346 pub fn angle(&self) -> f32 {
347 self.arc.angle()
348 }
349
350 #[inline(always)]
352 pub fn radius(&self) -> f32 {
353 self.arc.radius
354 }
355
356 #[inline(always)]
358 pub fn arc_length(&self) -> f32 {
359 self.arc.length()
360 }
361
362 #[inline(always)]
366 pub fn half_chord_length(&self) -> f32 {
367 self.arc.half_chord_length()
368 }
369
370 #[inline(always)]
374 pub fn chord_length(&self) -> f32 {
375 self.arc.chord_length()
376 }
377
378 #[inline(always)]
382 pub fn chord_midpoint(&self) -> Vec2 {
383 self.arc.chord_midpoint()
384 }
385
386 #[inline(always)]
390 pub fn apothem(&self) -> f32 {
391 self.arc.apothem()
392 }
393
394 #[inline(always)]
398 pub fn sagitta(&self) -> f32 {
399 self.arc.sagitta()
400 }
401}
402
403#[derive(Clone, Copy, Debug, PartialEq, From)]
414#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
415#[cfg_attr(
416 feature = "bevy_reflect",
417 derive(Reflect),
418 reflect(Debug, PartialEq, Default)
419)]
420#[cfg_attr(
421 all(feature = "serialize", feature = "bevy_reflect"),
422 reflect(Serialize, Deserialize)
423)]
424pub struct CircularSegment {
425 #[cfg_attr(feature = "serialize", serde(flatten))]
427 pub arc: Arc2d,
428}
429impl Primitive2d for CircularSegment {}
430
431impl Default for CircularSegment {
432 fn default() -> Self {
434 Self::from(Arc2d::default())
435 }
436}
437
438impl Measured2d for CircularSegment {
439 #[inline(always)]
440 fn area(&self) -> f32 {
441 0.5 * self.arc.radius.squared() * (self.arc.angle() - ops::sin(self.arc.angle()))
442 }
443
444 #[inline(always)]
445 fn perimeter(&self) -> f32 {
446 self.chord_length() + self.arc_length()
447 }
448}
449impl CircularSegment {
450 #[inline(always)]
452 pub fn new(radius: f32, angle: f32) -> Self {
453 Self::from(Arc2d::new(radius, angle))
454 }
455
456 #[inline(always)]
458 pub fn from_radians(radius: f32, angle: f32) -> Self {
459 Self::from(Arc2d::from_radians(radius, angle))
460 }
461
462 #[inline(always)]
464 pub fn from_degrees(radius: f32, angle: f32) -> Self {
465 Self::from(Arc2d::from_degrees(radius, angle))
466 }
467
468 #[inline(always)]
472 pub fn from_turns(radius: f32, fraction: f32) -> Self {
473 Self::from(Arc2d::from_turns(radius, fraction))
474 }
475
476 #[inline(always)]
478 pub fn half_angle(&self) -> f32 {
479 self.arc.half_angle
480 }
481
482 #[inline(always)]
484 pub fn angle(&self) -> f32 {
485 self.arc.angle()
486 }
487
488 #[inline(always)]
490 pub fn radius(&self) -> f32 {
491 self.arc.radius
492 }
493
494 #[inline(always)]
496 pub fn arc_length(&self) -> f32 {
497 self.arc.length()
498 }
499
500 #[inline(always)]
502 #[doc(alias = "half_base_length")]
503 pub fn half_chord_length(&self) -> f32 {
504 self.arc.half_chord_length()
505 }
506
507 #[inline(always)]
509 #[doc(alias = "base_length")]
510 #[doc(alias = "base")]
511 pub fn chord_length(&self) -> f32 {
512 self.arc.chord_length()
513 }
514
515 #[inline(always)]
517 #[doc(alias = "base_midpoint")]
518 pub fn chord_midpoint(&self) -> Vec2 {
519 self.arc.chord_midpoint()
520 }
521
522 #[inline(always)]
527 pub fn apothem(&self) -> f32 {
528 self.arc.apothem()
529 }
530
531 #[inline(always)]
535 #[doc(alias = "height")]
536 pub fn sagitta(&self) -> f32 {
537 self.arc.sagitta()
538 }
539}
540
541#[cfg(test)]
542mod arc_tests {
543 use core::f32::consts::FRAC_PI_4;
544 use core::f32::consts::SQRT_2;
545
546 use super::*;
549
550 struct ArcTestCase {
551 radius: f32,
552 half_angle: f32,
553 angle: f32,
554 length: f32,
555 right_endpoint: Vec2,
556 left_endpoint: Vec2,
557 endpoints: [Vec2; 2],
558 midpoint: Vec2,
559 half_chord_length: f32,
560 chord_length: f32,
561 chord_midpoint: Vec2,
562 apothem: f32,
563 sagitta: f32,
564 is_minor: bool,
565 is_major: bool,
566 sector_area: f32,
567 sector_perimeter: f32,
568 segment_area: f32,
569 segment_perimeter: f32,
570 }
571
572 impl ArcTestCase {
573 fn check_arc(&self, arc: Arc2d) {
574 assert_eq!(self.is_minor, arc.is_minor());
589 assert_eq!(self.is_major, arc.is_major());
590 }
591
592 fn check_sector(&self, sector: CircularSector) {
593 }
604
605 fn check_segment(&self, segment: CircularSegment) {
606 }
617 }
618
619 #[test]
620 fn zero_angle() {
621 let tests = ArcTestCase {
622 radius: 1.0,
623 half_angle: 0.0,
624 angle: 0.0,
625 length: 0.0,
626 left_endpoint: Vec2::Y,
627 right_endpoint: Vec2::Y,
628 endpoints: [Vec2::Y, Vec2::Y],
629 midpoint: Vec2::Y,
630 half_chord_length: 0.0,
631 chord_length: 0.0,
632 chord_midpoint: Vec2::Y,
633 apothem: 1.0,
634 sagitta: 0.0,
635 is_minor: true,
636 is_major: false,
637 sector_area: 0.0,
638 sector_perimeter: 2.0,
639 segment_area: 0.0,
640 segment_perimeter: 0.0,
641 };
642
643 tests.check_arc(Arc2d::new(1.0, 0.0));
644 tests.check_sector(CircularSector::new(1.0, 0.0));
645 tests.check_segment(CircularSegment::new(1.0, 0.0));
646 }
647
648 #[test]
649 fn zero_radius() {
650 let tests = ArcTestCase {
651 radius: 0.0,
652 half_angle: FRAC_PI_4,
653 angle: FRAC_PI_2,
654 length: 0.0,
655 left_endpoint: Vec2::ZERO,
656 right_endpoint: Vec2::ZERO,
657 endpoints: [Vec2::ZERO, Vec2::ZERO],
658 midpoint: Vec2::ZERO,
659 half_chord_length: 0.0,
660 chord_length: 0.0,
661 chord_midpoint: Vec2::ZERO,
662 apothem: 0.0,
663 sagitta: 0.0,
664 is_minor: true,
665 is_major: false,
666 sector_area: 0.0,
667 sector_perimeter: 0.0,
668 segment_area: 0.0,
669 segment_perimeter: 0.0,
670 };
671
672 tests.check_arc(Arc2d::new(0.0, FRAC_PI_4));
673 tests.check_sector(CircularSector::new(0.0, FRAC_PI_4));
674 tests.check_segment(CircularSegment::new(0.0, FRAC_PI_4));
675 }
676
677 #[test]
678 fn quarter_circle() {
679 let sqrt_half: f32 = f32::sqrt(0.5);
680 let tests = ArcTestCase {
681 radius: 1.0,
682 half_angle: FRAC_PI_4,
683 angle: FRAC_PI_2,
684 length: FRAC_PI_2,
685 left_endpoint: Vec2::new(-sqrt_half, sqrt_half),
686 right_endpoint: Vec2::splat(sqrt_half),
687 endpoints: [Vec2::new(-sqrt_half, sqrt_half), Vec2::splat(sqrt_half)],
688 midpoint: Vec2::Y,
689 half_chord_length: sqrt_half,
690 chord_length: f32::sqrt(2.0),
691 chord_midpoint: Vec2::new(0.0, sqrt_half),
692 apothem: sqrt_half,
693 sagitta: 1.0 - sqrt_half,
694 is_minor: true,
695 is_major: false,
696 sector_area: FRAC_PI_4,
697 sector_perimeter: FRAC_PI_2 + 2.0,
698 segment_area: FRAC_PI_4 - 0.5,
699 segment_perimeter: FRAC_PI_2 + SQRT_2,
700 };
701
702 tests.check_arc(Arc2d::from_turns(1.0, 0.25));
703 tests.check_sector(CircularSector::from_turns(1.0, 0.25));
704 tests.check_segment(CircularSegment::from_turns(1.0, 0.25));
705 }
706
707 #[test]
708 fn half_circle() {
709 let tests = ArcTestCase {
710 radius: 1.0,
711 half_angle: FRAC_PI_2,
712 angle: PI,
713 length: PI,
714 left_endpoint: Vec2::NEG_X,
715 right_endpoint: Vec2::X,
716 endpoints: [Vec2::NEG_X, Vec2::X],
717 midpoint: Vec2::Y,
718 half_chord_length: 1.0,
719 chord_length: 2.0,
720 chord_midpoint: Vec2::ZERO,
721 apothem: 0.0,
722 sagitta: 1.0,
723 is_minor: true,
724 is_major: true,
725 sector_area: FRAC_PI_2,
726 sector_perimeter: PI + 2.0,
727 segment_area: FRAC_PI_2,
728 segment_perimeter: PI + 2.0,
729 };
730
731 tests.check_arc(Arc2d::from_radians(1.0, PI));
732 tests.check_sector(CircularSector::from_radians(1.0, PI));
733 tests.check_segment(CircularSegment::from_radians(1.0, PI));
734 }
735
736 #[test]
737 fn full_circle() {
738 let tests = ArcTestCase {
739 radius: 1.0,
740 half_angle: PI,
741 angle: 2.0 * PI,
742 length: 2.0 * PI,
743 left_endpoint: Vec2::NEG_Y,
744 right_endpoint: Vec2::NEG_Y,
745 endpoints: [Vec2::NEG_Y, Vec2::NEG_Y],
746 midpoint: Vec2::Y,
747 half_chord_length: 0.0,
748 chord_length: 0.0,
749 chord_midpoint: Vec2::NEG_Y,
750 apothem: -1.0,
751 sagitta: 2.0,
752 is_minor: false,
753 is_major: true,
754 sector_area: PI,
755 sector_perimeter: 2.0 * PI,
756 segment_area: PI,
757 segment_perimeter: 2.0 * PI,
758 };
759
760 tests.check_arc(Arc2d::from_degrees(1.0, 360.0));
761 tests.check_sector(CircularSector::from_degrees(1.0, 360.0));
762 tests.check_segment(CircularSegment::from_degrees(1.0, 360.0));
763 }
764}
765
766#[derive(Clone, Copy, Debug, PartialEq)]
768#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
769#[cfg_attr(
770 feature = "bevy_reflect",
771 derive(Reflect),
772 reflect(Debug, PartialEq, Default)
773)]
774#[cfg_attr(
775 all(feature = "serialize", feature = "bevy_reflect"),
776 reflect(Serialize, Deserialize)
777)]
778pub struct Ellipse {
779 pub half_size: Vec2,
783}
784impl Primitive2d for Ellipse {}
785
786impl Default for Ellipse {
787 fn default() -> Self {
789 Self {
790 half_size: Vec2::new(1.0, 0.5),
791 }
792 }
793}
794
795impl Ellipse {
796 #[inline(always)]
800 pub const fn new(half_width: f32, half_height: f32) -> Self {
801 Self {
802 half_size: Vec2::new(half_width, half_height),
803 }
804 }
805
806 #[inline(always)]
810 pub fn from_size(size: Vec2) -> Self {
811 Self {
812 half_size: size / 2.0,
813 }
814 }
815
816 #[inline(always)]
817 pub fn eccentricity(&self) -> f32 {
822 let a = self.semi_major();
823 let b = self.semi_minor();
824
825 (a * a - b * b).sqrt() / a
826 }
827
828 #[inline(always)]
829 pub fn focal_length(&self) -> f32 {
833 let a = self.semi_major();
834 let b = self.semi_minor();
835
836 (a * a - b * b).sqrt()
837 }
838
839 #[inline(always)]
841 pub fn semi_major(&self) -> f32 {
842 self.half_size.max_element()
843 }
844
845 #[inline(always)]
847 pub fn semi_minor(&self) -> f32 {
848 self.half_size.min_element()
849 }
850}
851
852impl Measured2d for Ellipse {
853 #[inline(always)]
855 fn area(&self) -> f32 {
856 PI * self.half_size.x * self.half_size.y
857 }
858
859 #[inline(always)]
860 fn perimeter(&self) -> f32 {
864 let a = self.semi_major();
865 let b = self.semi_minor();
866
867 if a / b - 1. < 1e-5 {
869 return PI * (a + b);
870 };
871
872 if a / b > 1e4 {
874 return 4. * a;
875 };
876
877 const BINOMIAL_COEFFICIENTS: [f32; 21] = [
881 1.,
882 0.25,
883 0.015625,
884 0.00390625,
885 0.0015258789,
886 0.00074768066,
887 0.00042057037,
888 0.00025963783,
889 0.00017140154,
890 0.000119028846,
891 0.00008599834,
892 0.00006414339,
893 0.000049109784,
894 0.000038430585,
895 0.000030636627,
896 0.000024815668,
897 0.000020380836,
898 0.000016942893,
899 0.000014236736,
900 0.000012077564,
901 0.000010333865,
902 ];
903
904 let h = ((a - b) / (a + b)).squared();
908
909 PI * (a + b)
910 * (0..=20)
911 .map(|i| BINOMIAL_COEFFICIENTS[i] * ops::powf(h, i as f32))
912 .sum::<f32>()
913 }
914}
915
916#[derive(Clone, Copy, Debug, PartialEq)]
918#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
919#[cfg_attr(
920 feature = "bevy_reflect",
921 derive(Reflect),
922 reflect(Debug, PartialEq, Default)
923)]
924#[cfg_attr(
925 all(feature = "serialize", feature = "bevy_reflect"),
926 reflect(Serialize, Deserialize)
927)]
928#[doc(alias = "Ring")]
929pub struct Annulus {
930 pub inner_circle: Circle,
932 pub outer_circle: Circle,
934}
935impl Primitive2d for Annulus {}
936
937impl Default for Annulus {
938 fn default() -> Self {
940 Self {
941 inner_circle: Circle::new(0.5),
942 outer_circle: Circle::new(1.0),
943 }
944 }
945}
946
947impl Annulus {
948 #[inline(always)]
950 pub const fn new(inner_radius: f32, outer_radius: f32) -> Self {
951 Self {
952 inner_circle: Circle::new(inner_radius),
953 outer_circle: Circle::new(outer_radius),
954 }
955 }
956
957 #[inline(always)]
959 pub fn diameter(&self) -> f32 {
960 self.outer_circle.diameter()
961 }
962
963 #[inline(always)]
965 pub fn thickness(&self) -> f32 {
966 self.outer_circle.radius - self.inner_circle.radius
967 }
968
969 #[inline(always)]
975 pub fn closest_point(&self, point: Vec2) -> Vec2 {
976 let distance_squared = point.length_squared();
977
978 if self.inner_circle.radius.squared() <= distance_squared {
979 if distance_squared <= self.outer_circle.radius.squared() {
980 point
982 } else {
983 let dir_to_point = point / distance_squared.sqrt();
986 self.outer_circle.radius * dir_to_point
987 }
988 } else {
989 let dir_to_point = point / distance_squared.sqrt();
992 self.inner_circle.radius * dir_to_point
993 }
994 }
995}
996
997impl Measured2d for Annulus {
998 #[inline(always)]
1000 fn area(&self) -> f32 {
1001 PI * (self.outer_circle.radius.squared() - self.inner_circle.radius.squared())
1002 }
1003
1004 #[inline(always)]
1007 #[doc(alias = "circumference")]
1008 fn perimeter(&self) -> f32 {
1009 2.0 * PI * (self.outer_circle.radius + self.inner_circle.radius)
1010 }
1011}
1012
1013#[derive(Clone, Copy, Debug, PartialEq)]
1017#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1018#[cfg_attr(
1019 feature = "bevy_reflect",
1020 derive(Reflect),
1021 reflect(Debug, PartialEq, Default)
1022)]
1023#[cfg_attr(
1024 all(feature = "serialize", feature = "bevy_reflect"),
1025 reflect(Serialize, Deserialize)
1026)]
1027#[doc(alias = "Diamond")]
1028pub struct Rhombus {
1029 pub half_diagonals: Vec2,
1031}
1032impl Primitive2d for Rhombus {}
1033
1034impl Default for Rhombus {
1035 fn default() -> Self {
1037 Self {
1038 half_diagonals: Vec2::splat(0.5),
1039 }
1040 }
1041}
1042
1043impl Rhombus {
1044 #[inline(always)]
1046 pub fn new(horizontal_diagonal: f32, vertical_diagonal: f32) -> Self {
1047 Self {
1048 half_diagonals: Vec2::new(horizontal_diagonal / 2.0, vertical_diagonal / 2.0),
1049 }
1050 }
1051
1052 #[inline(always)]
1054 pub fn from_side(side: f32) -> Self {
1055 Self {
1056 half_diagonals: Vec2::splat(side * FRAC_1_SQRT_2),
1057 }
1058 }
1059
1060 #[inline(always)]
1062 pub fn from_inradius(inradius: f32) -> Self {
1063 let half_diagonal = inradius * 2.0 / core::f32::consts::SQRT_2;
1064 Self {
1065 half_diagonals: Vec2::new(half_diagonal, half_diagonal),
1066 }
1067 }
1068
1069 #[inline(always)]
1071 pub fn side(&self) -> f32 {
1072 self.half_diagonals.length()
1073 }
1074
1075 #[inline(always)]
1078 pub fn circumradius(&self) -> f32 {
1079 self.half_diagonals.x.max(self.half_diagonals.y)
1080 }
1081
1082 #[inline(always)]
1085 #[doc(alias = "apothem")]
1086 pub fn inradius(&self) -> f32 {
1087 let side = self.side();
1088 if side == 0.0 {
1089 0.0
1090 } else {
1091 (self.half_diagonals.x * self.half_diagonals.y) / side
1092 }
1093 }
1094
1095 #[inline(always)]
1100 pub fn closest_point(&self, point: Vec2) -> Vec2 {
1101 let point_abs = point.abs();
1103 let half_diagonals = self.half_diagonals.abs(); let normal = Vec2::new(half_diagonals.y, half_diagonals.x);
1107 let normal_magnitude_squared = normal.length_squared();
1108 if normal_magnitude_squared == 0.0 {
1109 return Vec2::ZERO; }
1111
1112 let distance_unnormalised = normal.dot(point_abs) - half_diagonals.x * half_diagonals.y;
1114
1115 if distance_unnormalised <= 0.0 {
1117 return point;
1118 }
1119
1120 let mut result = point_abs - normal * distance_unnormalised / normal_magnitude_squared;
1122
1123 if result.x <= 0.0 {
1126 result = Vec2::new(0.0, half_diagonals.y);
1127 } else if result.y <= 0.0 {
1128 result = Vec2::new(half_diagonals.x, 0.0);
1129 }
1130
1131 result.copysign(point)
1133 }
1134}
1135
1136impl Measured2d for Rhombus {
1137 #[inline(always)]
1139 fn area(&self) -> f32 {
1140 2.0 * self.half_diagonals.x * self.half_diagonals.y
1141 }
1142
1143 #[inline(always)]
1145 fn perimeter(&self) -> f32 {
1146 4.0 * self.side()
1147 }
1148}
1149
1150#[derive(Clone, Copy, Debug, PartialEq)]
1153#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1154#[cfg_attr(
1155 feature = "bevy_reflect",
1156 derive(Reflect),
1157 reflect(Debug, PartialEq, Default)
1158)]
1159#[cfg_attr(
1160 all(feature = "serialize", feature = "bevy_reflect"),
1161 reflect(Serialize, Deserialize)
1162)]
1163pub struct Plane2d {
1164 pub normal: Dir2,
1166}
1167impl Primitive2d for Plane2d {}
1168
1169impl Default for Plane2d {
1170 fn default() -> Self {
1172 Self { normal: Dir2::Y }
1173 }
1174}
1175
1176impl Plane2d {
1177 #[inline(always)]
1183 pub fn new(normal: Vec2) -> Self {
1184 Self {
1185 normal: Dir2::new(normal).expect("normal must be nonzero and finite"),
1186 }
1187 }
1188}
1189
1190#[derive(Clone, Copy, Debug, PartialEq)]
1194#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1195#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
1196#[cfg_attr(
1197 all(feature = "serialize", feature = "bevy_reflect"),
1198 reflect(Serialize, Deserialize)
1199)]
1200pub struct Line2d {
1201 pub direction: Dir2,
1204}
1205impl Primitive2d for Line2d {}
1206
1207#[derive(Clone, Copy, Debug, PartialEq)]
1209#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1210#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
1211#[cfg_attr(
1212 all(feature = "serialize", feature = "bevy_reflect"),
1213 reflect(Serialize, Deserialize)
1214)]
1215#[doc(alias = "LineSegment2d")]
1216pub struct Segment2d {
1217 pub direction: Dir2,
1219 pub half_length: f32,
1222}
1223impl Primitive2d for Segment2d {}
1224
1225impl Segment2d {
1226 #[inline(always)]
1228 pub fn new(direction: Dir2, length: f32) -> Self {
1229 Self {
1230 direction,
1231 half_length: length / 2.0,
1232 }
1233 }
1234
1235 #[inline(always)]
1241 pub fn from_points(point1: Vec2, point2: Vec2) -> (Self, Vec2) {
1242 let diff = point2 - point1;
1243 let length = diff.length();
1244
1245 (
1246 Self::new(Dir2::new_unchecked(diff / length), length),
1248 (point1 + point2) / 2.,
1249 )
1250 }
1251
1252 #[inline(always)]
1254 pub fn point1(&self) -> Vec2 {
1255 *self.direction * -self.half_length
1256 }
1257
1258 #[inline(always)]
1260 pub fn point2(&self) -> Vec2 {
1261 *self.direction * self.half_length
1262 }
1263}
1264
1265#[derive(Clone, Debug, PartialEq)]
1269#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1270#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
1271#[cfg_attr(
1272 all(feature = "serialize", feature = "bevy_reflect"),
1273 reflect(Serialize, Deserialize)
1274)]
1275pub struct Polyline2d<const N: usize> {
1276 #[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))]
1278 pub vertices: [Vec2; N],
1279}
1280impl<const N: usize> Primitive2d for Polyline2d<N> {}
1281
1282impl<const N: usize> FromIterator<Vec2> for Polyline2d<N> {
1283 fn from_iter<I: IntoIterator<Item = Vec2>>(iter: I) -> Self {
1284 let mut vertices: [Vec2; N] = [Vec2::ZERO; N];
1285
1286 for (index, i) in iter.into_iter().take(N).enumerate() {
1287 vertices[index] = i;
1288 }
1289 Self { vertices }
1290 }
1291}
1292
1293impl<const N: usize> Polyline2d<N> {
1294 pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Self {
1296 Self::from_iter(vertices)
1297 }
1298}
1299
1300#[derive(Clone, Debug, PartialEq)]
1305#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1306pub struct BoxedPolyline2d {
1307 pub vertices: Box<[Vec2]>,
1309}
1310impl Primitive2d for BoxedPolyline2d {}
1311
1312impl FromIterator<Vec2> for BoxedPolyline2d {
1313 fn from_iter<I: IntoIterator<Item = Vec2>>(iter: I) -> Self {
1314 let vertices: Vec<Vec2> = iter.into_iter().collect();
1315 Self {
1316 vertices: vertices.into_boxed_slice(),
1317 }
1318 }
1319}
1320
1321impl BoxedPolyline2d {
1322 pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Self {
1324 Self::from_iter(vertices)
1325 }
1326}
1327
1328#[derive(Clone, Copy, Debug, PartialEq)]
1330#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1331#[cfg_attr(
1332 feature = "bevy_reflect",
1333 derive(Reflect),
1334 reflect(Debug, PartialEq, Default)
1335)]
1336#[cfg_attr(
1337 all(feature = "serialize", feature = "bevy_reflect"),
1338 reflect(Serialize, Deserialize)
1339)]
1340pub struct Triangle2d {
1341 pub vertices: [Vec2; 3],
1343}
1344impl Primitive2d for Triangle2d {}
1345
1346impl Default for Triangle2d {
1347 fn default() -> Self {
1349 Self {
1350 vertices: [Vec2::Y * 0.5, Vec2::new(-0.5, -0.5), Vec2::new(0.5, -0.5)],
1351 }
1352 }
1353}
1354
1355impl Triangle2d {
1356 #[inline(always)]
1358 pub const fn new(a: Vec2, b: Vec2, c: Vec2) -> Self {
1359 Self {
1360 vertices: [a, b, c],
1361 }
1362 }
1363
1364 #[inline(always)]
1366 #[doc(alias = "orientation")]
1367 pub fn winding_order(&self) -> WindingOrder {
1368 let [a, b, c] = self.vertices;
1369 let area = (b - a).perp_dot(c - a);
1370 if area > f32::EPSILON {
1371 WindingOrder::CounterClockwise
1372 } else if area < -f32::EPSILON {
1373 WindingOrder::Clockwise
1374 } else {
1375 WindingOrder::Invalid
1376 }
1377 }
1378
1379 pub fn circumcircle(&self) -> (Circle, Vec2) {
1382 let a = self.vertices[0];
1396 let (b, c) = (self.vertices[1] - a, self.vertices[2] - a);
1397 let b_length_sq = b.length_squared();
1398 let c_length_sq = c.length_squared();
1399
1400 let inv_d = (2.0 * (b.x * c.y - b.y * c.x)).recip();
1402 let ux = inv_d * (c.y * b_length_sq - b.y * c_length_sq);
1403 let uy = inv_d * (b.x * c_length_sq - c.x * b_length_sq);
1404 let u = Vec2::new(ux, uy);
1405
1406 let center = u + a;
1409 let radius = u.length();
1410
1411 (Circle { radius }, center)
1412 }
1413
1414 #[inline(always)]
1419 pub fn is_degenerate(&self) -> bool {
1420 let [a, b, c] = self.vertices;
1421 let ab = (b - a).extend(0.);
1422 let ac = (c - a).extend(0.);
1423 ab.cross(ac).length() < 10e-7
1424 }
1425
1426 #[inline(always)]
1428 pub fn is_acute(&self) -> bool {
1429 let [a, b, c] = self.vertices;
1430 let ab = b - a;
1431 let bc = c - b;
1432 let ca = a - c;
1433
1434 let mut side_lengths = [
1436 ab.length_squared(),
1437 bc.length_squared(),
1438 ca.length_squared(),
1439 ];
1440 side_lengths.sort_by(|a, b| a.partial_cmp(b).unwrap());
1441 side_lengths[0] + side_lengths[1] > side_lengths[2]
1442 }
1443
1444 #[inline(always)]
1446 pub fn is_obtuse(&self) -> bool {
1447 let [a, b, c] = self.vertices;
1448 let ab = b - a;
1449 let bc = c - b;
1450 let ca = a - c;
1451
1452 let mut side_lengths = [
1454 ab.length_squared(),
1455 bc.length_squared(),
1456 ca.length_squared(),
1457 ];
1458 side_lengths.sort_by(|a, b| a.partial_cmp(b).unwrap());
1459 side_lengths[0] + side_lengths[1] < side_lengths[2]
1460 }
1461
1462 #[inline(always)]
1465 pub fn reverse(&mut self) {
1466 self.vertices.swap(0, 2);
1467 }
1468
1469 #[inline(always)]
1471 #[must_use]
1472 pub fn reversed(mut self) -> Self {
1473 self.reverse();
1474 self
1475 }
1476}
1477
1478impl Measured2d for Triangle2d {
1479 #[inline(always)]
1481 fn area(&self) -> f32 {
1482 let [a, b, c] = self.vertices;
1483 (a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)).abs() / 2.0
1484 }
1485
1486 #[inline(always)]
1488 fn perimeter(&self) -> f32 {
1489 let [a, b, c] = self.vertices;
1490
1491 let ab = a.distance(b);
1492 let bc = b.distance(c);
1493 let ca = c.distance(a);
1494
1495 ab + bc + ca
1496 }
1497}
1498
1499#[derive(Clone, Copy, Debug, PartialEq)]
1501#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1502#[cfg_attr(
1503 feature = "bevy_reflect",
1504 derive(Reflect),
1505 reflect(Debug, PartialEq, Default)
1506)]
1507#[cfg_attr(
1508 all(feature = "serialize", feature = "bevy_reflect"),
1509 reflect(Serialize, Deserialize)
1510)]
1511#[doc(alias = "Quad")]
1512pub struct Rectangle {
1513 pub half_size: Vec2,
1515}
1516impl Primitive2d for Rectangle {}
1517
1518impl Default for Rectangle {
1519 fn default() -> Self {
1521 Self {
1522 half_size: Vec2::splat(0.5),
1523 }
1524 }
1525}
1526
1527impl Rectangle {
1528 #[inline(always)]
1530 pub fn new(width: f32, height: f32) -> Self {
1531 Self::from_size(Vec2::new(width, height))
1532 }
1533
1534 #[inline(always)]
1536 pub fn from_size(size: Vec2) -> Self {
1537 Self {
1538 half_size: size / 2.0,
1539 }
1540 }
1541
1542 #[inline(always)]
1544 pub fn from_corners(point1: Vec2, point2: Vec2) -> Self {
1545 Self {
1546 half_size: (point2 - point1).abs() / 2.0,
1547 }
1548 }
1549
1550 #[inline(always)]
1553 pub fn from_length(length: f32) -> Self {
1554 Self {
1555 half_size: Vec2::splat(length / 2.0),
1556 }
1557 }
1558
1559 #[inline(always)]
1561 pub fn size(&self) -> Vec2 {
1562 2.0 * self.half_size
1563 }
1564
1565 #[inline(always)]
1570 pub fn closest_point(&self, point: Vec2) -> Vec2 {
1571 point.clamp(-self.half_size, self.half_size)
1573 }
1574}
1575
1576impl Measured2d for Rectangle {
1577 #[inline(always)]
1579 fn area(&self) -> f32 {
1580 4.0 * self.half_size.x * self.half_size.y
1581 }
1582
1583 #[inline(always)]
1585 fn perimeter(&self) -> f32 {
1586 4.0 * (self.half_size.x + self.half_size.y)
1587 }
1588}
1589
1590#[derive(Clone, Debug, PartialEq)]
1594#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1595#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
1596#[cfg_attr(
1597 all(feature = "serialize", feature = "bevy_reflect"),
1598 reflect(Serialize, Deserialize)
1599)]
1600pub struct Polygon<const N: usize> {
1601 #[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))]
1603 pub vertices: [Vec2; N],
1604}
1605impl<const N: usize> Primitive2d for Polygon<N> {}
1606
1607impl<const N: usize> FromIterator<Vec2> for Polygon<N> {
1608 fn from_iter<I: IntoIterator<Item = Vec2>>(iter: I) -> Self {
1609 let mut vertices: [Vec2; N] = [Vec2::ZERO; N];
1610
1611 for (index, i) in iter.into_iter().take(N).enumerate() {
1612 vertices[index] = i;
1613 }
1614 Self { vertices }
1615 }
1616}
1617
1618impl<const N: usize> Polygon<N> {
1619 pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Self {
1621 Self::from_iter(vertices)
1622 }
1623}
1624
1625impl<const N: usize> From<ConvexPolygon<N>> for Polygon<N> {
1626 fn from(val: ConvexPolygon<N>) -> Self {
1627 Polygon {
1628 vertices: val.vertices,
1629 }
1630 }
1631}
1632
1633#[derive(Clone, Debug, PartialEq)]
1635#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1636#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, PartialEq))]
1637#[cfg_attr(
1638 all(feature = "serialize", feature = "bevy_reflect"),
1639 reflect(Serialize, Deserialize)
1640)]
1641pub struct ConvexPolygon<const N: usize> {
1642 #[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))]
1644 vertices: [Vec2; N],
1645}
1646impl<const N: usize> Primitive2d for ConvexPolygon<N> {}
1647
1648#[derive(Error, Display, Debug, Clone)]
1650pub enum ConvexPolygonError {
1651 #[display("The created polygon is not convex")]
1653 Concave,
1654}
1655
1656impl<const N: usize> ConvexPolygon<N> {
1657 fn triangle_winding_order(
1658 &self,
1659 a_index: usize,
1660 b_index: usize,
1661 c_index: usize,
1662 ) -> WindingOrder {
1663 let a = self.vertices[a_index];
1664 let b = self.vertices[b_index];
1665 let c = self.vertices[c_index];
1666 Triangle2d::new(a, b, c).winding_order()
1667 }
1668
1669 pub fn new(vertices: [Vec2; N]) -> Result<Self, ConvexPolygonError> {
1675 let polygon = Self::new_unchecked(vertices);
1676 let ref_winding_order = polygon.triangle_winding_order(N - 1, 0, 1);
1677 for i in 1..N {
1678 let winding_order = polygon.triangle_winding_order(i - 1, i, (i + 1) % N);
1679 if winding_order != ref_winding_order {
1680 return Err(ConvexPolygonError::Concave);
1681 }
1682 }
1683 Ok(polygon)
1684 }
1685
1686 #[inline(always)]
1689 pub fn new_unchecked(vertices: [Vec2; N]) -> Self {
1690 Self { vertices }
1691 }
1692
1693 #[inline(always)]
1695 pub fn vertices(&self) -> &[Vec2; N] {
1696 &self.vertices
1697 }
1698}
1699
1700impl<const N: usize> TryFrom<Polygon<N>> for ConvexPolygon<N> {
1701 type Error = ConvexPolygonError;
1702
1703 fn try_from(val: Polygon<N>) -> Result<Self, Self::Error> {
1704 ConvexPolygon::new(val.vertices)
1705 }
1706}
1707
1708#[derive(Clone, Debug, PartialEq)]
1713#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1714pub struct BoxedPolygon {
1715 pub vertices: Box<[Vec2]>,
1717}
1718impl Primitive2d for BoxedPolygon {}
1719
1720impl FromIterator<Vec2> for BoxedPolygon {
1721 fn from_iter<I: IntoIterator<Item = Vec2>>(iter: I) -> Self {
1722 let vertices: Vec<Vec2> = iter.into_iter().collect();
1723 Self {
1724 vertices: vertices.into_boxed_slice(),
1725 }
1726 }
1727}
1728
1729impl BoxedPolygon {
1730 pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Self {
1732 Self::from_iter(vertices)
1733 }
1734}
1735
1736#[derive(Clone, Copy, Debug, PartialEq)]
1738#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1739#[cfg_attr(
1740 feature = "bevy_reflect",
1741 derive(Reflect),
1742 reflect(Debug, PartialEq, Default)
1743)]
1744#[cfg_attr(
1745 all(feature = "serialize", feature = "bevy_reflect"),
1746 reflect(Serialize, Deserialize)
1747)]
1748pub struct RegularPolygon {
1749 pub circumcircle: Circle,
1751 pub sides: u32,
1753}
1754impl Primitive2d for RegularPolygon {}
1755
1756impl Default for RegularPolygon {
1757 fn default() -> Self {
1759 Self {
1760 circumcircle: Circle { radius: 0.5 },
1761 sides: 6,
1762 }
1763 }
1764}
1765
1766impl RegularPolygon {
1767 #[inline(always)]
1774 pub fn new(circumradius: f32, sides: u32) -> Self {
1775 assert!(
1776 circumradius.is_sign_positive(),
1777 "polygon has a negative radius"
1778 );
1779 assert!(sides > 2, "polygon has less than 3 sides");
1780
1781 Self {
1782 circumcircle: Circle {
1783 radius: circumradius,
1784 },
1785 sides,
1786 }
1787 }
1788
1789 #[inline(always)]
1792 pub fn circumradius(&self) -> f32 {
1793 self.circumcircle.radius
1794 }
1795
1796 #[inline(always)]
1800 #[doc(alias = "apothem")]
1801 pub fn inradius(&self) -> f32 {
1802 self.circumradius() * ops::cos(PI / self.sides as f32)
1803 }
1804
1805 #[inline(always)]
1807 pub fn side_length(&self) -> f32 {
1808 2.0 * self.circumradius() * ops::sin(PI / self.sides as f32)
1809 }
1810
1811 #[inline(always)]
1816 pub fn internal_angle_degrees(&self) -> f32 {
1817 (self.sides - 2) as f32 / self.sides as f32 * 180.0
1818 }
1819
1820 #[inline(always)]
1825 pub fn internal_angle_radians(&self) -> f32 {
1826 (self.sides - 2) as f32 * PI / self.sides as f32
1827 }
1828
1829 #[inline(always)]
1834 pub fn external_angle_degrees(&self) -> f32 {
1835 360.0 / self.sides as f32
1836 }
1837
1838 #[inline(always)]
1843 pub fn external_angle_radians(&self) -> f32 {
1844 2.0 * PI / self.sides as f32
1845 }
1846
1847 pub fn vertices(self, rotation: f32) -> impl IntoIterator<Item = Vec2> {
1852 let start_angle = rotation + FRAC_PI_2;
1854 let step = core::f32::consts::TAU / self.sides as f32;
1855
1856 (0..self.sides).map(move |i| {
1857 let theta = start_angle + i as f32 * step;
1858 let (sin, cos) = ops::sin_cos(theta);
1859 Vec2::new(cos, sin) * self.circumcircle.radius
1860 })
1861 }
1862}
1863
1864impl Measured2d for RegularPolygon {
1865 #[inline(always)]
1867 fn area(&self) -> f32 {
1868 let angle: f32 = 2.0 * PI / (self.sides as f32);
1869 (self.sides as f32) * self.circumradius().squared() * ops::sin(angle) / 2.0
1870 }
1871
1872 #[inline(always)]
1875 fn perimeter(&self) -> f32 {
1876 self.sides as f32 * self.side_length()
1877 }
1878}
1879
1880#[derive(Clone, Copy, Debug, PartialEq)]
1884#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1885#[cfg_attr(
1886 feature = "bevy_reflect",
1887 derive(Reflect),
1888 reflect(Debug, PartialEq, Default)
1889)]
1890#[cfg_attr(
1891 all(feature = "serialize", feature = "bevy_reflect"),
1892 reflect(Serialize, Deserialize)
1893)]
1894#[doc(alias = "stadium", alias = "pill")]
1895pub struct Capsule2d {
1896 pub radius: f32,
1898 pub half_length: f32,
1900}
1901impl Primitive2d for Capsule2d {}
1902
1903impl Default for Capsule2d {
1904 fn default() -> Self {
1907 Self {
1908 radius: 0.5,
1909 half_length: 0.5,
1910 }
1911 }
1912}
1913
1914impl Capsule2d {
1915 pub fn new(radius: f32, length: f32) -> Self {
1917 Self {
1918 radius,
1919 half_length: length / 2.0,
1920 }
1921 }
1922
1923 #[inline]
1925 pub fn to_inner_rectangle(&self) -> Rectangle {
1926 Rectangle::new(self.radius * 2.0, self.half_length * 2.0)
1927 }
1928}
1929
1930impl Measured2d for Capsule2d {
1931 #[inline]
1933 fn area(&self) -> f32 {
1934 PI * self.radius.squared() + self.to_inner_rectangle().area()
1936 }
1937
1938 #[inline]
1940 fn perimeter(&self) -> f32 {
1941 2.0 * PI * self.radius + 4.0 * self.half_length
1943 }
1944}
1945
1946#[cfg(test)]
1947mod tests {
1948 use super::*;
1951 #[test]
1954 fn rectangle_closest_point() {
1955 let rectangle = Rectangle::new(2.0, 2.0);
1956 assert_eq!(rectangle.closest_point(Vec2::X * 10.0), Vec2::X);
1957 assert_eq!(rectangle.closest_point(Vec2::NEG_ONE * 10.0), Vec2::NEG_ONE);
1958 assert_eq!(
1959 rectangle.closest_point(Vec2::new(0.25, 0.1)),
1960 Vec2::new(0.25, 0.1)
1961 );
1962 }
1963
1964 #[test]
1965 fn circle_closest_point() {
1966 let circle = Circle { radius: 1.0 };
1967 assert_eq!(circle.closest_point(Vec2::X * 10.0), Vec2::X);
1968 assert_eq!(
1969 circle.closest_point(Vec2::NEG_ONE * 10.0),
1970 Vec2::NEG_ONE.normalize()
1971 );
1972 assert_eq!(
1973 circle.closest_point(Vec2::new(0.25, 0.1)),
1974 Vec2::new(0.25, 0.1)
1975 );
1976 }
1977
1978 #[test]
1979 fn annulus_closest_point() {
1980 let annulus = Annulus::new(1.5, 2.0);
1981 assert_eq!(annulus.closest_point(Vec2::X * 10.0), Vec2::X * 2.0);
1982 assert_eq!(
1983 annulus.closest_point(Vec2::NEG_ONE),
1984 Vec2::NEG_ONE.normalize() * 1.5
1985 );
1986 assert_eq!(
1987 annulus.closest_point(Vec2::new(1.55, 0.85)),
1988 Vec2::new(1.55, 0.85)
1989 );
1990 }
1991
1992 #[test]
1993 fn rhombus_closest_point() {
1994 let rhombus = Rhombus::new(2.0, 1.0);
1995 assert_eq!(rhombus.closest_point(Vec2::X * 10.0), Vec2::X);
1996 assert_eq!(
1997 rhombus.closest_point(Vec2::NEG_ONE * 0.2),
1998 Vec2::NEG_ONE * 0.2
1999 );
2000 assert_eq!(
2001 rhombus.closest_point(Vec2::new(-0.55, 0.35)),
2002 Vec2::new(-0.5, 0.25)
2003 );
2004
2005 let rhombus = Rhombus::new(0.0, 0.0);
2006 assert_eq!(rhombus.closest_point(Vec2::X * 10.0), Vec2::ZERO);
2007 assert_eq!(rhombus.closest_point(Vec2::NEG_ONE * 0.2), Vec2::ZERO);
2008 assert_eq!(rhombus.closest_point(Vec2::new(-0.55, 0.35)), Vec2::ZERO);
2009 }
2010
2011 #[test]
2012 fn circle_math() {
2013 let circle = Circle { radius: 3.0 };
2014 assert_eq!(circle.diameter(), 6.0, "incorrect diameter");
2015 assert_eq!(circle.area(), 28.274334, "incorrect area");
2016 assert_eq!(circle.perimeter(), 18.849556, "incorrect perimeter");
2017 }
2018
2019 #[test]
2020 fn capsule_math() {
2021 let capsule = Capsule2d::new(2.0, 9.0);
2022 assert_eq!(
2023 capsule.to_inner_rectangle(),
2024 Rectangle::new(4.0, 9.0),
2025 "rectangle wasn't created correctly from a capsule"
2026 );
2027 assert_eq!(capsule.area(), 48.566371, "incorrect area");
2028 assert_eq!(capsule.perimeter(), 30.566371, "incorrect perimeter");
2029 }
2030
2031 #[test]
2032 fn annulus_math() {
2033 let annulus = Annulus::new(2.5, 3.5);
2034 assert_eq!(annulus.diameter(), 7.0, "incorrect diameter");
2035 assert_eq!(annulus.thickness(), 1.0, "incorrect thickness");
2036 assert_eq!(annulus.area(), 18.849556, "incorrect area");
2037 assert_eq!(annulus.perimeter(), 37.699112, "incorrect perimeter");
2038 }
2039
2040 #[test]
2041 fn rhombus_math() {
2042 let rhombus = Rhombus::new(3.0, 4.0);
2043 assert_eq!(rhombus.area(), 6.0, "incorrect area");
2044 assert_eq!(rhombus.perimeter(), 10.0, "incorrect perimeter");
2045 assert_eq!(rhombus.side(), 2.5, "incorrect side");
2046 assert_eq!(rhombus.inradius(), 1.2, "incorrect inradius");
2047 assert_eq!(rhombus.circumradius(), 2.0, "incorrect circumradius");
2048 let rhombus = Rhombus::new(0.0, 0.0);
2049 assert_eq!(rhombus.area(), 0.0, "incorrect area");
2050 assert_eq!(rhombus.perimeter(), 0.0, "incorrect perimeter");
2051 assert_eq!(rhombus.side(), 0.0, "incorrect side");
2052 assert_eq!(rhombus.inradius(), 0.0, "incorrect inradius");
2053 assert_eq!(rhombus.circumradius(), 0.0, "incorrect circumradius");
2054 let rhombus = Rhombus::from_side(core::f32::consts::SQRT_2);
2055 }
2061
2062 #[test]
2063 fn ellipse_math() {
2064 let ellipse = Ellipse::new(3.0, 1.0);
2065 assert_eq!(ellipse.area(), 9.424778, "incorrect area");
2066
2067 assert_eq!(ellipse.eccentricity(), 0.94280905, "incorrect eccentricity");
2068
2069 let line = Ellipse::new(1., 0.);
2070 assert_eq!(line.eccentricity(), 1., "incorrect line eccentricity");
2071
2072 let circle = Ellipse::new(2., 2.);
2073 assert_eq!(circle.eccentricity(), 0., "incorrect circle eccentricity");
2074 }
2075
2076 #[test]
2077 fn ellipse_perimeter() {
2078 let circle = Ellipse::new(1., 1.);
2079 let line = Ellipse::new(75_000., 0.5);
2082 let ellipse = Ellipse::new(0.5, 2.);
2085 let ellipse = Ellipse::new(5., 3.);
2088 }
2090
2091 #[test]
2092 fn triangle_math() {
2093 let triangle = Triangle2d::new(
2094 Vec2::new(-2.0, -1.0),
2095 Vec2::new(1.0, 4.0),
2096 Vec2::new(7.0, 0.0),
2097 );
2098 assert_eq!(triangle.area(), 21.0, "incorrect area");
2099 assert_eq!(triangle.perimeter(), 22.097439, "incorrect perimeter");
2100
2101 let degenerate_triangle =
2102 Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(0., 0.), Vec2::new(1., 0.));
2103 assert!(degenerate_triangle.is_degenerate());
2104
2105 let acute_triangle =
2106 Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(1., 0.), Vec2::new(0., 5.));
2107 let obtuse_triangle =
2108 Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(1., 0.), Vec2::new(0., 0.5));
2109
2110 assert!(acute_triangle.is_acute());
2111 assert!(!acute_triangle.is_obtuse());
2112 assert!(!obtuse_triangle.is_acute());
2113 assert!(obtuse_triangle.is_obtuse());
2114 }
2115
2116 #[test]
2117 fn triangle_winding_order() {
2118 let mut cw_triangle = Triangle2d::new(
2119 Vec2::new(0.0, 2.0),
2120 Vec2::new(-0.5, -1.2),
2121 Vec2::new(-1.0, -1.0),
2122 );
2123 assert_eq!(cw_triangle.winding_order(), WindingOrder::Clockwise);
2124
2125 let ccw_triangle = Triangle2d::new(
2126 Vec2::new(-1.0, -1.0),
2127 Vec2::new(-0.5, -1.2),
2128 Vec2::new(0.0, 2.0),
2129 );
2130 assert_eq!(ccw_triangle.winding_order(), WindingOrder::CounterClockwise);
2131
2132 cw_triangle.reverse();
2135 assert_eq!(cw_triangle, ccw_triangle);
2136
2137 let invalid_triangle = Triangle2d::new(
2138 Vec2::new(0.0, 2.0),
2139 Vec2::new(0.0, -1.0),
2140 Vec2::new(0.0, -1.2),
2141 );
2142 assert_eq!(invalid_triangle.winding_order(), WindingOrder::Invalid);
2143 }
2144
2145 #[test]
2146 fn rectangle_math() {
2147 let rectangle = Rectangle::new(3.0, 7.0);
2148 assert_eq!(
2149 rectangle,
2150 Rectangle::from_corners(Vec2::new(-1.5, -3.5), Vec2::new(1.5, 3.5))
2151 );
2152 assert_eq!(rectangle.area(), 21.0, "incorrect area");
2153 assert_eq!(rectangle.perimeter(), 20.0, "incorrect perimeter");
2154 }
2155
2156 #[test]
2157 fn regular_polygon_math() {
2158 let polygon = RegularPolygon::new(3.0, 6);
2159 assert_eq!(polygon.inradius(), 2.598076, "incorrect inradius");
2160 assert_eq!(polygon.side_length(), 3.0, "incorrect side length");
2161 assert_eq!(polygon.perimeter(), 18.0, "incorrect perimeter");
2163 assert_eq!(
2164 polygon.internal_angle_degrees(),
2165 120.0,
2166 "incorrect internal angle"
2167 );
2168 assert_eq!(
2169 polygon.internal_angle_radians(),
2170 120_f32.to_radians(),
2171 "incorrect internal angle"
2172 );
2173 assert_eq!(
2174 polygon.external_angle_degrees(),
2175 60.0,
2176 "incorrect external angle"
2177 );
2178 assert_eq!(
2179 polygon.external_angle_radians(),
2180 60_f32.to_radians(),
2181 "incorrect external angle"
2182 );
2183 }
2184
2185 #[test]
2186 fn triangle_circumcenter() {
2187 let triangle = Triangle2d::new(
2188 Vec2::new(10.0, 2.0),
2189 Vec2::new(-5.0, -3.0),
2190 Vec2::new(2.0, -1.0),
2191 );
2192 let (Circle { radius }, circumcenter) = triangle.circumcircle();
2193
2194 assert_eq!(radius, 98.34887);
2196 assert_eq!(circumcenter, Vec2::new(-28.5, 92.5));
2197 }
2198
2199 #[test]
2200 fn regular_polygon_vertices() {
2201 let polygon = RegularPolygon::new(1.0, 4);
2202
2203 let mut vertices = polygon.vertices(0.0).into_iter();
2205 assert!((vertices.next().unwrap() - Vec2::Y).length() < 1e-7);
2206
2207 let mut rotated_vertices = polygon.vertices(core::f32::consts::FRAC_PI_4).into_iter();
2209
2210 let side_sistance = FRAC_1_SQRT_2;
2212 assert!(
2213 (rotated_vertices.next().unwrap() - Vec2::new(-side_sistance, side_sistance)).length()
2214 < 1e-7,
2215 );
2216 }
2217}