1use core::f32::consts::{FRAC_1_SQRT_2, FRAC_PI_2, FRAC_PI_3, PI};
2use derive_more::derive::From;
3use thiserror::Error;
4
5use super::{Measured2d, Primitive2d, WindingOrder};
6use crate::{
7 ops::{self, FloatPow},
8 Dir2, InvalidDirectionError, Isometry2d, Ray2d, Rot2, Vec2,
9};
10
11#[cfg(feature = "alloc")]
12use super::polygon::is_polygon_simple;
13
14#[cfg(feature = "bevy_reflect")]
15use bevy_reflect::{std_traits::ReflectDefault, Reflect};
16#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
17use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
18
19#[cfg(feature = "alloc")]
20use alloc::{boxed::Box, vec::Vec};
21
22#[derive(Clone, Copy, Debug, PartialEq)]
24#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
25#[cfg_attr(
26 feature = "bevy_reflect",
27 derive(Reflect),
28 reflect(Debug, PartialEq, Default, Clone)
29)]
30#[cfg_attr(
31 all(feature = "serialize", feature = "bevy_reflect"),
32 reflect(Serialize, Deserialize)
33)]
34pub struct Circle {
35 pub radius: f32,
37}
38impl Primitive2d for Circle {}
39
40impl Default for Circle {
41 fn default() -> Self {
43 Self { radius: 0.5 }
44 }
45}
46
47impl Circle {
48 #[inline(always)]
50 pub const fn new(radius: f32) -> Self {
51 Self { radius }
52 }
53
54 #[inline(always)]
56 pub fn diameter(&self) -> f32 {
57 2.0 * self.radius
58 }
59
60 #[inline(always)]
65 pub fn closest_point(&self, point: Vec2) -> Vec2 {
66 let distance_squared = point.length_squared();
67
68 if distance_squared <= self.radius.squared() {
69 point
71 } else {
72 let dir_to_point = point / ops::sqrt(distance_squared);
75 self.radius * dir_to_point
76 }
77 }
78}
79
80impl Measured2d for Circle {
81 #[inline(always)]
83 fn area(&self) -> f32 {
84 PI * self.radius.squared()
85 }
86
87 #[inline(always)]
89 #[doc(alias = "circumference")]
90 fn perimeter(&self) -> f32 {
91 2.0 * PI * self.radius
92 }
93}
94
95#[derive(Clone, Copy, Debug, PartialEq)]
110#[doc(alias("CircularArc", "CircleArc"))]
111#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
112#[cfg_attr(
113 feature = "bevy_reflect",
114 derive(Reflect),
115 reflect(Debug, PartialEq, Default, Clone)
116)]
117#[cfg_attr(
118 all(feature = "serialize", feature = "bevy_reflect"),
119 reflect(Serialize, Deserialize)
120)]
121pub struct Arc2d {
122 pub radius: f32,
124 pub half_angle: f32,
126}
127impl Primitive2d for Arc2d {}
128
129impl Default for Arc2d {
130 fn default() -> Self {
132 Self {
133 radius: 0.5,
134 half_angle: 2.0 * FRAC_PI_3,
135 }
136 }
137}
138
139impl Arc2d {
140 #[inline(always)]
142 pub fn new(radius: f32, half_angle: f32) -> Self {
143 Self { radius, half_angle }
144 }
145
146 #[inline(always)]
148 pub fn from_radians(radius: f32, angle: f32) -> Self {
149 Self {
150 radius,
151 half_angle: angle / 2.0,
152 }
153 }
154
155 #[inline(always)]
157 pub fn from_degrees(radius: f32, angle: f32) -> Self {
158 Self {
159 radius,
160 half_angle: angle.to_radians() / 2.0,
161 }
162 }
163
164 #[inline(always)]
168 pub fn from_turns(radius: f32, fraction: f32) -> Self {
169 Self {
170 radius,
171 half_angle: fraction * PI,
172 }
173 }
174
175 #[inline(always)]
177 pub fn angle(&self) -> f32 {
178 self.half_angle * 2.0
179 }
180
181 #[inline(always)]
183 pub fn length(&self) -> f32 {
184 self.angle() * self.radius
185 }
186
187 #[inline(always)]
189 pub fn right_endpoint(&self) -> Vec2 {
190 self.radius * Vec2::from_angle(FRAC_PI_2 - self.half_angle)
191 }
192
193 #[inline(always)]
195 pub fn left_endpoint(&self) -> Vec2 {
196 self.radius * Vec2::from_angle(FRAC_PI_2 + self.half_angle)
197 }
198
199 #[inline(always)]
201 pub fn endpoints(&self) -> [Vec2; 2] {
202 [self.left_endpoint(), self.right_endpoint()]
203 }
204
205 #[inline]
207 pub fn midpoint(&self) -> Vec2 {
208 self.radius * Vec2::Y
209 }
210
211 #[inline(always)]
213 pub fn half_chord_length(&self) -> f32 {
214 self.radius * ops::sin(self.half_angle)
215 }
216
217 #[inline(always)]
219 pub fn chord_length(&self) -> f32 {
220 2.0 * self.half_chord_length()
221 }
222
223 #[inline(always)]
225 pub fn chord_midpoint(&self) -> Vec2 {
226 self.apothem() * Vec2::Y
227 }
228
229 #[inline(always)]
235 pub fn apothem(&self) -> f32 {
239 let sign = if self.is_minor() { 1.0 } else { -1.0 };
240 sign * ops::sqrt(self.radius.squared() - self.half_chord_length().squared())
241 }
242
243 pub fn sagitta(&self) -> f32 {
249 self.radius - self.apothem()
250 }
251
252 #[inline(always)]
256 pub fn is_minor(&self) -> bool {
257 self.half_angle <= FRAC_PI_2
258 }
259
260 #[inline(always)]
264 pub fn is_major(&self) -> bool {
265 self.half_angle >= FRAC_PI_2
266 }
267}
268
269#[derive(Clone, Copy, Debug, PartialEq, From)]
278#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
279#[cfg_attr(
280 feature = "bevy_reflect",
281 derive(Reflect),
282 reflect(Debug, PartialEq, Default, Clone)
283)]
284#[cfg_attr(
285 all(feature = "serialize", feature = "bevy_reflect"),
286 reflect(Serialize, Deserialize)
287)]
288pub struct CircularSector {
289 #[cfg_attr(all(feature = "serialize", feature = "alloc"), serde(flatten))]
291 pub arc: Arc2d,
292}
293impl Primitive2d for CircularSector {}
294
295impl Default for CircularSector {
296 fn default() -> Self {
298 Self::from(Arc2d::default())
299 }
300}
301
302impl Measured2d for CircularSector {
303 #[inline(always)]
304 fn area(&self) -> f32 {
305 self.arc.radius.squared() * self.arc.half_angle
306 }
307
308 #[inline(always)]
309 fn perimeter(&self) -> f32 {
310 if self.half_angle() >= PI {
311 self.arc.radius * 2.0 * PI
312 } else {
313 2.0 * self.radius() + self.arc_length()
314 }
315 }
316}
317
318impl CircularSector {
319 #[inline(always)]
321 pub fn new(radius: f32, angle: f32) -> Self {
322 Self::from(Arc2d::new(radius, angle))
323 }
324
325 #[inline(always)]
327 pub fn from_radians(radius: f32, angle: f32) -> Self {
328 Self::from(Arc2d::from_radians(radius, angle))
329 }
330
331 #[inline(always)]
333 pub fn from_degrees(radius: f32, angle: f32) -> Self {
334 Self::from(Arc2d::from_degrees(radius, angle))
335 }
336
337 #[inline(always)]
341 pub fn from_turns(radius: f32, fraction: f32) -> Self {
342 Self::from(Arc2d::from_turns(radius, fraction))
343 }
344
345 #[inline(always)]
347 pub fn half_angle(&self) -> f32 {
348 self.arc.half_angle
349 }
350
351 #[inline(always)]
353 pub fn angle(&self) -> f32 {
354 self.arc.angle()
355 }
356
357 #[inline(always)]
359 pub fn radius(&self) -> f32 {
360 self.arc.radius
361 }
362
363 #[inline(always)]
365 pub fn arc_length(&self) -> f32 {
366 self.arc.length()
367 }
368
369 #[inline(always)]
373 pub fn half_chord_length(&self) -> f32 {
374 self.arc.half_chord_length()
375 }
376
377 #[inline(always)]
381 pub fn chord_length(&self) -> f32 {
382 self.arc.chord_length()
383 }
384
385 #[inline(always)]
389 pub fn chord_midpoint(&self) -> Vec2 {
390 self.arc.chord_midpoint()
391 }
392
393 #[inline(always)]
397 pub fn apothem(&self) -> f32 {
398 self.arc.apothem()
399 }
400
401 #[inline(always)]
405 pub fn sagitta(&self) -> f32 {
406 self.arc.sagitta()
407 }
408}
409
410#[derive(Clone, Copy, Debug, PartialEq, From)]
421#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
422#[cfg_attr(
423 feature = "bevy_reflect",
424 derive(Reflect),
425 reflect(Debug, PartialEq, Default, Clone)
426)]
427#[cfg_attr(
428 all(feature = "serialize", feature = "bevy_reflect"),
429 reflect(Serialize, Deserialize)
430)]
431pub struct CircularSegment {
432 #[cfg_attr(all(feature = "serialize", feature = "alloc"), serde(flatten))]
434 pub arc: Arc2d,
435}
436impl Primitive2d for CircularSegment {}
437
438impl Default for CircularSegment {
439 fn default() -> Self {
441 Self::from(Arc2d::default())
442 }
443}
444
445impl Measured2d for CircularSegment {
446 #[inline(always)]
447 fn area(&self) -> f32 {
448 0.5 * self.arc.radius.squared() * (self.arc.angle() - ops::sin(self.arc.angle()))
449 }
450
451 #[inline(always)]
452 fn perimeter(&self) -> f32 {
453 self.chord_length() + self.arc_length()
454 }
455}
456impl CircularSegment {
457 #[inline(always)]
459 pub fn new(radius: f32, angle: f32) -> Self {
460 Self::from(Arc2d::new(radius, angle))
461 }
462
463 #[inline(always)]
465 pub fn from_radians(radius: f32, angle: f32) -> Self {
466 Self::from(Arc2d::from_radians(radius, angle))
467 }
468
469 #[inline(always)]
471 pub fn from_degrees(radius: f32, angle: f32) -> Self {
472 Self::from(Arc2d::from_degrees(radius, angle))
473 }
474
475 #[inline(always)]
479 pub fn from_turns(radius: f32, fraction: f32) -> Self {
480 Self::from(Arc2d::from_turns(radius, fraction))
481 }
482
483 #[inline(always)]
485 pub fn half_angle(&self) -> f32 {
486 self.arc.half_angle
487 }
488
489 #[inline(always)]
491 pub fn angle(&self) -> f32 {
492 self.arc.angle()
493 }
494
495 #[inline(always)]
497 pub fn radius(&self) -> f32 {
498 self.arc.radius
499 }
500
501 #[inline(always)]
503 pub fn arc_length(&self) -> f32 {
504 self.arc.length()
505 }
506
507 #[inline(always)]
509 #[doc(alias = "half_base_length")]
510 pub fn half_chord_length(&self) -> f32 {
511 self.arc.half_chord_length()
512 }
513
514 #[inline(always)]
516 #[doc(alias = "base_length")]
517 #[doc(alias = "base")]
518 pub fn chord_length(&self) -> f32 {
519 self.arc.chord_length()
520 }
521
522 #[inline(always)]
524 #[doc(alias = "base_midpoint")]
525 pub fn chord_midpoint(&self) -> Vec2 {
526 self.arc.chord_midpoint()
527 }
528
529 #[inline(always)]
534 pub fn apothem(&self) -> f32 {
535 self.arc.apothem()
536 }
537
538 #[inline(always)]
542 #[doc(alias = "height")]
543 pub fn sagitta(&self) -> f32 {
544 self.arc.sagitta()
545 }
546}
547
548#[cfg(test)]
549mod arc_tests {
550 use core::f32::consts::FRAC_PI_4;
551 use core::f32::consts::SQRT_2;
552
553 use approx::assert_abs_diff_eq;
554
555 use super::*;
556
557 struct ArcTestCase {
558 radius: f32,
559 half_angle: f32,
560 angle: f32,
561 length: f32,
562 right_endpoint: Vec2,
563 left_endpoint: Vec2,
564 endpoints: [Vec2; 2],
565 midpoint: Vec2,
566 half_chord_length: f32,
567 chord_length: f32,
568 chord_midpoint: Vec2,
569 apothem: f32,
570 sagitta: f32,
571 is_minor: bool,
572 is_major: bool,
573 sector_area: f32,
574 sector_perimeter: f32,
575 segment_area: f32,
576 segment_perimeter: f32,
577 }
578
579 impl ArcTestCase {
580 fn check_arc(&self, arc: Arc2d) {
581 assert_abs_diff_eq!(self.radius, arc.radius);
582 assert_abs_diff_eq!(self.half_angle, arc.half_angle);
583 assert_abs_diff_eq!(self.angle, arc.angle());
584 assert_abs_diff_eq!(self.length, arc.length());
585 assert_abs_diff_eq!(self.right_endpoint, arc.right_endpoint());
586 assert_abs_diff_eq!(self.left_endpoint, arc.left_endpoint());
587 assert_abs_diff_eq!(self.endpoints[0], arc.endpoints()[0]);
588 assert_abs_diff_eq!(self.endpoints[1], arc.endpoints()[1]);
589 assert_abs_diff_eq!(self.midpoint, arc.midpoint());
590 assert_abs_diff_eq!(self.half_chord_length, arc.half_chord_length());
591 assert_abs_diff_eq!(self.chord_length, arc.chord_length(), epsilon = 0.00001);
592 assert_abs_diff_eq!(self.chord_midpoint, arc.chord_midpoint());
593 assert_abs_diff_eq!(self.apothem, arc.apothem());
594 assert_abs_diff_eq!(self.sagitta, arc.sagitta());
595 assert_eq!(self.is_minor, arc.is_minor());
596 assert_eq!(self.is_major, arc.is_major());
597 }
598
599 fn check_sector(&self, sector: CircularSector) {
600 assert_abs_diff_eq!(self.radius, sector.radius());
601 assert_abs_diff_eq!(self.half_angle, sector.half_angle());
602 assert_abs_diff_eq!(self.angle, sector.angle());
603 assert_abs_diff_eq!(self.half_chord_length, sector.half_chord_length());
604 assert_abs_diff_eq!(self.chord_length, sector.chord_length(), epsilon = 0.00001);
605 assert_abs_diff_eq!(self.chord_midpoint, sector.chord_midpoint());
606 assert_abs_diff_eq!(self.apothem, sector.apothem());
607 assert_abs_diff_eq!(self.sagitta, sector.sagitta());
608 assert_abs_diff_eq!(self.sector_area, sector.area());
609 assert_abs_diff_eq!(self.sector_perimeter, sector.perimeter());
610 }
611
612 fn check_segment(&self, segment: CircularSegment) {
613 assert_abs_diff_eq!(self.radius, segment.radius());
614 assert_abs_diff_eq!(self.half_angle, segment.half_angle());
615 assert_abs_diff_eq!(self.angle, segment.angle());
616 assert_abs_diff_eq!(self.half_chord_length, segment.half_chord_length());
617 assert_abs_diff_eq!(self.chord_length, segment.chord_length(), epsilon = 0.00001);
618 assert_abs_diff_eq!(self.chord_midpoint, segment.chord_midpoint());
619 assert_abs_diff_eq!(self.apothem, segment.apothem());
620 assert_abs_diff_eq!(self.sagitta, segment.sagitta());
621 assert_abs_diff_eq!(self.segment_area, segment.area());
622 assert_abs_diff_eq!(self.segment_perimeter, segment.perimeter());
623 }
624 }
625
626 #[test]
627 fn zero_angle() {
628 let tests = ArcTestCase {
629 radius: 1.0,
630 half_angle: 0.0,
631 angle: 0.0,
632 length: 0.0,
633 left_endpoint: Vec2::Y,
634 right_endpoint: Vec2::Y,
635 endpoints: [Vec2::Y, Vec2::Y],
636 midpoint: Vec2::Y,
637 half_chord_length: 0.0,
638 chord_length: 0.0,
639 chord_midpoint: Vec2::Y,
640 apothem: 1.0,
641 sagitta: 0.0,
642 is_minor: true,
643 is_major: false,
644 sector_area: 0.0,
645 sector_perimeter: 2.0,
646 segment_area: 0.0,
647 segment_perimeter: 0.0,
648 };
649
650 tests.check_arc(Arc2d::new(1.0, 0.0));
651 tests.check_sector(CircularSector::new(1.0, 0.0));
652 tests.check_segment(CircularSegment::new(1.0, 0.0));
653 }
654
655 #[test]
656 fn zero_radius() {
657 let tests = ArcTestCase {
658 radius: 0.0,
659 half_angle: FRAC_PI_4,
660 angle: FRAC_PI_2,
661 length: 0.0,
662 left_endpoint: Vec2::ZERO,
663 right_endpoint: Vec2::ZERO,
664 endpoints: [Vec2::ZERO, Vec2::ZERO],
665 midpoint: Vec2::ZERO,
666 half_chord_length: 0.0,
667 chord_length: 0.0,
668 chord_midpoint: Vec2::ZERO,
669 apothem: 0.0,
670 sagitta: 0.0,
671 is_minor: true,
672 is_major: false,
673 sector_area: 0.0,
674 sector_perimeter: 0.0,
675 segment_area: 0.0,
676 segment_perimeter: 0.0,
677 };
678
679 tests.check_arc(Arc2d::new(0.0, FRAC_PI_4));
680 tests.check_sector(CircularSector::new(0.0, FRAC_PI_4));
681 tests.check_segment(CircularSegment::new(0.0, FRAC_PI_4));
682 }
683
684 #[test]
685 fn quarter_circle() {
686 let sqrt_half: f32 = ops::sqrt(0.5);
687 let tests = ArcTestCase {
688 radius: 1.0,
689 half_angle: FRAC_PI_4,
690 angle: FRAC_PI_2,
691 length: FRAC_PI_2,
692 left_endpoint: Vec2::new(-sqrt_half, sqrt_half),
693 right_endpoint: Vec2::splat(sqrt_half),
694 endpoints: [Vec2::new(-sqrt_half, sqrt_half), Vec2::splat(sqrt_half)],
695 midpoint: Vec2::Y,
696 half_chord_length: sqrt_half,
697 chord_length: ops::sqrt(2.0),
698 chord_midpoint: Vec2::new(0.0, sqrt_half),
699 apothem: sqrt_half,
700 sagitta: 1.0 - sqrt_half,
701 is_minor: true,
702 is_major: false,
703 sector_area: FRAC_PI_4,
704 sector_perimeter: FRAC_PI_2 + 2.0,
705 segment_area: FRAC_PI_4 - 0.5,
706 segment_perimeter: FRAC_PI_2 + SQRT_2,
707 };
708
709 tests.check_arc(Arc2d::from_turns(1.0, 0.25));
710 tests.check_sector(CircularSector::from_turns(1.0, 0.25));
711 tests.check_segment(CircularSegment::from_turns(1.0, 0.25));
712 }
713
714 #[test]
715 fn half_circle() {
716 let tests = ArcTestCase {
717 radius: 1.0,
718 half_angle: FRAC_PI_2,
719 angle: PI,
720 length: PI,
721 left_endpoint: Vec2::NEG_X,
722 right_endpoint: Vec2::X,
723 endpoints: [Vec2::NEG_X, Vec2::X],
724 midpoint: Vec2::Y,
725 half_chord_length: 1.0,
726 chord_length: 2.0,
727 chord_midpoint: Vec2::ZERO,
728 apothem: 0.0,
729 sagitta: 1.0,
730 is_minor: true,
731 is_major: true,
732 sector_area: FRAC_PI_2,
733 sector_perimeter: PI + 2.0,
734 segment_area: FRAC_PI_2,
735 segment_perimeter: PI + 2.0,
736 };
737
738 tests.check_arc(Arc2d::from_radians(1.0, PI));
739 tests.check_sector(CircularSector::from_radians(1.0, PI));
740 tests.check_segment(CircularSegment::from_radians(1.0, PI));
741 }
742
743 #[test]
744 fn full_circle() {
745 let tests = ArcTestCase {
746 radius: 1.0,
747 half_angle: PI,
748 angle: 2.0 * PI,
749 length: 2.0 * PI,
750 left_endpoint: Vec2::NEG_Y,
751 right_endpoint: Vec2::NEG_Y,
752 endpoints: [Vec2::NEG_Y, Vec2::NEG_Y],
753 midpoint: Vec2::Y,
754 half_chord_length: 0.0,
755 chord_length: 0.0,
756 chord_midpoint: Vec2::NEG_Y,
757 apothem: -1.0,
758 sagitta: 2.0,
759 is_minor: false,
760 is_major: true,
761 sector_area: PI,
762 sector_perimeter: 2.0 * PI,
763 segment_area: PI,
764 segment_perimeter: 2.0 * PI,
765 };
766
767 tests.check_arc(Arc2d::from_degrees(1.0, 360.0));
768 tests.check_sector(CircularSector::from_degrees(1.0, 360.0));
769 tests.check_segment(CircularSegment::from_degrees(1.0, 360.0));
770 }
771}
772
773#[derive(Clone, Copy, Debug, PartialEq)]
775#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
776#[cfg_attr(
777 feature = "bevy_reflect",
778 derive(Reflect),
779 reflect(Debug, PartialEq, Default, Clone)
780)]
781#[cfg_attr(
782 all(feature = "serialize", feature = "bevy_reflect"),
783 reflect(Serialize, Deserialize)
784)]
785pub struct Ellipse {
786 pub half_size: Vec2,
790}
791impl Primitive2d for Ellipse {}
792
793impl Default for Ellipse {
794 fn default() -> Self {
796 Self {
797 half_size: Vec2::new(1.0, 0.5),
798 }
799 }
800}
801
802impl Ellipse {
803 #[inline(always)]
807 pub const fn new(half_width: f32, half_height: f32) -> Self {
808 Self {
809 half_size: Vec2::new(half_width, half_height),
810 }
811 }
812
813 #[inline(always)]
817 pub fn from_size(size: Vec2) -> Self {
818 Self {
819 half_size: size / 2.0,
820 }
821 }
822
823 #[inline(always)]
824 pub fn eccentricity(&self) -> f32 {
829 let a = self.semi_major();
830 let b = self.semi_minor();
831
832 ops::sqrt(a * a - b * b) / a
833 }
834
835 #[inline(always)]
836 pub fn focal_length(&self) -> f32 {
840 let a = self.semi_major();
841 let b = self.semi_minor();
842
843 ops::sqrt(a * a - b * b)
844 }
845
846 #[inline(always)]
848 pub fn semi_major(&self) -> f32 {
849 self.half_size.max_element()
850 }
851
852 #[inline(always)]
854 pub fn semi_minor(&self) -> f32 {
855 self.half_size.min_element()
856 }
857}
858
859impl Measured2d for Ellipse {
860 #[inline(always)]
862 fn area(&self) -> f32 {
863 PI * self.half_size.x * self.half_size.y
864 }
865
866 #[inline(always)]
867 fn perimeter(&self) -> f32 {
871 let a = self.semi_major();
872 let b = self.semi_minor();
873
874 if a / b - 1. < 1e-5 {
876 return PI * (a + b);
877 };
878
879 if a / b > 1e4 {
881 return 4. * a;
882 };
883
884 const BINOMIAL_COEFFICIENTS: [f32; 21] = [
888 1.,
889 0.25,
890 0.015625,
891 0.00390625,
892 0.0015258789,
893 0.00074768066,
894 0.00042057037,
895 0.00025963783,
896 0.00017140154,
897 0.000119028846,
898 0.00008599834,
899 0.00006414339,
900 0.000049109784,
901 0.000038430585,
902 0.000030636627,
903 0.000024815668,
904 0.000020380836,
905 0.000016942893,
906 0.000014236736,
907 0.000012077564,
908 0.000010333865,
909 ];
910
911 let h = ((a - b) / (a + b)).squared();
915
916 PI * (a + b)
917 * (0..=20)
918 .map(|i| BINOMIAL_COEFFICIENTS[i] * ops::powf(h, i as f32))
919 .sum::<f32>()
920 }
921}
922
923#[derive(Clone, Copy, Debug, PartialEq)]
925#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
926#[cfg_attr(
927 feature = "bevy_reflect",
928 derive(Reflect),
929 reflect(Debug, PartialEq, Default, Clone)
930)]
931#[cfg_attr(
932 all(feature = "serialize", feature = "bevy_reflect"),
933 reflect(Serialize, Deserialize)
934)]
935#[doc(alias = "Ring")]
936pub struct Annulus {
937 pub inner_circle: Circle,
939 pub outer_circle: Circle,
941}
942impl Primitive2d for Annulus {}
943
944impl Default for Annulus {
945 fn default() -> Self {
947 Self {
948 inner_circle: Circle::new(0.5),
949 outer_circle: Circle::new(1.0),
950 }
951 }
952}
953
954impl Annulus {
955 #[inline(always)]
957 pub const fn new(inner_radius: f32, outer_radius: f32) -> Self {
958 Self {
959 inner_circle: Circle::new(inner_radius),
960 outer_circle: Circle::new(outer_radius),
961 }
962 }
963
964 #[inline(always)]
966 pub fn diameter(&self) -> f32 {
967 self.outer_circle.diameter()
968 }
969
970 #[inline(always)]
972 pub fn thickness(&self) -> f32 {
973 self.outer_circle.radius - self.inner_circle.radius
974 }
975
976 #[inline(always)]
982 pub fn closest_point(&self, point: Vec2) -> Vec2 {
983 let distance_squared = point.length_squared();
984
985 if self.inner_circle.radius.squared() <= distance_squared {
986 if distance_squared <= self.outer_circle.radius.squared() {
987 point
989 } else {
990 let dir_to_point = point / ops::sqrt(distance_squared);
993 self.outer_circle.radius * dir_to_point
994 }
995 } else {
996 let dir_to_point = point / ops::sqrt(distance_squared);
999 self.inner_circle.radius * dir_to_point
1000 }
1001 }
1002}
1003
1004impl Measured2d for Annulus {
1005 #[inline(always)]
1007 fn area(&self) -> f32 {
1008 PI * (self.outer_circle.radius.squared() - self.inner_circle.radius.squared())
1009 }
1010
1011 #[inline(always)]
1014 #[doc(alias = "circumference")]
1015 fn perimeter(&self) -> f32 {
1016 2.0 * PI * (self.outer_circle.radius + self.inner_circle.radius)
1017 }
1018}
1019
1020#[derive(Clone, Copy, Debug, PartialEq)]
1024#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1025#[cfg_attr(
1026 feature = "bevy_reflect",
1027 derive(Reflect),
1028 reflect(Debug, PartialEq, Default, Clone)
1029)]
1030#[cfg_attr(
1031 all(feature = "serialize", feature = "bevy_reflect"),
1032 reflect(Serialize, Deserialize)
1033)]
1034#[doc(alias = "Diamond")]
1035pub struct Rhombus {
1036 pub half_diagonals: Vec2,
1038}
1039impl Primitive2d for Rhombus {}
1040
1041impl Default for Rhombus {
1042 fn default() -> Self {
1044 Self {
1045 half_diagonals: Vec2::splat(0.5),
1046 }
1047 }
1048}
1049
1050impl Rhombus {
1051 #[inline(always)]
1053 pub fn new(horizontal_diagonal: f32, vertical_diagonal: f32) -> Self {
1054 Self {
1055 half_diagonals: Vec2::new(horizontal_diagonal / 2.0, vertical_diagonal / 2.0),
1056 }
1057 }
1058
1059 #[inline(always)]
1061 pub fn from_side(side: f32) -> Self {
1062 Self {
1063 half_diagonals: Vec2::splat(side * FRAC_1_SQRT_2),
1064 }
1065 }
1066
1067 #[inline(always)]
1069 pub fn from_inradius(inradius: f32) -> Self {
1070 let half_diagonal = inradius * 2.0 / core::f32::consts::SQRT_2;
1071 Self {
1072 half_diagonals: Vec2::new(half_diagonal, half_diagonal),
1073 }
1074 }
1075
1076 #[inline(always)]
1078 pub fn side(&self) -> f32 {
1079 self.half_diagonals.length()
1080 }
1081
1082 #[inline(always)]
1085 pub fn circumradius(&self) -> f32 {
1086 self.half_diagonals.x.max(self.half_diagonals.y)
1087 }
1088
1089 #[inline(always)]
1092 #[doc(alias = "apothem")]
1093 pub fn inradius(&self) -> f32 {
1094 let side = self.side();
1095 if side == 0.0 {
1096 0.0
1097 } else {
1098 (self.half_diagonals.x * self.half_diagonals.y) / side
1099 }
1100 }
1101
1102 #[inline(always)]
1107 pub fn closest_point(&self, point: Vec2) -> Vec2 {
1108 let point_abs = point.abs();
1110 let half_diagonals = self.half_diagonals.abs(); let normal = Vec2::new(half_diagonals.y, half_diagonals.x);
1114 let normal_magnitude_squared = normal.length_squared();
1115 if normal_magnitude_squared == 0.0 {
1116 return Vec2::ZERO; }
1118
1119 let distance_unnormalised = normal.dot(point_abs) - half_diagonals.x * half_diagonals.y;
1121
1122 if distance_unnormalised <= 0.0 {
1124 return point;
1125 }
1126
1127 let mut result = point_abs - normal * distance_unnormalised / normal_magnitude_squared;
1129
1130 if result.x <= 0.0 {
1133 result = Vec2::new(0.0, half_diagonals.y);
1134 } else if result.y <= 0.0 {
1135 result = Vec2::new(half_diagonals.x, 0.0);
1136 }
1137
1138 result.copysign(point)
1140 }
1141}
1142
1143impl Measured2d for Rhombus {
1144 #[inline(always)]
1146 fn area(&self) -> f32 {
1147 2.0 * self.half_diagonals.x * self.half_diagonals.y
1148 }
1149
1150 #[inline(always)]
1152 fn perimeter(&self) -> f32 {
1153 4.0 * self.side()
1154 }
1155}
1156
1157#[derive(Clone, Copy, Debug, PartialEq)]
1160#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1161#[cfg_attr(
1162 feature = "bevy_reflect",
1163 derive(Reflect),
1164 reflect(Debug, PartialEq, Default, Clone)
1165)]
1166#[cfg_attr(
1167 all(feature = "serialize", feature = "bevy_reflect"),
1168 reflect(Serialize, Deserialize)
1169)]
1170pub struct Plane2d {
1171 pub normal: Dir2,
1173}
1174impl Primitive2d for Plane2d {}
1175
1176impl Default for Plane2d {
1177 fn default() -> Self {
1179 Self { normal: Dir2::Y }
1180 }
1181}
1182
1183impl Plane2d {
1184 #[inline(always)]
1190 pub fn new(normal: Vec2) -> Self {
1191 Self {
1192 normal: Dir2::new(normal).expect("normal must be nonzero and finite"),
1193 }
1194 }
1195}
1196
1197#[derive(Clone, Copy, Debug, PartialEq)]
1201#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1202#[cfg_attr(
1203 feature = "bevy_reflect",
1204 derive(Reflect),
1205 reflect(Debug, PartialEq, Clone)
1206)]
1207#[cfg_attr(
1208 all(feature = "serialize", feature = "bevy_reflect"),
1209 reflect(Serialize, Deserialize)
1210)]
1211pub struct Line2d {
1212 pub direction: Dir2,
1215}
1216impl Primitive2d for Line2d {}
1217
1218#[derive(Clone, Copy, Debug, PartialEq)]
1220#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1221#[cfg_attr(
1222 feature = "bevy_reflect",
1223 derive(Reflect),
1224 reflect(Debug, PartialEq, Clone)
1225)]
1226#[cfg_attr(
1227 all(feature = "serialize", feature = "bevy_reflect"),
1228 reflect(Serialize, Deserialize)
1229)]
1230#[doc(alias = "LineSegment2d")]
1231pub struct Segment2d {
1232 pub vertices: [Vec2; 2],
1234}
1235impl Primitive2d for Segment2d {}
1236
1237impl Segment2d {
1238 #[inline(always)]
1240 pub const fn new(point1: Vec2, point2: Vec2) -> Self {
1241 Self {
1242 vertices: [point1, point2],
1243 }
1244 }
1245
1246 #[inline(always)]
1248 #[deprecated(since = "0.16.0", note = "Use the `new` constructor instead")]
1249 pub fn from_points(point1: Vec2, point2: Vec2) -> (Self, Vec2) {
1250 (Self::new(point1, point2), (point1 + point2) / 2.)
1251 }
1252
1253 #[inline(always)]
1257 pub fn from_direction_and_length(direction: Dir2, length: f32) -> Self {
1258 let endpoint = 0.5 * length * direction;
1259 Self {
1260 vertices: [-endpoint, endpoint],
1261 }
1262 }
1263
1264 #[inline(always)]
1269 pub fn from_scaled_direction(scaled_direction: Vec2) -> Self {
1270 let endpoint = 0.5 * scaled_direction;
1271 Self {
1272 vertices: [-endpoint, endpoint],
1273 }
1274 }
1275
1276 #[inline(always)]
1281 pub fn from_ray_and_length(ray: Ray2d, length: f32) -> Self {
1282 Self {
1283 vertices: [ray.origin, ray.get_point(length)],
1284 }
1285 }
1286
1287 #[inline(always)]
1289 pub fn point1(&self) -> Vec2 {
1290 self.vertices[0]
1291 }
1292
1293 #[inline(always)]
1295 pub fn point2(&self) -> Vec2 {
1296 self.vertices[1]
1297 }
1298
1299 #[inline(always)]
1301 #[doc(alias = "midpoint")]
1302 pub fn center(&self) -> Vec2 {
1303 self.point1().midpoint(self.point2())
1304 }
1305
1306 #[inline(always)]
1308 pub fn length(&self) -> f32 {
1309 self.point1().distance(self.point2())
1310 }
1311
1312 #[inline(always)]
1314 pub fn length_squared(&self) -> f32 {
1315 self.point1().distance_squared(self.point2())
1316 }
1317
1318 #[inline(always)]
1326 pub fn direction(&self) -> Dir2 {
1327 self.try_direction().unwrap_or_else(|err| {
1328 panic!("Failed to compute the direction of a line segment: {err}")
1329 })
1330 }
1331
1332 #[inline(always)]
1337 pub fn try_direction(&self) -> Result<Dir2, InvalidDirectionError> {
1338 Dir2::new(self.scaled_direction())
1339 }
1340
1341 #[inline(always)]
1343 pub fn scaled_direction(&self) -> Vec2 {
1344 self.point2() - self.point1()
1345 }
1346
1347 #[inline(always)]
1355 pub fn left_normal(&self) -> Dir2 {
1356 self.try_left_normal().unwrap_or_else(|err| {
1357 panic!("Failed to compute the left-hand side normal of a line segment: {err}")
1358 })
1359 }
1360
1361 #[inline(always)]
1366 pub fn try_left_normal(&self) -> Result<Dir2, InvalidDirectionError> {
1367 Dir2::new(self.scaled_left_normal())
1368 }
1369
1370 #[inline(always)]
1374 pub fn scaled_left_normal(&self) -> Vec2 {
1375 let scaled_direction = self.scaled_direction();
1376 Vec2::new(-scaled_direction.y, scaled_direction.x)
1377 }
1378
1379 #[inline(always)]
1387 pub fn right_normal(&self) -> Dir2 {
1388 self.try_right_normal().unwrap_or_else(|err| {
1389 panic!("Failed to compute the right-hand side normal of a line segment: {err}")
1390 })
1391 }
1392
1393 #[inline(always)]
1398 pub fn try_right_normal(&self) -> Result<Dir2, InvalidDirectionError> {
1399 Dir2::new(self.scaled_right_normal())
1400 }
1401
1402 #[inline(always)]
1406 pub fn scaled_right_normal(&self) -> Vec2 {
1407 let scaled_direction = self.scaled_direction();
1408 Vec2::new(scaled_direction.y, -scaled_direction.x)
1409 }
1410
1411 #[inline(always)]
1413 pub fn transformed(&self, isometry: impl Into<Isometry2d>) -> Self {
1414 let isometry: Isometry2d = isometry.into();
1415 Self::new(
1416 isometry.transform_point(self.point1()),
1417 isometry.transform_point(self.point2()),
1418 )
1419 }
1420
1421 #[inline(always)]
1423 pub fn translated(&self, translation: Vec2) -> Segment2d {
1424 Self::new(self.point1() + translation, self.point2() + translation)
1425 }
1426
1427 #[inline(always)]
1429 pub fn rotated(&self, rotation: Rot2) -> Segment2d {
1430 Segment2d::new(rotation * self.point1(), rotation * self.point2())
1431 }
1432
1433 #[inline(always)]
1435 pub fn rotated_around(&self, rotation: Rot2, point: Vec2) -> Segment2d {
1436 let offset = self.translated(-point);
1438 let rotated = offset.rotated(rotation);
1439 rotated.translated(point)
1440 }
1441
1442 #[inline(always)]
1444 pub fn rotated_around_center(&self, rotation: Rot2) -> Segment2d {
1445 self.rotated_around(rotation, self.center())
1446 }
1447
1448 #[inline(always)]
1450 pub fn centered(&self) -> Segment2d {
1451 let center = self.center();
1452 self.translated(-center)
1453 }
1454
1455 #[inline(always)]
1457 pub fn resized(&self, length: f32) -> Segment2d {
1458 let offset_from_origin = self.center();
1459 let centered = self.translated(-offset_from_origin);
1460 let ratio = length / self.length();
1461 let segment = Segment2d::new(centered.point1() * ratio, centered.point2() * ratio);
1462 segment.translated(offset_from_origin)
1463 }
1464
1465 #[inline(always)]
1467 pub fn reverse(&mut self) {
1468 let [point1, point2] = &mut self.vertices;
1469 core::mem::swap(point1, point2);
1470 }
1471
1472 #[inline(always)]
1474 #[must_use]
1475 pub fn reversed(mut self) -> Self {
1476 self.reverse();
1477 self
1478 }
1479}
1480
1481impl From<[Vec2; 2]> for Segment2d {
1482 #[inline(always)]
1483 fn from(vertices: [Vec2; 2]) -> Self {
1484 Self { vertices }
1485 }
1486}
1487
1488impl From<(Vec2, Vec2)> for Segment2d {
1489 #[inline(always)]
1490 fn from((point1, point2): (Vec2, Vec2)) -> Self {
1491 Self::new(point1, point2)
1492 }
1493}
1494
1495#[derive(Clone, Debug, PartialEq)]
1499#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1500#[cfg_attr(
1501 feature = "bevy_reflect",
1502 derive(Reflect),
1503 reflect(Debug, PartialEq, Clone)
1504)]
1505#[cfg_attr(
1506 all(feature = "serialize", feature = "bevy_reflect"),
1507 reflect(Serialize, Deserialize)
1508)]
1509pub struct Polyline2d<const N: usize> {
1510 #[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))]
1512 pub vertices: [Vec2; N],
1513}
1514impl<const N: usize> Primitive2d for Polyline2d<N> {}
1515
1516impl<const N: usize> FromIterator<Vec2> for Polyline2d<N> {
1517 fn from_iter<I: IntoIterator<Item = Vec2>>(iter: I) -> Self {
1518 let mut vertices: [Vec2; N] = [Vec2::ZERO; N];
1519
1520 for (index, i) in iter.into_iter().take(N).enumerate() {
1521 vertices[index] = i;
1522 }
1523 Self { vertices }
1524 }
1525}
1526
1527impl<const N: usize> Polyline2d<N> {
1528 pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Self {
1530 Self::from_iter(vertices)
1531 }
1532}
1533
1534#[cfg(feature = "alloc")]
1539#[derive(Clone, Debug, PartialEq)]
1540#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1541pub struct BoxedPolyline2d {
1542 pub vertices: Box<[Vec2]>,
1544}
1545
1546#[cfg(feature = "alloc")]
1547impl Primitive2d for BoxedPolyline2d {}
1548
1549#[cfg(feature = "alloc")]
1550impl FromIterator<Vec2> for BoxedPolyline2d {
1551 fn from_iter<I: IntoIterator<Item = Vec2>>(iter: I) -> Self {
1552 let vertices: Vec<Vec2> = iter.into_iter().collect();
1553 Self {
1554 vertices: vertices.into_boxed_slice(),
1555 }
1556 }
1557}
1558
1559#[cfg(feature = "alloc")]
1560impl BoxedPolyline2d {
1561 pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Self {
1563 Self::from_iter(vertices)
1564 }
1565}
1566
1567#[derive(Clone, Copy, Debug, PartialEq)]
1569#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1570#[cfg_attr(
1571 feature = "bevy_reflect",
1572 derive(Reflect),
1573 reflect(Debug, PartialEq, Default, Clone)
1574)]
1575#[cfg_attr(
1576 all(feature = "serialize", feature = "bevy_reflect"),
1577 reflect(Serialize, Deserialize)
1578)]
1579pub struct Triangle2d {
1580 pub vertices: [Vec2; 3],
1582}
1583impl Primitive2d for Triangle2d {}
1584
1585impl Default for Triangle2d {
1586 fn default() -> Self {
1588 Self {
1589 vertices: [Vec2::Y * 0.5, Vec2::new(-0.5, -0.5), Vec2::new(0.5, -0.5)],
1590 }
1591 }
1592}
1593
1594impl Triangle2d {
1595 #[inline(always)]
1597 pub const fn new(a: Vec2, b: Vec2, c: Vec2) -> Self {
1598 Self {
1599 vertices: [a, b, c],
1600 }
1601 }
1602
1603 #[inline(always)]
1605 #[doc(alias = "orientation")]
1606 pub fn winding_order(&self) -> WindingOrder {
1607 let [a, b, c] = self.vertices;
1608 let area = (b - a).perp_dot(c - a);
1609 if area > f32::EPSILON {
1610 WindingOrder::CounterClockwise
1611 } else if area < -f32::EPSILON {
1612 WindingOrder::Clockwise
1613 } else {
1614 WindingOrder::Invalid
1615 }
1616 }
1617
1618 pub fn circumcircle(&self) -> (Circle, Vec2) {
1621 let a = self.vertices[0];
1635 let (b, c) = (self.vertices[1] - a, self.vertices[2] - a);
1636 let b_length_sq = b.length_squared();
1637 let c_length_sq = c.length_squared();
1638
1639 let inv_d = (2.0 * (b.x * c.y - b.y * c.x)).recip();
1641 let ux = inv_d * (c.y * b_length_sq - b.y * c_length_sq);
1642 let uy = inv_d * (b.x * c_length_sq - c.x * b_length_sq);
1643 let u = Vec2::new(ux, uy);
1644
1645 let center = u + a;
1648 let radius = u.length();
1649
1650 (Circle { radius }, center)
1651 }
1652
1653 #[inline(always)]
1658 pub fn is_degenerate(&self) -> bool {
1659 let [a, b, c] = self.vertices;
1660 let ab = (b - a).extend(0.);
1661 let ac = (c - a).extend(0.);
1662 ab.cross(ac).length() < 10e-7
1663 }
1664
1665 #[inline(always)]
1667 pub fn is_acute(&self) -> bool {
1668 let [a, b, c] = self.vertices;
1669 let ab = b - a;
1670 let bc = c - b;
1671 let ca = a - c;
1672
1673 let mut side_lengths = [
1675 ab.length_squared(),
1676 bc.length_squared(),
1677 ca.length_squared(),
1678 ];
1679 side_lengths.sort_by(|a, b| a.partial_cmp(b).unwrap());
1680 side_lengths[0] + side_lengths[1] > side_lengths[2]
1681 }
1682
1683 #[inline(always)]
1685 pub fn is_obtuse(&self) -> bool {
1686 let [a, b, c] = self.vertices;
1687 let ab = b - a;
1688 let bc = c - b;
1689 let ca = a - c;
1690
1691 let mut side_lengths = [
1693 ab.length_squared(),
1694 bc.length_squared(),
1695 ca.length_squared(),
1696 ];
1697 side_lengths.sort_by(|a, b| a.partial_cmp(b).unwrap());
1698 side_lengths[0] + side_lengths[1] < side_lengths[2]
1699 }
1700
1701 #[inline(always)]
1704 pub fn reverse(&mut self) {
1705 self.vertices.swap(0, 2);
1706 }
1707
1708 #[inline(always)]
1710 #[must_use]
1711 pub fn reversed(mut self) -> Self {
1712 self.reverse();
1713 self
1714 }
1715}
1716
1717impl Measured2d for Triangle2d {
1718 #[inline(always)]
1720 fn area(&self) -> f32 {
1721 let [a, b, c] = self.vertices;
1722 ops::abs(a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) / 2.0
1723 }
1724
1725 #[inline(always)]
1727 fn perimeter(&self) -> f32 {
1728 let [a, b, c] = self.vertices;
1729
1730 let ab = a.distance(b);
1731 let bc = b.distance(c);
1732 let ca = c.distance(a);
1733
1734 ab + bc + ca
1735 }
1736}
1737
1738#[derive(Clone, Copy, Debug, PartialEq)]
1740#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1741#[cfg_attr(
1742 feature = "bevy_reflect",
1743 derive(Reflect),
1744 reflect(Debug, PartialEq, Default, Clone)
1745)]
1746#[cfg_attr(
1747 all(feature = "serialize", feature = "bevy_reflect"),
1748 reflect(Serialize, Deserialize)
1749)]
1750#[doc(alias = "Quad")]
1751pub struct Rectangle {
1752 pub half_size: Vec2,
1754}
1755impl Primitive2d for Rectangle {}
1756
1757impl Default for Rectangle {
1758 fn default() -> Self {
1760 Self {
1761 half_size: Vec2::splat(0.5),
1762 }
1763 }
1764}
1765
1766impl Rectangle {
1767 #[inline(always)]
1769 pub fn new(width: f32, height: f32) -> Self {
1770 Self::from_size(Vec2::new(width, height))
1771 }
1772
1773 #[inline(always)]
1775 pub fn from_size(size: Vec2) -> Self {
1776 Self {
1777 half_size: size / 2.0,
1778 }
1779 }
1780
1781 #[inline(always)]
1783 pub fn from_corners(point1: Vec2, point2: Vec2) -> Self {
1784 Self {
1785 half_size: (point2 - point1).abs() / 2.0,
1786 }
1787 }
1788
1789 #[inline(always)]
1792 pub fn from_length(length: f32) -> Self {
1793 Self {
1794 half_size: Vec2::splat(length / 2.0),
1795 }
1796 }
1797
1798 #[inline(always)]
1800 pub fn size(&self) -> Vec2 {
1801 2.0 * self.half_size
1802 }
1803
1804 #[inline(always)]
1809 pub fn closest_point(&self, point: Vec2) -> Vec2 {
1810 point.clamp(-self.half_size, self.half_size)
1812 }
1813}
1814
1815impl Measured2d for Rectangle {
1816 #[inline(always)]
1818 fn area(&self) -> f32 {
1819 4.0 * self.half_size.x * self.half_size.y
1820 }
1821
1822 #[inline(always)]
1824 fn perimeter(&self) -> f32 {
1825 4.0 * (self.half_size.x + self.half_size.y)
1826 }
1827}
1828
1829#[derive(Clone, Debug, PartialEq)]
1833#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1834#[cfg_attr(
1835 feature = "bevy_reflect",
1836 derive(Reflect),
1837 reflect(Debug, PartialEq, Clone)
1838)]
1839#[cfg_attr(
1840 all(feature = "serialize", feature = "bevy_reflect"),
1841 reflect(Serialize, Deserialize)
1842)]
1843pub struct Polygon<const N: usize> {
1844 #[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))]
1846 pub vertices: [Vec2; N],
1847}
1848impl<const N: usize> Primitive2d for Polygon<N> {}
1849
1850impl<const N: usize> FromIterator<Vec2> for Polygon<N> {
1851 fn from_iter<I: IntoIterator<Item = Vec2>>(iter: I) -> Self {
1852 let mut vertices: [Vec2; N] = [Vec2::ZERO; N];
1853
1854 for (index, i) in iter.into_iter().take(N).enumerate() {
1855 vertices[index] = i;
1856 }
1857 Self { vertices }
1858 }
1859}
1860
1861impl<const N: usize> Polygon<N> {
1862 pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Self {
1864 Self::from_iter(vertices)
1865 }
1866
1867 #[cfg(feature = "alloc")]
1872 pub fn is_simple(&self) -> bool {
1873 is_polygon_simple(&self.vertices)
1874 }
1875}
1876
1877impl<const N: usize> From<ConvexPolygon<N>> for Polygon<N> {
1878 fn from(val: ConvexPolygon<N>) -> Self {
1879 Polygon {
1880 vertices: val.vertices,
1881 }
1882 }
1883}
1884
1885#[derive(Clone, Debug, PartialEq)]
1887#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1888#[cfg_attr(
1889 feature = "bevy_reflect",
1890 derive(Reflect),
1891 reflect(Debug, PartialEq, Clone)
1892)]
1893#[cfg_attr(
1894 all(feature = "serialize", feature = "bevy_reflect"),
1895 reflect(Serialize, Deserialize)
1896)]
1897pub struct ConvexPolygon<const N: usize> {
1898 #[cfg_attr(feature = "serialize", serde(with = "super::serde::array"))]
1900 vertices: [Vec2; N],
1901}
1902impl<const N: usize> Primitive2d for ConvexPolygon<N> {}
1903
1904#[derive(Error, Debug, Clone)]
1906pub enum ConvexPolygonError {
1907 #[error("The created polygon is not convex")]
1909 Concave,
1910}
1911
1912impl<const N: usize> ConvexPolygon<N> {
1913 fn triangle_winding_order(
1914 &self,
1915 a_index: usize,
1916 b_index: usize,
1917 c_index: usize,
1918 ) -> WindingOrder {
1919 let a = self.vertices[a_index];
1920 let b = self.vertices[b_index];
1921 let c = self.vertices[c_index];
1922 Triangle2d::new(a, b, c).winding_order()
1923 }
1924
1925 pub fn new(vertices: [Vec2; N]) -> Result<Self, ConvexPolygonError> {
1931 let polygon = Self::new_unchecked(vertices);
1932 let ref_winding_order = polygon.triangle_winding_order(N - 1, 0, 1);
1933 for i in 1..N {
1934 let winding_order = polygon.triangle_winding_order(i - 1, i, (i + 1) % N);
1935 if winding_order != ref_winding_order {
1936 return Err(ConvexPolygonError::Concave);
1937 }
1938 }
1939 Ok(polygon)
1940 }
1941
1942 #[inline(always)]
1945 pub fn new_unchecked(vertices: [Vec2; N]) -> Self {
1946 Self { vertices }
1947 }
1948
1949 #[inline(always)]
1951 pub fn vertices(&self) -> &[Vec2; N] {
1952 &self.vertices
1953 }
1954}
1955
1956impl<const N: usize> TryFrom<Polygon<N>> for ConvexPolygon<N> {
1957 type Error = ConvexPolygonError;
1958
1959 fn try_from(val: Polygon<N>) -> Result<Self, Self::Error> {
1960 ConvexPolygon::new(val.vertices)
1961 }
1962}
1963
1964#[cfg(feature = "alloc")]
1969#[derive(Clone, Debug, PartialEq)]
1970#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1971pub struct BoxedPolygon {
1972 pub vertices: Box<[Vec2]>,
1974}
1975
1976#[cfg(feature = "alloc")]
1977impl Primitive2d for BoxedPolygon {}
1978
1979#[cfg(feature = "alloc")]
1980impl FromIterator<Vec2> for BoxedPolygon {
1981 fn from_iter<I: IntoIterator<Item = Vec2>>(iter: I) -> Self {
1982 let vertices: Vec<Vec2> = iter.into_iter().collect();
1983 Self {
1984 vertices: vertices.into_boxed_slice(),
1985 }
1986 }
1987}
1988
1989#[cfg(feature = "alloc")]
1990impl BoxedPolygon {
1991 pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Self {
1993 Self::from_iter(vertices)
1994 }
1995
1996 pub fn is_simple(&self) -> bool {
2001 is_polygon_simple(&self.vertices)
2002 }
2003}
2004
2005#[derive(Clone, Copy, Debug, PartialEq)]
2007#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2008#[cfg_attr(
2009 feature = "bevy_reflect",
2010 derive(Reflect),
2011 reflect(Debug, PartialEq, Default, Clone)
2012)]
2013#[cfg_attr(
2014 all(feature = "serialize", feature = "bevy_reflect"),
2015 reflect(Serialize, Deserialize)
2016)]
2017pub struct RegularPolygon {
2018 pub circumcircle: Circle,
2020 pub sides: u32,
2022}
2023impl Primitive2d for RegularPolygon {}
2024
2025impl Default for RegularPolygon {
2026 fn default() -> Self {
2028 Self {
2029 circumcircle: Circle { radius: 0.5 },
2030 sides: 6,
2031 }
2032 }
2033}
2034
2035impl RegularPolygon {
2036 #[inline(always)]
2043 pub fn new(circumradius: f32, sides: u32) -> Self {
2044 assert!(
2045 circumradius.is_sign_positive(),
2046 "polygon has a negative radius"
2047 );
2048 assert!(sides > 2, "polygon has less than 3 sides");
2049
2050 Self {
2051 circumcircle: Circle {
2052 radius: circumradius,
2053 },
2054 sides,
2055 }
2056 }
2057
2058 #[inline(always)]
2061 pub fn circumradius(&self) -> f32 {
2062 self.circumcircle.radius
2063 }
2064
2065 #[inline(always)]
2069 #[doc(alias = "apothem")]
2070 pub fn inradius(&self) -> f32 {
2071 self.circumradius() * ops::cos(PI / self.sides as f32)
2072 }
2073
2074 #[inline(always)]
2076 pub fn side_length(&self) -> f32 {
2077 2.0 * self.circumradius() * ops::sin(PI / self.sides as f32)
2078 }
2079
2080 #[inline(always)]
2085 pub fn internal_angle_degrees(&self) -> f32 {
2086 (self.sides - 2) as f32 / self.sides as f32 * 180.0
2087 }
2088
2089 #[inline(always)]
2094 pub fn internal_angle_radians(&self) -> f32 {
2095 (self.sides - 2) as f32 * PI / self.sides as f32
2096 }
2097
2098 #[inline(always)]
2103 pub fn external_angle_degrees(&self) -> f32 {
2104 360.0 / self.sides as f32
2105 }
2106
2107 #[inline(always)]
2112 pub fn external_angle_radians(&self) -> f32 {
2113 2.0 * PI / self.sides as f32
2114 }
2115
2116 pub fn vertices(self, rotation: f32) -> impl IntoIterator<Item = Vec2> {
2121 let start_angle = rotation + FRAC_PI_2;
2123 let step = core::f32::consts::TAU / self.sides as f32;
2124
2125 (0..self.sides).map(move |i| {
2126 let theta = start_angle + i as f32 * step;
2127 let (sin, cos) = ops::sin_cos(theta);
2128 Vec2::new(cos, sin) * self.circumcircle.radius
2129 })
2130 }
2131}
2132
2133impl Measured2d for RegularPolygon {
2134 #[inline(always)]
2136 fn area(&self) -> f32 {
2137 let angle: f32 = 2.0 * PI / (self.sides as f32);
2138 (self.sides as f32) * self.circumradius().squared() * ops::sin(angle) / 2.0
2139 }
2140
2141 #[inline(always)]
2144 fn perimeter(&self) -> f32 {
2145 self.sides as f32 * self.side_length()
2146 }
2147}
2148
2149#[derive(Clone, Copy, Debug, PartialEq)]
2153#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2154#[cfg_attr(
2155 feature = "bevy_reflect",
2156 derive(Reflect),
2157 reflect(Debug, PartialEq, Default, Clone)
2158)]
2159#[cfg_attr(
2160 all(feature = "serialize", feature = "bevy_reflect"),
2161 reflect(Serialize, Deserialize)
2162)]
2163#[doc(alias = "stadium", alias = "pill")]
2164pub struct Capsule2d {
2165 pub radius: f32,
2167 pub half_length: f32,
2169}
2170impl Primitive2d for Capsule2d {}
2171
2172impl Default for Capsule2d {
2173 fn default() -> Self {
2176 Self {
2177 radius: 0.5,
2178 half_length: 0.5,
2179 }
2180 }
2181}
2182
2183impl Capsule2d {
2184 pub fn new(radius: f32, length: f32) -> Self {
2186 Self {
2187 radius,
2188 half_length: length / 2.0,
2189 }
2190 }
2191
2192 #[inline]
2194 pub fn to_inner_rectangle(&self) -> Rectangle {
2195 Rectangle::new(self.radius * 2.0, self.half_length * 2.0)
2196 }
2197}
2198
2199impl Measured2d for Capsule2d {
2200 #[inline]
2202 fn area(&self) -> f32 {
2203 PI * self.radius.squared() + self.to_inner_rectangle().area()
2205 }
2206
2207 #[inline]
2209 fn perimeter(&self) -> f32 {
2210 2.0 * PI * self.radius + 4.0 * self.half_length
2212 }
2213}
2214
2215#[cfg(test)]
2216mod tests {
2217 use super::*;
2220 use approx::{assert_abs_diff_eq, assert_relative_eq};
2221
2222 #[test]
2223 fn rectangle_closest_point() {
2224 let rectangle = Rectangle::new(2.0, 2.0);
2225 assert_eq!(rectangle.closest_point(Vec2::X * 10.0), Vec2::X);
2226 assert_eq!(rectangle.closest_point(Vec2::NEG_ONE * 10.0), Vec2::NEG_ONE);
2227 assert_eq!(
2228 rectangle.closest_point(Vec2::new(0.25, 0.1)),
2229 Vec2::new(0.25, 0.1)
2230 );
2231 }
2232
2233 #[test]
2234 fn circle_closest_point() {
2235 let circle = Circle { radius: 1.0 };
2236 assert_eq!(circle.closest_point(Vec2::X * 10.0), Vec2::X);
2237 assert_eq!(
2238 circle.closest_point(Vec2::NEG_ONE * 10.0),
2239 Vec2::NEG_ONE.normalize()
2240 );
2241 assert_eq!(
2242 circle.closest_point(Vec2::new(0.25, 0.1)),
2243 Vec2::new(0.25, 0.1)
2244 );
2245 }
2246
2247 #[test]
2248 fn annulus_closest_point() {
2249 let annulus = Annulus::new(1.5, 2.0);
2250 assert_eq!(annulus.closest_point(Vec2::X * 10.0), Vec2::X * 2.0);
2251 assert_eq!(
2252 annulus.closest_point(Vec2::NEG_ONE),
2253 Vec2::NEG_ONE.normalize() * 1.5
2254 );
2255 assert_eq!(
2256 annulus.closest_point(Vec2::new(1.55, 0.85)),
2257 Vec2::new(1.55, 0.85)
2258 );
2259 }
2260
2261 #[test]
2262 fn rhombus_closest_point() {
2263 let rhombus = Rhombus::new(2.0, 1.0);
2264 assert_eq!(rhombus.closest_point(Vec2::X * 10.0), Vec2::X);
2265 assert_eq!(
2266 rhombus.closest_point(Vec2::NEG_ONE * 0.2),
2267 Vec2::NEG_ONE * 0.2
2268 );
2269 assert_eq!(
2270 rhombus.closest_point(Vec2::new(-0.55, 0.35)),
2271 Vec2::new(-0.5, 0.25)
2272 );
2273
2274 let rhombus = Rhombus::new(0.0, 0.0);
2275 assert_eq!(rhombus.closest_point(Vec2::X * 10.0), Vec2::ZERO);
2276 assert_eq!(rhombus.closest_point(Vec2::NEG_ONE * 0.2), Vec2::ZERO);
2277 assert_eq!(rhombus.closest_point(Vec2::new(-0.55, 0.35)), Vec2::ZERO);
2278 }
2279
2280 #[test]
2281 fn circle_math() {
2282 let circle = Circle { radius: 3.0 };
2283 assert_eq!(circle.diameter(), 6.0, "incorrect diameter");
2284 assert_eq!(circle.area(), 28.274334, "incorrect area");
2285 assert_eq!(circle.perimeter(), 18.849556, "incorrect perimeter");
2286 }
2287
2288 #[test]
2289 fn capsule_math() {
2290 let capsule = Capsule2d::new(2.0, 9.0);
2291 assert_eq!(
2292 capsule.to_inner_rectangle(),
2293 Rectangle::new(4.0, 9.0),
2294 "rectangle wasn't created correctly from a capsule"
2295 );
2296 assert_eq!(capsule.area(), 48.566371, "incorrect area");
2297 assert_eq!(capsule.perimeter(), 30.566371, "incorrect perimeter");
2298 }
2299
2300 #[test]
2301 fn annulus_math() {
2302 let annulus = Annulus::new(2.5, 3.5);
2303 assert_eq!(annulus.diameter(), 7.0, "incorrect diameter");
2304 assert_eq!(annulus.thickness(), 1.0, "incorrect thickness");
2305 assert_eq!(annulus.area(), 18.849556, "incorrect area");
2306 assert_eq!(annulus.perimeter(), 37.699112, "incorrect perimeter");
2307 }
2308
2309 #[test]
2310 fn rhombus_math() {
2311 let rhombus = Rhombus::new(3.0, 4.0);
2312 assert_eq!(rhombus.area(), 6.0, "incorrect area");
2313 assert_eq!(rhombus.perimeter(), 10.0, "incorrect perimeter");
2314 assert_eq!(rhombus.side(), 2.5, "incorrect side");
2315 assert_eq!(rhombus.inradius(), 1.2, "incorrect inradius");
2316 assert_eq!(rhombus.circumradius(), 2.0, "incorrect circumradius");
2317 let rhombus = Rhombus::new(0.0, 0.0);
2318 assert_eq!(rhombus.area(), 0.0, "incorrect area");
2319 assert_eq!(rhombus.perimeter(), 0.0, "incorrect perimeter");
2320 assert_eq!(rhombus.side(), 0.0, "incorrect side");
2321 assert_eq!(rhombus.inradius(), 0.0, "incorrect inradius");
2322 assert_eq!(rhombus.circumradius(), 0.0, "incorrect circumradius");
2323 let rhombus = Rhombus::from_side(core::f32::consts::SQRT_2);
2324 assert_abs_diff_eq!(rhombus.half_diagonals, Vec2::new(1.0, 1.0));
2325 assert_abs_diff_eq!(
2326 rhombus.half_diagonals,
2327 Rhombus::from_inradius(FRAC_1_SQRT_2).half_diagonals
2328 );
2329 }
2330
2331 #[test]
2332 fn ellipse_math() {
2333 let ellipse = Ellipse::new(3.0, 1.0);
2334 assert_eq!(ellipse.area(), 9.424778, "incorrect area");
2335
2336 assert_eq!(ellipse.eccentricity(), 0.94280905, "incorrect eccentricity");
2337
2338 let line = Ellipse::new(1., 0.);
2339 assert_eq!(line.eccentricity(), 1., "incorrect line eccentricity");
2340
2341 let circle = Ellipse::new(2., 2.);
2342 assert_eq!(circle.eccentricity(), 0., "incorrect circle eccentricity");
2343 }
2344
2345 #[test]
2346 fn ellipse_perimeter() {
2347 let circle = Ellipse::new(1., 1.);
2348 assert_relative_eq!(circle.perimeter(), 6.2831855);
2349
2350 let line = Ellipse::new(75_000., 0.5);
2351 assert_relative_eq!(line.perimeter(), 300_000.);
2352
2353 let ellipse = Ellipse::new(0.5, 2.);
2354 assert_relative_eq!(ellipse.perimeter(), 8.578423);
2355
2356 let ellipse = Ellipse::new(5., 3.);
2357 assert_relative_eq!(ellipse.perimeter(), 25.526999);
2358 }
2359
2360 #[test]
2361 fn triangle_math() {
2362 let triangle = Triangle2d::new(
2363 Vec2::new(-2.0, -1.0),
2364 Vec2::new(1.0, 4.0),
2365 Vec2::new(7.0, 0.0),
2366 );
2367 assert_eq!(triangle.area(), 21.0, "incorrect area");
2368 assert_eq!(triangle.perimeter(), 22.097439, "incorrect perimeter");
2369
2370 let degenerate_triangle =
2371 Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(0., 0.), Vec2::new(1., 0.));
2372 assert!(degenerate_triangle.is_degenerate());
2373
2374 let acute_triangle =
2375 Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(1., 0.), Vec2::new(0., 5.));
2376 let obtuse_triangle =
2377 Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(1., 0.), Vec2::new(0., 0.5));
2378
2379 assert!(acute_triangle.is_acute());
2380 assert!(!acute_triangle.is_obtuse());
2381 assert!(!obtuse_triangle.is_acute());
2382 assert!(obtuse_triangle.is_obtuse());
2383 }
2384
2385 #[test]
2386 fn triangle_winding_order() {
2387 let mut cw_triangle = Triangle2d::new(
2388 Vec2::new(0.0, 2.0),
2389 Vec2::new(-0.5, -1.2),
2390 Vec2::new(-1.0, -1.0),
2391 );
2392 assert_eq!(cw_triangle.winding_order(), WindingOrder::Clockwise);
2393
2394 let ccw_triangle = Triangle2d::new(
2395 Vec2::new(-1.0, -1.0),
2396 Vec2::new(-0.5, -1.2),
2397 Vec2::new(0.0, 2.0),
2398 );
2399 assert_eq!(ccw_triangle.winding_order(), WindingOrder::CounterClockwise);
2400
2401 cw_triangle.reverse();
2404 assert_eq!(cw_triangle, ccw_triangle);
2405
2406 let invalid_triangle = Triangle2d::new(
2407 Vec2::new(0.0, 2.0),
2408 Vec2::new(0.0, -1.0),
2409 Vec2::new(0.0, -1.2),
2410 );
2411 assert_eq!(invalid_triangle.winding_order(), WindingOrder::Invalid);
2412 }
2413
2414 #[test]
2415 fn rectangle_math() {
2416 let rectangle = Rectangle::new(3.0, 7.0);
2417 assert_eq!(
2418 rectangle,
2419 Rectangle::from_corners(Vec2::new(-1.5, -3.5), Vec2::new(1.5, 3.5))
2420 );
2421 assert_eq!(rectangle.area(), 21.0, "incorrect area");
2422 assert_eq!(rectangle.perimeter(), 20.0, "incorrect perimeter");
2423 }
2424
2425 #[test]
2426 fn regular_polygon_math() {
2427 let polygon = RegularPolygon::new(3.0, 6);
2428 assert_eq!(polygon.inradius(), 2.598076, "incorrect inradius");
2429 assert_eq!(polygon.side_length(), 3.0, "incorrect side length");
2430 assert_relative_eq!(polygon.area(), 23.38268, epsilon = 0.00001);
2431 assert_eq!(polygon.perimeter(), 18.0, "incorrect perimeter");
2432 assert_eq!(
2433 polygon.internal_angle_degrees(),
2434 120.0,
2435 "incorrect internal angle"
2436 );
2437 assert_eq!(
2438 polygon.internal_angle_radians(),
2439 120_f32.to_radians(),
2440 "incorrect internal angle"
2441 );
2442 assert_eq!(
2443 polygon.external_angle_degrees(),
2444 60.0,
2445 "incorrect external angle"
2446 );
2447 assert_eq!(
2448 polygon.external_angle_radians(),
2449 60_f32.to_radians(),
2450 "incorrect external angle"
2451 );
2452 }
2453
2454 #[test]
2455 fn triangle_circumcenter() {
2456 let triangle = Triangle2d::new(
2457 Vec2::new(10.0, 2.0),
2458 Vec2::new(-5.0, -3.0),
2459 Vec2::new(2.0, -1.0),
2460 );
2461 let (Circle { radius }, circumcenter) = triangle.circumcircle();
2462
2463 assert_eq!(radius, 98.34887);
2465 assert_eq!(circumcenter, Vec2::new(-28.5, 92.5));
2466 }
2467
2468 #[test]
2469 fn regular_polygon_vertices() {
2470 let polygon = RegularPolygon::new(1.0, 4);
2471
2472 let mut vertices = polygon.vertices(0.0).into_iter();
2474 assert!((vertices.next().unwrap() - Vec2::Y).length() < 1e-7);
2475
2476 let mut rotated_vertices = polygon.vertices(core::f32::consts::FRAC_PI_4).into_iter();
2478
2479 let side_distance = FRAC_1_SQRT_2;
2481 assert!(
2482 (rotated_vertices.next().unwrap() - Vec2::new(-side_distance, side_distance)).length()
2483 < 1e-7,
2484 );
2485 }
2486}