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 super::{Measured2d, Primitive2d, WindingOrder};
7use crate::{
8 ops::{self, FloatPow},
9 Dir2, InvalidDirectionError, Isometry2d, Ray2d, 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(always)]
52 pub const fn new(radius: f32) -> Self {
53 Self { radius }
54 }
55
56 #[inline(always)]
58 pub const fn diameter(&self) -> f32 {
59 2.0 * self.radius
60 }
61
62 #[inline(always)]
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(always)]
85 fn area(&self) -> f32 {
86 PI * self.radius.squared()
87 }
88
89 #[inline(always)]
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(always)]
145 pub const fn new(radius: f32, half_angle: f32) -> Self {
146 Self { radius, half_angle }
147 }
148
149 #[inline(always)]
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(always)]
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(always)]
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(always)]
180 pub const fn angle(&self) -> f32 {
181 self.half_angle * 2.0
182 }
183
184 #[inline(always)]
186 pub const fn length(&self) -> f32 {
187 self.angle() * self.radius
188 }
189
190 #[inline(always)]
192 pub fn right_endpoint(&self) -> Vec2 {
193 self.radius * Vec2::from_angle(FRAC_PI_2 - self.half_angle)
194 }
195
196 #[inline(always)]
198 pub fn left_endpoint(&self) -> Vec2 {
199 self.radius * Vec2::from_angle(FRAC_PI_2 + self.half_angle)
200 }
201
202 #[inline(always)]
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(always)]
216 pub fn half_chord_length(&self) -> f32 {
217 self.radius * ops::sin(self.half_angle)
218 }
219
220 #[inline(always)]
222 pub fn chord_length(&self) -> f32 {
223 2.0 * self.half_chord_length()
224 }
225
226 #[inline(always)]
228 pub fn chord_midpoint(&self) -> Vec2 {
229 self.apothem() * Vec2::Y
230 }
231
232 #[inline(always)]
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(always)]
259 pub const fn is_minor(&self) -> bool {
260 self.half_angle <= FRAC_PI_2
261 }
262
263 #[inline(always)]
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(always)]
308 fn area(&self) -> f32 {
309 self.arc.radius.squared() * self.arc.half_angle
310 }
311
312 #[inline(always)]
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(always)]
325 pub const fn new(radius: f32, angle: f32) -> Self {
326 Self {
327 arc: Arc2d::new(radius, angle),
328 }
329 }
330
331 #[inline(always)]
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(always)]
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(always)]
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(always)]
359 pub const fn half_angle(&self) -> f32 {
360 self.arc.half_angle
361 }
362
363 #[inline(always)]
365 pub const fn angle(&self) -> f32 {
366 self.arc.angle()
367 }
368
369 #[inline(always)]
371 pub const fn radius(&self) -> f32 {
372 self.arc.radius
373 }
374
375 #[inline(always)]
377 pub const fn arc_length(&self) -> f32 {
378 self.arc.length()
379 }
380
381 #[inline(always)]
385 pub fn half_chord_length(&self) -> f32 {
386 self.arc.half_chord_length()
387 }
388
389 #[inline(always)]
393 pub fn chord_length(&self) -> f32 {
394 self.arc.chord_length()
395 }
396
397 #[inline(always)]
401 pub fn chord_midpoint(&self) -> Vec2 {
402 self.arc.chord_midpoint()
403 }
404
405 #[inline(always)]
409 pub fn apothem(&self) -> f32 {
410 self.arc.apothem()
411 }
412
413 #[inline(always)]
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(always)]
460 fn area(&self) -> f32 {
461 0.5 * self.arc.radius.squared() * (self.arc.angle() - ops::sin(self.arc.angle()))
462 }
463
464 #[inline(always)]
465 fn perimeter(&self) -> f32 {
466 self.chord_length() + self.arc_length()
467 }
468}
469
470impl CircularSegment {
471 #[inline(always)]
473 pub const fn new(radius: f32, angle: f32) -> Self {
474 Self {
475 arc: Arc2d::new(radius, angle),
476 }
477 }
478
479 #[inline(always)]
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(always)]
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(always)]
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(always)]
507 pub const fn half_angle(&self) -> f32 {
508 self.arc.half_angle
509 }
510
511 #[inline(always)]
513 pub const fn angle(&self) -> f32 {
514 self.arc.angle()
515 }
516
517 #[inline(always)]
519 pub const fn radius(&self) -> f32 {
520 self.arc.radius
521 }
522
523 #[inline(always)]
525 pub const fn arc_length(&self) -> f32 {
526 self.arc.length()
527 }
528
529 #[inline(always)]
531 #[doc(alias = "half_base_length")]
532 pub fn half_chord_length(&self) -> f32 {
533 self.arc.half_chord_length()
534 }
535
536 #[inline(always)]
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(always)]
546 #[doc(alias = "base_midpoint")]
547 pub fn chord_midpoint(&self) -> Vec2 {
548 self.arc.chord_midpoint()
549 }
550
551 #[inline(always)]
556 pub fn apothem(&self) -> f32 {
557 self.arc.apothem()
558 }
559
560 #[inline(always)]
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)]
797#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
798#[cfg_attr(
799 feature = "bevy_reflect",
800 derive(Reflect),
801 reflect(Debug, PartialEq, Default, Clone)
802)]
803#[cfg_attr(
804 all(feature = "serialize", feature = "bevy_reflect"),
805 reflect(Serialize, Deserialize)
806)]
807pub struct Ellipse {
808 pub half_size: Vec2,
812}
813
814impl Primitive2d for Ellipse {}
815
816impl Default for Ellipse {
817 fn default() -> Self {
819 Self {
820 half_size: Vec2::new(1.0, 0.5),
821 }
822 }
823}
824
825impl Ellipse {
826 #[inline(always)]
830 pub const fn new(half_width: f32, half_height: f32) -> Self {
831 Self {
832 half_size: Vec2::new(half_width, half_height),
833 }
834 }
835
836 #[inline(always)]
840 pub const fn from_size(size: Vec2) -> Self {
841 Self {
842 half_size: Vec2::new(size.x / 2.0, size.y / 2.0),
843 }
844 }
845
846 #[inline(always)]
847 pub fn eccentricity(&self) -> f32 {
852 let a = self.semi_major();
853 let b = self.semi_minor();
854
855 ops::sqrt(a * a - b * b) / a
856 }
857
858 #[inline(always)]
859 pub fn focal_length(&self) -> f32 {
863 let a = self.semi_major();
864 let b = self.semi_minor();
865
866 ops::sqrt(a * a - b * b)
867 }
868
869 #[inline(always)]
871 pub fn semi_major(&self) -> f32 {
872 self.half_size.max_element()
873 }
874
875 #[inline(always)]
877 pub fn semi_minor(&self) -> f32 {
878 self.half_size.min_element()
879 }
880}
881
882impl Measured2d for Ellipse {
883 #[inline(always)]
885 fn area(&self) -> f32 {
886 PI * self.half_size.x * self.half_size.y
887 }
888
889 #[inline(always)]
890 fn perimeter(&self) -> f32 {
894 let a = self.semi_major();
895 let b = self.semi_minor();
896
897 if a / b - 1. < 1e-5 {
899 return PI * (a + b);
900 };
901
902 if a / b > 1e4 {
904 return 4. * a;
905 };
906
907 const BINOMIAL_COEFFICIENTS: [f32; 21] = [
911 1.,
912 0.25,
913 0.015625,
914 0.00390625,
915 0.0015258789,
916 0.00074768066,
917 0.00042057037,
918 0.00025963783,
919 0.00017140154,
920 0.000119028846,
921 0.00008599834,
922 0.00006414339,
923 0.000049109784,
924 0.000038430585,
925 0.000030636627,
926 0.000024815668,
927 0.000020380836,
928 0.000016942893,
929 0.000014236736,
930 0.000012077564,
931 0.000010333865,
932 ];
933
934 let h = ((a - b) / (a + b)).squared();
938
939 PI * (a + b)
940 * (0..=20)
941 .map(|i| BINOMIAL_COEFFICIENTS[i] * ops::powf(h, i as f32))
942 .sum::<f32>()
943 }
944}
945
946#[derive(Clone, Copy, Debug, PartialEq)]
948#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
949#[cfg_attr(
950 feature = "bevy_reflect",
951 derive(Reflect),
952 reflect(Debug, PartialEq, Default, Clone)
953)]
954#[cfg_attr(
955 all(feature = "serialize", feature = "bevy_reflect"),
956 reflect(Serialize, Deserialize)
957)]
958#[doc(alias = "Ring")]
959pub struct Annulus {
960 pub inner_circle: Circle,
962 pub outer_circle: Circle,
964}
965
966impl Primitive2d for Annulus {}
967
968impl Default for Annulus {
969 fn default() -> Self {
971 Self {
972 inner_circle: Circle::new(0.5),
973 outer_circle: Circle::new(1.0),
974 }
975 }
976}
977
978impl Annulus {
979 #[inline(always)]
981 pub const fn new(inner_radius: f32, outer_radius: f32) -> Self {
982 Self {
983 inner_circle: Circle::new(inner_radius),
984 outer_circle: Circle::new(outer_radius),
985 }
986 }
987
988 #[inline(always)]
990 pub const fn diameter(&self) -> f32 {
991 self.outer_circle.diameter()
992 }
993
994 #[inline(always)]
996 pub const fn thickness(&self) -> f32 {
997 self.outer_circle.radius - self.inner_circle.radius
998 }
999
1000 #[inline(always)]
1006 pub fn closest_point(&self, point: Vec2) -> Vec2 {
1007 let distance_squared = point.length_squared();
1008
1009 if self.inner_circle.radius.squared() <= distance_squared {
1010 if distance_squared <= self.outer_circle.radius.squared() {
1011 point
1013 } else {
1014 let dir_to_point = point / ops::sqrt(distance_squared);
1017 self.outer_circle.radius * dir_to_point
1018 }
1019 } else {
1020 let dir_to_point = point / ops::sqrt(distance_squared);
1023 self.inner_circle.radius * dir_to_point
1024 }
1025 }
1026}
1027
1028impl Measured2d for Annulus {
1029 #[inline(always)]
1031 fn area(&self) -> f32 {
1032 PI * (self.outer_circle.radius.squared() - self.inner_circle.radius.squared())
1033 }
1034
1035 #[inline(always)]
1038 #[doc(alias = "circumference")]
1039 fn perimeter(&self) -> f32 {
1040 2.0 * PI * (self.outer_circle.radius + self.inner_circle.radius)
1041 }
1042}
1043
1044#[derive(Clone, Copy, Debug, PartialEq)]
1048#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1049#[cfg_attr(
1050 feature = "bevy_reflect",
1051 derive(Reflect),
1052 reflect(Debug, PartialEq, Default, Clone)
1053)]
1054#[cfg_attr(
1055 all(feature = "serialize", feature = "bevy_reflect"),
1056 reflect(Serialize, Deserialize)
1057)]
1058#[doc(alias = "Diamond")]
1059pub struct Rhombus {
1060 pub half_diagonals: Vec2,
1062}
1063
1064impl Primitive2d for Rhombus {}
1065
1066impl Default for Rhombus {
1067 fn default() -> Self {
1069 Self {
1070 half_diagonals: Vec2::splat(0.5),
1071 }
1072 }
1073}
1074
1075impl Rhombus {
1076 #[inline(always)]
1078 pub const fn new(horizontal_diagonal: f32, vertical_diagonal: f32) -> Self {
1079 Self {
1080 half_diagonals: Vec2::new(horizontal_diagonal / 2.0, vertical_diagonal / 2.0),
1081 }
1082 }
1083
1084 #[inline(always)]
1086 pub const fn from_side(side: f32) -> Self {
1087 Self {
1088 half_diagonals: Vec2::splat(side * FRAC_1_SQRT_2),
1089 }
1090 }
1091
1092 #[inline(always)]
1094 pub const fn from_inradius(inradius: f32) -> Self {
1095 let half_diagonal = inradius * 2.0 / core::f32::consts::SQRT_2;
1096 Self {
1097 half_diagonals: Vec2::new(half_diagonal, half_diagonal),
1098 }
1099 }
1100
1101 #[inline(always)]
1103 pub fn side(&self) -> f32 {
1104 self.half_diagonals.length()
1105 }
1106
1107 #[inline(always)]
1110 pub const fn circumradius(&self) -> f32 {
1111 self.half_diagonals.x.max(self.half_diagonals.y)
1112 }
1113
1114 #[inline(always)]
1117 #[doc(alias = "apothem")]
1118 pub fn inradius(&self) -> f32 {
1119 let side = self.side();
1120 if side == 0.0 {
1121 0.0
1122 } else {
1123 (self.half_diagonals.x * self.half_diagonals.y) / side
1124 }
1125 }
1126
1127 #[inline(always)]
1132 pub fn closest_point(&self, point: Vec2) -> Vec2 {
1133 let point_abs = point.abs();
1135 let half_diagonals = self.half_diagonals.abs(); let normal = Vec2::new(half_diagonals.y, half_diagonals.x);
1139 let normal_magnitude_squared = normal.length_squared();
1140 if normal_magnitude_squared == 0.0 {
1141 return Vec2::ZERO; }
1143
1144 let distance_unnormalised = normal.dot(point_abs) - half_diagonals.x * half_diagonals.y;
1146
1147 if distance_unnormalised <= 0.0 {
1149 return point;
1150 }
1151
1152 let mut result = point_abs - normal * distance_unnormalised / normal_magnitude_squared;
1154
1155 if result.x <= 0.0 {
1158 result = Vec2::new(0.0, half_diagonals.y);
1159 } else if result.y <= 0.0 {
1160 result = Vec2::new(half_diagonals.x, 0.0);
1161 }
1162
1163 result.copysign(point)
1165 }
1166}
1167
1168impl Measured2d for Rhombus {
1169 #[inline(always)]
1171 fn area(&self) -> f32 {
1172 2.0 * self.half_diagonals.x * self.half_diagonals.y
1173 }
1174
1175 #[inline(always)]
1177 fn perimeter(&self) -> f32 {
1178 4.0 * self.side()
1179 }
1180}
1181
1182#[derive(Clone, Copy, Debug, PartialEq)]
1185#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1186#[cfg_attr(
1187 feature = "bevy_reflect",
1188 derive(Reflect),
1189 reflect(Debug, PartialEq, Default, Clone)
1190)]
1191#[cfg_attr(
1192 all(feature = "serialize", feature = "bevy_reflect"),
1193 reflect(Serialize, Deserialize)
1194)]
1195pub struct Plane2d {
1196 pub normal: Dir2,
1198}
1199
1200impl Primitive2d for Plane2d {}
1201
1202impl Default for Plane2d {
1203 fn default() -> Self {
1205 Self { normal: Dir2::Y }
1206 }
1207}
1208
1209impl Plane2d {
1210 #[inline(always)]
1216 pub fn new(normal: Vec2) -> Self {
1217 Self {
1218 normal: Dir2::new(normal).expect("normal must be nonzero and finite"),
1219 }
1220 }
1221}
1222
1223#[derive(Clone, Copy, Debug, PartialEq)]
1227#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1228#[cfg_attr(
1229 feature = "bevy_reflect",
1230 derive(Reflect),
1231 reflect(Debug, PartialEq, Clone)
1232)]
1233#[cfg_attr(
1234 all(feature = "serialize", feature = "bevy_reflect"),
1235 reflect(Serialize, Deserialize)
1236)]
1237pub struct Line2d {
1238 pub direction: Dir2,
1241}
1242
1243impl Primitive2d for Line2d {}
1244
1245#[derive(Clone, Copy, Debug, PartialEq)]
1247#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1248#[cfg_attr(
1249 feature = "bevy_reflect",
1250 derive(Reflect),
1251 reflect(Debug, PartialEq, Clone)
1252)]
1253#[cfg_attr(
1254 all(feature = "serialize", feature = "bevy_reflect"),
1255 reflect(Serialize, Deserialize)
1256)]
1257#[doc(alias = "LineSegment2d")]
1258pub struct Segment2d {
1259 pub vertices: [Vec2; 2],
1261}
1262
1263impl Primitive2d for Segment2d {}
1264
1265impl Default for Segment2d {
1266 fn default() -> Self {
1267 Self {
1268 vertices: [Vec2::new(-0.5, 0.0), Vec2::new(0.5, 0.0)],
1269 }
1270 }
1271}
1272
1273impl Segment2d {
1274 #[inline(always)]
1276 pub const fn new(point1: Vec2, point2: Vec2) -> Self {
1277 Self {
1278 vertices: [point1, point2],
1279 }
1280 }
1281
1282 #[inline(always)]
1286 pub fn from_direction_and_length(direction: Dir2, length: f32) -> Self {
1287 let endpoint = 0.5 * length * direction;
1288 Self {
1289 vertices: [-endpoint, endpoint],
1290 }
1291 }
1292
1293 #[inline(always)]
1298 pub fn from_scaled_direction(scaled_direction: Vec2) -> Self {
1299 let endpoint = 0.5 * scaled_direction;
1300 Self {
1301 vertices: [-endpoint, endpoint],
1302 }
1303 }
1304
1305 #[inline(always)]
1310 pub fn from_ray_and_length(ray: Ray2d, length: f32) -> Self {
1311 Self {
1312 vertices: [ray.origin, ray.get_point(length)],
1313 }
1314 }
1315
1316 #[inline(always)]
1318 pub const fn point1(&self) -> Vec2 {
1319 self.vertices[0]
1320 }
1321
1322 #[inline(always)]
1324 pub const fn point2(&self) -> Vec2 {
1325 self.vertices[1]
1326 }
1327
1328 #[inline(always)]
1330 #[doc(alias = "midpoint")]
1331 pub fn center(&self) -> Vec2 {
1332 self.point1().midpoint(self.point2())
1333 }
1334
1335 #[inline(always)]
1337 pub fn length(&self) -> f32 {
1338 self.point1().distance(self.point2())
1339 }
1340
1341 #[inline(always)]
1343 pub fn length_squared(&self) -> f32 {
1344 self.point1().distance_squared(self.point2())
1345 }
1346
1347 #[inline(always)]
1355 pub fn direction(&self) -> Dir2 {
1356 self.try_direction().unwrap_or_else(|err| {
1357 panic!("Failed to compute the direction of a line segment: {err}")
1358 })
1359 }
1360
1361 #[inline(always)]
1366 pub fn try_direction(&self) -> Result<Dir2, InvalidDirectionError> {
1367 Dir2::new(self.scaled_direction())
1368 }
1369
1370 #[inline(always)]
1372 pub fn scaled_direction(&self) -> Vec2 {
1373 self.point2() - self.point1()
1374 }
1375
1376 #[inline(always)]
1384 pub fn left_normal(&self) -> Dir2 {
1385 self.try_left_normal().unwrap_or_else(|err| {
1386 panic!("Failed to compute the left-hand side normal of a line segment: {err}")
1387 })
1388 }
1389
1390 #[inline(always)]
1395 pub fn try_left_normal(&self) -> Result<Dir2, InvalidDirectionError> {
1396 Dir2::new(self.scaled_left_normal())
1397 }
1398
1399 #[inline(always)]
1403 pub fn scaled_left_normal(&self) -> Vec2 {
1404 let scaled_direction = self.scaled_direction();
1405 Vec2::new(-scaled_direction.y, scaled_direction.x)
1406 }
1407
1408 #[inline(always)]
1416 pub fn right_normal(&self) -> Dir2 {
1417 self.try_right_normal().unwrap_or_else(|err| {
1418 panic!("Failed to compute the right-hand side normal of a line segment: {err}")
1419 })
1420 }
1421
1422 #[inline(always)]
1427 pub fn try_right_normal(&self) -> Result<Dir2, InvalidDirectionError> {
1428 Dir2::new(self.scaled_right_normal())
1429 }
1430
1431 #[inline(always)]
1435 pub fn scaled_right_normal(&self) -> Vec2 {
1436 let scaled_direction = self.scaled_direction();
1437 Vec2::new(scaled_direction.y, -scaled_direction.x)
1438 }
1439
1440 #[inline(always)]
1442 pub fn transformed(&self, isometry: impl Into<Isometry2d>) -> Self {
1443 let isometry: Isometry2d = isometry.into();
1444 Self::new(
1445 isometry.transform_point(self.point1()),
1446 isometry.transform_point(self.point2()),
1447 )
1448 }
1449
1450 #[inline(always)]
1452 pub fn translated(&self, translation: Vec2) -> Segment2d {
1453 Self::new(self.point1() + translation, self.point2() + translation)
1454 }
1455
1456 #[inline(always)]
1458 pub fn rotated(&self, rotation: Rot2) -> Segment2d {
1459 Segment2d::new(rotation * self.point1(), rotation * self.point2())
1460 }
1461
1462 #[inline(always)]
1464 pub fn rotated_around(&self, rotation: Rot2, point: Vec2) -> Segment2d {
1465 let offset = self.translated(-point);
1467 let rotated = offset.rotated(rotation);
1468 rotated.translated(point)
1469 }
1470
1471 #[inline(always)]
1473 pub fn rotated_around_center(&self, rotation: Rot2) -> Segment2d {
1474 self.rotated_around(rotation, self.center())
1475 }
1476
1477 #[inline(always)]
1479 pub fn centered(&self) -> Segment2d {
1480 let center = self.center();
1481 self.translated(-center)
1482 }
1483
1484 #[inline(always)]
1486 pub fn resized(&self, length: f32) -> Segment2d {
1487 let offset_from_origin = self.center();
1488 let centered = self.translated(-offset_from_origin);
1489 let ratio = length / self.length();
1490 let segment = Segment2d::new(centered.point1() * ratio, centered.point2() * ratio);
1491 segment.translated(offset_from_origin)
1492 }
1493
1494 #[inline(always)]
1496 pub fn reverse(&mut self) {
1497 let [point1, point2] = &mut self.vertices;
1498 core::mem::swap(point1, point2);
1499 }
1500
1501 #[inline(always)]
1503 #[must_use]
1504 pub fn reversed(mut self) -> Self {
1505 self.reverse();
1506 self
1507 }
1508
1509 #[inline(always)]
1511 pub fn closest_point(&self, point: Vec2) -> Vec2 {
1512 let segment_vector = self.vertices[1] - self.vertices[0];
1521 let offset = point - self.vertices[0];
1522 let projection_scaled = segment_vector.dot(offset);
1524
1525 if projection_scaled <= 0.0 {
1527 return self.vertices[0];
1528 }
1529
1530 let length_squared = segment_vector.length_squared();
1531 if projection_scaled >= length_squared {
1533 return self.vertices[1];
1534 }
1535
1536 let t = projection_scaled / length_squared;
1538 self.vertices[0] + t * segment_vector
1539 }
1540}
1541
1542impl From<[Vec2; 2]> for Segment2d {
1543 #[inline(always)]
1544 fn from(vertices: [Vec2; 2]) -> Self {
1545 Self { vertices }
1546 }
1547}
1548
1549impl From<(Vec2, Vec2)> for Segment2d {
1550 #[inline(always)]
1551 fn from((point1, point2): (Vec2, Vec2)) -> Self {
1552 Self::new(point1, point2)
1553 }
1554}
1555
1556#[cfg(feature = "alloc")]
1558#[derive(Clone, Debug, PartialEq)]
1559#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1560#[cfg_attr(
1561 feature = "bevy_reflect",
1562 derive(Reflect),
1563 reflect(Debug, PartialEq, Clone)
1564)]
1565#[cfg_attr(
1566 all(feature = "serialize", feature = "bevy_reflect"),
1567 reflect(Serialize, Deserialize)
1568)]
1569pub struct Polyline2d {
1570 pub vertices: Vec<Vec2>,
1572}
1573
1574#[cfg(feature = "alloc")]
1575impl Primitive2d for Polyline2d {}
1576
1577#[cfg(feature = "alloc")]
1578impl FromIterator<Vec2> for Polyline2d {
1579 fn from_iter<I: IntoIterator<Item = Vec2>>(iter: I) -> Self {
1580 Self {
1581 vertices: iter.into_iter().collect(),
1582 }
1583 }
1584}
1585
1586#[cfg(feature = "alloc")]
1587impl Default for Polyline2d {
1588 fn default() -> Self {
1589 Self {
1590 vertices: Vec::from([Vec2::new(-0.5, 0.0), Vec2::new(0.5, 0.0)]),
1591 }
1592 }
1593}
1594
1595#[cfg(feature = "alloc")]
1596impl Polyline2d {
1597 pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Self {
1599 Self::from_iter(vertices)
1600 }
1601
1602 pub fn with_subdivisions(start: Vec2, end: Vec2, subdivisions: usize) -> Self {
1606 let total_vertices = subdivisions + 2;
1607 let mut vertices = Vec::with_capacity(total_vertices);
1608
1609 let step = (end - start) / (subdivisions + 1) as f32;
1610 for i in 0..total_vertices {
1611 vertices.push(start + step * i as f32);
1612 }
1613
1614 Self { vertices }
1615 }
1616}
1617
1618#[derive(Clone, Copy, Debug, PartialEq)]
1620#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1621#[cfg_attr(
1622 feature = "bevy_reflect",
1623 derive(Reflect),
1624 reflect(Debug, PartialEq, Default, Clone)
1625)]
1626#[cfg_attr(
1627 all(feature = "serialize", feature = "bevy_reflect"),
1628 reflect(Serialize, Deserialize)
1629)]
1630pub struct Triangle2d {
1631 pub vertices: [Vec2; 3],
1633}
1634
1635impl Primitive2d for Triangle2d {}
1636
1637impl Default for Triangle2d {
1638 fn default() -> Self {
1640 Self {
1641 vertices: [Vec2::Y * 0.5, Vec2::new(-0.5, -0.5), Vec2::new(0.5, -0.5)],
1642 }
1643 }
1644}
1645
1646impl Triangle2d {
1647 #[inline(always)]
1649 pub const fn new(a: Vec2, b: Vec2, c: Vec2) -> Self {
1650 Self {
1651 vertices: [a, b, c],
1652 }
1653 }
1654
1655 #[inline(always)]
1657 #[doc(alias = "orientation")]
1658 pub fn winding_order(&self) -> WindingOrder {
1659 let [a, b, c] = self.vertices;
1660 let area = (b - a).perp_dot(c - a);
1661 if area > f32::EPSILON {
1662 WindingOrder::CounterClockwise
1663 } else if area < -f32::EPSILON {
1664 WindingOrder::Clockwise
1665 } else {
1666 WindingOrder::Invalid
1667 }
1668 }
1669
1670 pub fn circumcircle(&self) -> (Circle, Vec2) {
1673 let a = self.vertices[0];
1687 let (b, c) = (self.vertices[1] - a, self.vertices[2] - a);
1688 let b_length_sq = b.length_squared();
1689 let c_length_sq = c.length_squared();
1690
1691 let inv_d = (2.0 * (b.x * c.y - b.y * c.x)).recip();
1693 let ux = inv_d * (c.y * b_length_sq - b.y * c_length_sq);
1694 let uy = inv_d * (b.x * c_length_sq - c.x * b_length_sq);
1695 let u = Vec2::new(ux, uy);
1696
1697 let center = u + a;
1700 let radius = u.length();
1701
1702 (Circle { radius }, center)
1703 }
1704
1705 #[inline(always)]
1710 pub fn is_degenerate(&self) -> bool {
1711 let [a, b, c] = self.vertices;
1712 let ab = (b - a).extend(0.);
1713 let ac = (c - a).extend(0.);
1714 ab.cross(ac).length() < 10e-7
1715 }
1716
1717 #[inline(always)]
1719 pub fn is_acute(&self) -> bool {
1720 let [a, b, c] = self.vertices;
1721 let ab = b - a;
1722 let bc = c - b;
1723 let ca = a - c;
1724
1725 let mut side_lengths = [
1727 ab.length_squared(),
1728 bc.length_squared(),
1729 ca.length_squared(),
1730 ];
1731 side_lengths.sort_by(|a, b| a.partial_cmp(b).unwrap());
1732 side_lengths[0] + side_lengths[1] > side_lengths[2]
1733 }
1734
1735 #[inline(always)]
1737 pub fn is_obtuse(&self) -> bool {
1738 let [a, b, c] = self.vertices;
1739 let ab = b - a;
1740 let bc = c - b;
1741 let ca = a - c;
1742
1743 let mut side_lengths = [
1745 ab.length_squared(),
1746 bc.length_squared(),
1747 ca.length_squared(),
1748 ];
1749 side_lengths.sort_by(|a, b| a.partial_cmp(b).unwrap());
1750 side_lengths[0] + side_lengths[1] < side_lengths[2]
1751 }
1752
1753 #[inline(always)]
1756 pub fn reverse(&mut self) {
1757 self.vertices.swap(0, 2);
1758 }
1759
1760 #[inline(always)]
1762 #[must_use]
1763 pub fn reversed(mut self) -> Self {
1764 self.reverse();
1765 self
1766 }
1767}
1768
1769impl Measured2d for Triangle2d {
1770 #[inline(always)]
1772 fn area(&self) -> f32 {
1773 let [a, b, c] = self.vertices;
1774 ops::abs(a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) / 2.0
1775 }
1776
1777 #[inline(always)]
1779 fn perimeter(&self) -> f32 {
1780 let [a, b, c] = self.vertices;
1781
1782 let ab = a.distance(b);
1783 let bc = b.distance(c);
1784 let ca = c.distance(a);
1785
1786 ab + bc + ca
1787 }
1788}
1789
1790#[derive(Clone, Copy, Debug, PartialEq)]
1792#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1793#[cfg_attr(
1794 feature = "bevy_reflect",
1795 derive(Reflect),
1796 reflect(Debug, PartialEq, Default, Clone)
1797)]
1798#[cfg_attr(
1799 all(feature = "serialize", feature = "bevy_reflect"),
1800 reflect(Serialize, Deserialize)
1801)]
1802#[doc(alias = "Quad")]
1803pub struct Rectangle {
1804 pub half_size: Vec2,
1806}
1807
1808impl Primitive2d for Rectangle {}
1809
1810impl Default for Rectangle {
1811 fn default() -> Self {
1813 Self {
1814 half_size: Vec2::splat(0.5),
1815 }
1816 }
1817}
1818
1819impl Rectangle {
1820 #[inline(always)]
1822 pub const fn new(width: f32, height: f32) -> Self {
1823 Self::from_size(Vec2::new(width, height))
1824 }
1825
1826 #[inline(always)]
1828 pub const fn from_size(size: Vec2) -> Self {
1829 Self {
1830 half_size: Vec2::new(size.x / 2.0, size.y / 2.0),
1831 }
1832 }
1833
1834 #[inline(always)]
1836 pub fn from_corners(point1: Vec2, point2: Vec2) -> Self {
1837 Self {
1838 half_size: (point2 - point1).abs() / 2.0,
1839 }
1840 }
1841
1842 #[inline(always)]
1845 pub const fn from_length(length: f32) -> Self {
1846 Self {
1847 half_size: Vec2::splat(length / 2.0),
1848 }
1849 }
1850
1851 #[inline(always)]
1853 pub fn size(&self) -> Vec2 {
1854 2.0 * self.half_size
1855 }
1856
1857 #[inline(always)]
1862 pub fn closest_point(&self, point: Vec2) -> Vec2 {
1863 point.clamp(-self.half_size, self.half_size)
1865 }
1866}
1867
1868impl Measured2d for Rectangle {
1869 #[inline(always)]
1871 fn area(&self) -> f32 {
1872 4.0 * self.half_size.x * self.half_size.y
1873 }
1874
1875 #[inline(always)]
1877 fn perimeter(&self) -> f32 {
1878 4.0 * (self.half_size.x + self.half_size.y)
1879 }
1880}
1881
1882#[cfg(feature = "alloc")]
1884#[derive(Clone, Debug, PartialEq)]
1885#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1886#[cfg_attr(
1887 feature = "bevy_reflect",
1888 derive(Reflect),
1889 reflect(Debug, PartialEq, Clone)
1890)]
1891#[cfg_attr(
1892 all(feature = "serialize", feature = "bevy_reflect"),
1893 reflect(Serialize, Deserialize)
1894)]
1895pub struct Polygon {
1896 pub vertices: Vec<Vec2>,
1898}
1899
1900#[cfg(feature = "alloc")]
1901impl Primitive2d for Polygon {}
1902
1903#[cfg(feature = "alloc")]
1904impl FromIterator<Vec2> for Polygon {
1905 fn from_iter<I: IntoIterator<Item = Vec2>>(iter: I) -> Self {
1906 Self {
1907 vertices: iter.into_iter().collect(),
1908 }
1909 }
1910}
1911
1912#[cfg(feature = "alloc")]
1913impl Polygon {
1914 pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Self {
1916 Self::from_iter(vertices)
1917 }
1918
1919 #[cfg(feature = "alloc")]
1924 pub fn is_simple(&self) -> bool {
1925 is_polygon_simple(&self.vertices)
1926 }
1927}
1928
1929#[cfg(feature = "alloc")]
1930impl From<ConvexPolygon> for Polygon {
1931 fn from(val: ConvexPolygon) -> Self {
1932 Polygon {
1933 vertices: val.vertices,
1934 }
1935 }
1936}
1937
1938#[cfg(feature = "alloc")]
1940#[derive(Clone, Debug, PartialEq)]
1941#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1942#[cfg_attr(
1943 feature = "bevy_reflect",
1944 derive(Reflect),
1945 reflect(Debug, PartialEq, Clone)
1946)]
1947#[cfg_attr(
1948 all(feature = "serialize", feature = "bevy_reflect"),
1949 reflect(Serialize, Deserialize)
1950)]
1951pub struct ConvexPolygon {
1952 vertices: Vec<Vec2>,
1954}
1955
1956#[cfg(feature = "alloc")]
1957impl Primitive2d for ConvexPolygon {}
1958
1959#[cfg(feature = "alloc")]
1961#[derive(Error, Debug, Clone)]
1962pub enum ConvexPolygonError {
1963 #[error("The created polygon is not convex")]
1965 Concave,
1966}
1967
1968#[cfg(feature = "alloc")]
1969impl ConvexPolygon {
1970 fn triangle_winding_order(
1971 &self,
1972 a_index: usize,
1973 b_index: usize,
1974 c_index: usize,
1975 ) -> WindingOrder {
1976 let a = self.vertices[a_index];
1977 let b = self.vertices[b_index];
1978 let c = self.vertices[c_index];
1979 Triangle2d::new(a, b, c).winding_order()
1980 }
1981
1982 pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Result<Self, ConvexPolygonError> {
1988 let polygon = Self::new_unchecked(vertices);
1989 let len = polygon.vertices.len();
1990 let ref_winding_order = polygon.triangle_winding_order(len - 1, 0, 1);
1991 for i in 1..len {
1992 let winding_order = polygon.triangle_winding_order(i - 1, i, (i + 1) % len);
1993 if winding_order != ref_winding_order {
1994 return Err(ConvexPolygonError::Concave);
1995 }
1996 }
1997 Ok(polygon)
1998 }
1999
2000 #[inline(always)]
2003 pub fn new_unchecked(vertices: impl IntoIterator<Item = Vec2>) -> Self {
2004 Self {
2005 vertices: vertices.into_iter().collect(),
2006 }
2007 }
2008
2009 #[inline(always)]
2011 pub fn vertices(&self) -> &[Vec2] {
2012 &self.vertices
2013 }
2014}
2015
2016#[cfg(feature = "alloc")]
2017impl TryFrom<Polygon> for ConvexPolygon {
2018 type Error = ConvexPolygonError;
2019
2020 fn try_from(val: Polygon) -> Result<Self, Self::Error> {
2021 ConvexPolygon::new(val.vertices)
2022 }
2023}
2024
2025#[derive(Clone, Copy, Debug, PartialEq)]
2027#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2028#[cfg_attr(
2029 feature = "bevy_reflect",
2030 derive(Reflect),
2031 reflect(Debug, PartialEq, Default, Clone)
2032)]
2033#[cfg_attr(
2034 all(feature = "serialize", feature = "bevy_reflect"),
2035 reflect(Serialize, Deserialize)
2036)]
2037pub struct RegularPolygon {
2038 pub circumcircle: Circle,
2040 pub sides: u32,
2042}
2043
2044impl Primitive2d for RegularPolygon {}
2045
2046impl Default for RegularPolygon {
2047 fn default() -> Self {
2049 Self {
2050 circumcircle: Circle { radius: 0.5 },
2051 sides: 6,
2052 }
2053 }
2054}
2055
2056impl RegularPolygon {
2057 #[inline(always)]
2064 pub const fn new(circumradius: f32, sides: u32) -> Self {
2065 assert!(
2066 circumradius.is_sign_positive(),
2067 "polygon has a negative radius"
2068 );
2069 assert!(sides > 2, "polygon has less than 3 sides");
2070
2071 Self {
2072 circumcircle: Circle {
2073 radius: circumradius,
2074 },
2075 sides,
2076 }
2077 }
2078
2079 #[inline(always)]
2082 pub const fn circumradius(&self) -> f32 {
2083 self.circumcircle.radius
2084 }
2085
2086 #[inline(always)]
2090 #[doc(alias = "apothem")]
2091 pub fn inradius(&self) -> f32 {
2092 self.circumradius() * ops::cos(PI / self.sides as f32)
2093 }
2094
2095 #[inline(always)]
2097 pub fn side_length(&self) -> f32 {
2098 2.0 * self.circumradius() * ops::sin(PI / self.sides as f32)
2099 }
2100
2101 #[inline(always)]
2106 pub const fn internal_angle_degrees(&self) -> f32 {
2107 (self.sides - 2) as f32 / self.sides as f32 * 180.0
2108 }
2109
2110 #[inline(always)]
2115 pub const fn internal_angle_radians(&self) -> f32 {
2116 (self.sides - 2) as f32 * PI / self.sides as f32
2117 }
2118
2119 #[inline(always)]
2124 pub const fn external_angle_degrees(&self) -> f32 {
2125 360.0 / self.sides as f32
2126 }
2127
2128 #[inline(always)]
2133 pub const fn external_angle_radians(&self) -> f32 {
2134 2.0 * PI / self.sides as f32
2135 }
2136
2137 pub fn vertices(self, rotation: f32) -> impl IntoIterator<Item = Vec2> {
2142 let start_angle = rotation + FRAC_PI_2;
2144 let step = core::f32::consts::TAU / self.sides as f32;
2145
2146 (0..self.sides).map(move |i| {
2147 let theta = start_angle + i as f32 * step;
2148 let (sin, cos) = ops::sin_cos(theta);
2149 Vec2::new(cos, sin) * self.circumcircle.radius
2150 })
2151 }
2152}
2153
2154impl Measured2d for RegularPolygon {
2155 #[inline(always)]
2157 fn area(&self) -> f32 {
2158 let angle: f32 = 2.0 * PI / (self.sides as f32);
2159 (self.sides as f32) * self.circumradius().squared() * ops::sin(angle) / 2.0
2160 }
2161
2162 #[inline(always)]
2165 fn perimeter(&self) -> f32 {
2166 self.sides as f32 * self.side_length()
2167 }
2168}
2169
2170#[derive(Clone, Copy, Debug, PartialEq)]
2174#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2175#[cfg_attr(
2176 feature = "bevy_reflect",
2177 derive(Reflect),
2178 reflect(Debug, PartialEq, Default, Clone)
2179)]
2180#[cfg_attr(
2181 all(feature = "serialize", feature = "bevy_reflect"),
2182 reflect(Serialize, Deserialize)
2183)]
2184#[doc(alias = "stadium", alias = "pill")]
2185pub struct Capsule2d {
2186 pub radius: f32,
2188 pub half_length: f32,
2190}
2191
2192impl Primitive2d for Capsule2d {}
2193
2194impl Default for Capsule2d {
2195 fn default() -> Self {
2198 Self {
2199 radius: 0.5,
2200 half_length: 0.5,
2201 }
2202 }
2203}
2204
2205impl Capsule2d {
2206 pub const fn new(radius: f32, length: f32) -> Self {
2208 Self {
2209 radius,
2210 half_length: length / 2.0,
2211 }
2212 }
2213
2214 #[inline]
2216 pub const fn to_inner_rectangle(&self) -> Rectangle {
2217 Rectangle::new(self.radius * 2.0, self.half_length * 2.0)
2218 }
2219}
2220
2221impl Measured2d for Capsule2d {
2222 #[inline]
2224 fn area(&self) -> f32 {
2225 PI * self.radius.squared() + self.to_inner_rectangle().area()
2227 }
2228
2229 #[inline]
2231 fn perimeter(&self) -> f32 {
2232 2.0 * PI * self.radius + 4.0 * self.half_length
2234 }
2235}
2236
2237#[cfg(test)]
2238mod tests {
2239 use super::*;
2242 use approx::{assert_abs_diff_eq, assert_relative_eq};
2243
2244 #[test]
2245 fn rectangle_closest_point() {
2246 let rectangle = Rectangle::new(2.0, 2.0);
2247 assert_eq!(rectangle.closest_point(Vec2::X * 10.0), Vec2::X);
2248 assert_eq!(rectangle.closest_point(Vec2::NEG_ONE * 10.0), Vec2::NEG_ONE);
2249 assert_eq!(
2250 rectangle.closest_point(Vec2::new(0.25, 0.1)),
2251 Vec2::new(0.25, 0.1)
2252 );
2253 }
2254
2255 #[test]
2256 fn circle_closest_point() {
2257 let circle = Circle { radius: 1.0 };
2258 assert_eq!(circle.closest_point(Vec2::X * 10.0), Vec2::X);
2259 assert_eq!(
2260 circle.closest_point(Vec2::NEG_ONE * 10.0),
2261 Vec2::NEG_ONE.normalize()
2262 );
2263 assert_eq!(
2264 circle.closest_point(Vec2::new(0.25, 0.1)),
2265 Vec2::new(0.25, 0.1)
2266 );
2267 }
2268
2269 #[test]
2270 fn annulus_closest_point() {
2271 let annulus = Annulus::new(1.5, 2.0);
2272 assert_eq!(annulus.closest_point(Vec2::X * 10.0), Vec2::X * 2.0);
2273 assert_eq!(
2274 annulus.closest_point(Vec2::NEG_ONE),
2275 Vec2::NEG_ONE.normalize() * 1.5
2276 );
2277 assert_eq!(
2278 annulus.closest_point(Vec2::new(1.55, 0.85)),
2279 Vec2::new(1.55, 0.85)
2280 );
2281 }
2282
2283 #[test]
2284 fn rhombus_closest_point() {
2285 let rhombus = Rhombus::new(2.0, 1.0);
2286 assert_eq!(rhombus.closest_point(Vec2::X * 10.0), Vec2::X);
2287 assert_eq!(
2288 rhombus.closest_point(Vec2::NEG_ONE * 0.2),
2289 Vec2::NEG_ONE * 0.2
2290 );
2291 assert_eq!(
2292 rhombus.closest_point(Vec2::new(-0.55, 0.35)),
2293 Vec2::new(-0.5, 0.25)
2294 );
2295
2296 let rhombus = Rhombus::new(0.0, 0.0);
2297 assert_eq!(rhombus.closest_point(Vec2::X * 10.0), Vec2::ZERO);
2298 assert_eq!(rhombus.closest_point(Vec2::NEG_ONE * 0.2), Vec2::ZERO);
2299 assert_eq!(rhombus.closest_point(Vec2::new(-0.55, 0.35)), Vec2::ZERO);
2300 }
2301
2302 #[test]
2303 fn segment_closest_point() {
2304 assert_eq!(
2305 Segment2d::new(Vec2::new(0.0, 0.0), Vec2::new(3.0, 0.0))
2306 .closest_point(Vec2::new(1.0, 6.0)),
2307 Vec2::new(1.0, 0.0)
2308 );
2309
2310 let segments = [
2311 Segment2d::new(Vec2::new(0.0, 0.0), Vec2::new(0.0, 0.0)),
2312 Segment2d::new(Vec2::new(0.0, 0.0), Vec2::new(1.0, 0.0)),
2313 Segment2d::new(Vec2::new(1.0, 0.0), Vec2::new(0.0, 1.0)),
2314 Segment2d::new(Vec2::new(1.0, 0.0), Vec2::new(1.0, 5.0 * f32::EPSILON)),
2315 ];
2316 let points = [
2317 Vec2::new(0.0, 0.0),
2318 Vec2::new(1.0, 0.0),
2319 Vec2::new(-1.0, 1.0),
2320 Vec2::new(1.0, 1.0),
2321 Vec2::new(-1.0, 0.0),
2322 Vec2::new(5.0, -1.0),
2323 Vec2::new(1.0, f32::EPSILON),
2324 ];
2325
2326 for point in points.iter() {
2327 for segment in segments.iter() {
2328 let closest = segment.closest_point(*point);
2329 assert!(
2330 point.distance_squared(closest) <= point.distance_squared(segment.point1()),
2331 "Closest point must always be at least as close as either vertex."
2332 );
2333 assert!(
2334 point.distance_squared(closest) <= point.distance_squared(segment.point2()),
2335 "Closest point must always be at least as close as either vertex."
2336 );
2337 assert!(
2338 point.distance_squared(closest) <= point.distance_squared(segment.center()),
2339 "Closest point must always be at least as close as the center."
2340 );
2341 let closest_to_closest = segment.closest_point(closest);
2342 assert_relative_eq!(closest_to_closest, closest);
2344 }
2345 }
2346 }
2347
2348 #[test]
2349 fn circle_math() {
2350 let circle = Circle { radius: 3.0 };
2351 assert_eq!(circle.diameter(), 6.0, "incorrect diameter");
2352 assert_eq!(circle.area(), 28.274334, "incorrect area");
2353 assert_eq!(circle.perimeter(), 18.849556, "incorrect perimeter");
2354 }
2355
2356 #[test]
2357 fn capsule_math() {
2358 let capsule = Capsule2d::new(2.0, 9.0);
2359 assert_eq!(
2360 capsule.to_inner_rectangle(),
2361 Rectangle::new(4.0, 9.0),
2362 "rectangle wasn't created correctly from a capsule"
2363 );
2364 assert_eq!(capsule.area(), 48.566371, "incorrect area");
2365 assert_eq!(capsule.perimeter(), 30.566371, "incorrect perimeter");
2366 }
2367
2368 #[test]
2369 fn annulus_math() {
2370 let annulus = Annulus::new(2.5, 3.5);
2371 assert_eq!(annulus.diameter(), 7.0, "incorrect diameter");
2372 assert_eq!(annulus.thickness(), 1.0, "incorrect thickness");
2373 assert_eq!(annulus.area(), 18.849556, "incorrect area");
2374 assert_eq!(annulus.perimeter(), 37.699112, "incorrect perimeter");
2375 }
2376
2377 #[test]
2378 fn rhombus_math() {
2379 let rhombus = Rhombus::new(3.0, 4.0);
2380 assert_eq!(rhombus.area(), 6.0, "incorrect area");
2381 assert_eq!(rhombus.perimeter(), 10.0, "incorrect perimeter");
2382 assert_eq!(rhombus.side(), 2.5, "incorrect side");
2383 assert_eq!(rhombus.inradius(), 1.2, "incorrect inradius");
2384 assert_eq!(rhombus.circumradius(), 2.0, "incorrect circumradius");
2385 let rhombus = Rhombus::new(0.0, 0.0);
2386 assert_eq!(rhombus.area(), 0.0, "incorrect area");
2387 assert_eq!(rhombus.perimeter(), 0.0, "incorrect perimeter");
2388 assert_eq!(rhombus.side(), 0.0, "incorrect side");
2389 assert_eq!(rhombus.inradius(), 0.0, "incorrect inradius");
2390 assert_eq!(rhombus.circumradius(), 0.0, "incorrect circumradius");
2391 let rhombus = Rhombus::from_side(core::f32::consts::SQRT_2);
2392 assert_abs_diff_eq!(rhombus.half_diagonals, Vec2::new(1.0, 1.0));
2393 assert_abs_diff_eq!(
2394 rhombus.half_diagonals,
2395 Rhombus::from_inradius(FRAC_1_SQRT_2).half_diagonals
2396 );
2397 }
2398
2399 #[test]
2400 fn ellipse_math() {
2401 let ellipse = Ellipse::new(3.0, 1.0);
2402 assert_eq!(ellipse.area(), 9.424778, "incorrect area");
2403
2404 assert_eq!(ellipse.eccentricity(), 0.94280905, "incorrect eccentricity");
2405
2406 let line = Ellipse::new(1., 0.);
2407 assert_eq!(line.eccentricity(), 1., "incorrect line eccentricity");
2408
2409 let circle = Ellipse::new(2., 2.);
2410 assert_eq!(circle.eccentricity(), 0., "incorrect circle eccentricity");
2411 }
2412
2413 #[test]
2414 fn ellipse_perimeter() {
2415 let circle = Ellipse::new(1., 1.);
2416 assert_relative_eq!(circle.perimeter(), 6.2831855);
2417
2418 let line = Ellipse::new(75_000., 0.5);
2419 assert_relative_eq!(line.perimeter(), 300_000.);
2420
2421 let ellipse = Ellipse::new(0.5, 2.);
2422 assert_relative_eq!(ellipse.perimeter(), 8.578423);
2423
2424 let ellipse = Ellipse::new(5., 3.);
2425 assert_relative_eq!(ellipse.perimeter(), 25.526999);
2426 }
2427
2428 #[test]
2429 fn triangle_math() {
2430 let triangle = Triangle2d::new(
2431 Vec2::new(-2.0, -1.0),
2432 Vec2::new(1.0, 4.0),
2433 Vec2::new(7.0, 0.0),
2434 );
2435 assert_eq!(triangle.area(), 21.0, "incorrect area");
2436 assert_eq!(triangle.perimeter(), 22.097439, "incorrect perimeter");
2437
2438 let degenerate_triangle =
2439 Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(0., 0.), Vec2::new(1., 0.));
2440 assert!(degenerate_triangle.is_degenerate());
2441
2442 let acute_triangle =
2443 Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(1., 0.), Vec2::new(0., 5.));
2444 let obtuse_triangle =
2445 Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(1., 0.), Vec2::new(0., 0.5));
2446
2447 assert!(acute_triangle.is_acute());
2448 assert!(!acute_triangle.is_obtuse());
2449 assert!(!obtuse_triangle.is_acute());
2450 assert!(obtuse_triangle.is_obtuse());
2451 }
2452
2453 #[test]
2454 fn triangle_winding_order() {
2455 let mut cw_triangle = Triangle2d::new(
2456 Vec2::new(0.0, 2.0),
2457 Vec2::new(-0.5, -1.2),
2458 Vec2::new(-1.0, -1.0),
2459 );
2460 assert_eq!(cw_triangle.winding_order(), WindingOrder::Clockwise);
2461
2462 let ccw_triangle = Triangle2d::new(
2463 Vec2::new(-1.0, -1.0),
2464 Vec2::new(-0.5, -1.2),
2465 Vec2::new(0.0, 2.0),
2466 );
2467 assert_eq!(ccw_triangle.winding_order(), WindingOrder::CounterClockwise);
2468
2469 cw_triangle.reverse();
2472 assert_eq!(cw_triangle, ccw_triangle);
2473
2474 let invalid_triangle = Triangle2d::new(
2475 Vec2::new(0.0, 2.0),
2476 Vec2::new(0.0, -1.0),
2477 Vec2::new(0.0, -1.2),
2478 );
2479 assert_eq!(invalid_triangle.winding_order(), WindingOrder::Invalid);
2480 }
2481
2482 #[test]
2483 fn rectangle_math() {
2484 let rectangle = Rectangle::new(3.0, 7.0);
2485 assert_eq!(
2486 rectangle,
2487 Rectangle::from_corners(Vec2::new(-1.5, -3.5), Vec2::new(1.5, 3.5))
2488 );
2489 assert_eq!(rectangle.area(), 21.0, "incorrect area");
2490 assert_eq!(rectangle.perimeter(), 20.0, "incorrect perimeter");
2491 }
2492
2493 #[test]
2494 fn regular_polygon_math() {
2495 let polygon = RegularPolygon::new(3.0, 6);
2496 assert_eq!(polygon.inradius(), 2.598076, "incorrect inradius");
2497 assert_eq!(polygon.side_length(), 3.0, "incorrect side length");
2498 assert_relative_eq!(polygon.area(), 23.38268, epsilon = 0.00001);
2499 assert_eq!(polygon.perimeter(), 18.0, "incorrect perimeter");
2500 assert_eq!(
2501 polygon.internal_angle_degrees(),
2502 120.0,
2503 "incorrect internal angle"
2504 );
2505 assert_eq!(
2506 polygon.internal_angle_radians(),
2507 120_f32.to_radians(),
2508 "incorrect internal angle"
2509 );
2510 assert_eq!(
2511 polygon.external_angle_degrees(),
2512 60.0,
2513 "incorrect external angle"
2514 );
2515 assert_eq!(
2516 polygon.external_angle_radians(),
2517 60_f32.to_radians(),
2518 "incorrect external angle"
2519 );
2520 }
2521
2522 #[test]
2523 fn triangle_circumcenter() {
2524 let triangle = Triangle2d::new(
2525 Vec2::new(10.0, 2.0),
2526 Vec2::new(-5.0, -3.0),
2527 Vec2::new(2.0, -1.0),
2528 );
2529 let (Circle { radius }, circumcenter) = triangle.circumcircle();
2530
2531 assert_eq!(radius, 98.34887);
2533 assert_eq!(circumcenter, Vec2::new(-28.5, 92.5));
2534 }
2535
2536 #[test]
2537 fn regular_polygon_vertices() {
2538 let polygon = RegularPolygon::new(1.0, 4);
2539
2540 let mut vertices = polygon.vertices(0.0).into_iter();
2542 assert!((vertices.next().unwrap() - Vec2::Y).length() < 1e-7);
2543
2544 let mut rotated_vertices = polygon.vertices(core::f32::consts::FRAC_PI_4).into_iter();
2546
2547 let side_distance = FRAC_1_SQRT_2;
2549 assert!(
2550 (rotated_vertices.next().unwrap() - Vec2::new(-side_distance, side_distance)).length()
2551 < 1e-7,
2552 );
2553 }
2554}