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