bevy_math/primitives/
dim2.rs

1use core::f32::consts::{FRAC_1_SQRT_2, FRAC_PI_2, FRAC_PI_3, PI};
2use derive_more::derive::From;
3#[cfg(feature = "alloc")]
4use thiserror::Error;
5
6use super::{Measured2d, Primitive2d, WindingOrder};
7use crate::{
8    ops::{self, FloatPow},
9    Dir2, InvalidDirectionError, Isometry2d, Ray2d, Rot2, Vec2,
10};
11
12#[cfg(feature = "alloc")]
13use super::polygon::is_polygon_simple;
14
15#[cfg(feature = "bevy_reflect")]
16use bevy_reflect::{std_traits::ReflectDefault, Reflect};
17#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
18use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
19
20#[cfg(feature = "alloc")]
21use alloc::vec::Vec;
22
23/// A circle primitive, representing the set of points some distance from the origin
24#[derive(Clone, Copy, Debug, PartialEq)]
25#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
26#[cfg_attr(
27    feature = "bevy_reflect",
28    derive(Reflect),
29    reflect(Debug, PartialEq, Default, Clone)
30)]
31#[cfg_attr(
32    all(feature = "serialize", feature = "bevy_reflect"),
33    reflect(Serialize, Deserialize)
34)]
35pub struct Circle {
36    /// The radius of the circle
37    pub radius: f32,
38}
39
40impl Primitive2d for Circle {}
41
42impl Default for Circle {
43    /// Returns the default [`Circle`] with a radius of `0.5`.
44    fn default() -> Self {
45        Self { radius: 0.5 }
46    }
47}
48
49impl Circle {
50    /// Create a new [`Circle`] from a `radius`
51    #[inline(always)]
52    pub const fn new(radius: f32) -> Self {
53        Self { radius }
54    }
55
56    /// Get the diameter of the circle
57    #[inline(always)]
58    pub const fn diameter(&self) -> f32 {
59        2.0 * self.radius
60    }
61
62    /// Finds the point on the circle that is closest to the given `point`.
63    ///
64    /// If the point is outside the circle, the returned point will be on the perimeter of the circle.
65    /// Otherwise, it will be inside the circle and returned as is.
66    #[inline(always)]
67    pub fn closest_point(&self, point: Vec2) -> Vec2 {
68        let distance_squared = point.length_squared();
69
70        if distance_squared <= self.radius.squared() {
71            // The point is inside the circle.
72            point
73        } else {
74            // The point is outside the circle.
75            // Find the closest point on the perimeter of the circle.
76            let dir_to_point = point / ops::sqrt(distance_squared);
77            self.radius * dir_to_point
78        }
79    }
80}
81
82impl Measured2d for Circle {
83    /// Get the area of the circle
84    #[inline(always)]
85    fn area(&self) -> f32 {
86        PI * self.radius.squared()
87    }
88
89    /// Get the perimeter or circumference of the circle
90    #[inline(always)]
91    #[doc(alias = "circumference")]
92    fn perimeter(&self) -> f32 {
93        2.0 * PI * self.radius
94    }
95}
96
97/// A primitive representing an arc between two points on a circle.
98///
99/// An arc has no area.
100/// If you want to include the portion of a circle's area swept out by the arc,
101/// use the pie-shaped [`CircularSector`].
102/// If you want to include only the space inside the convex hull of the arc,
103/// use the bowl-shaped [`CircularSegment`].
104///
105/// The arc is drawn starting from [`Vec2::Y`], extending by `half_angle` radians on
106/// either side. The center of the circle is the origin [`Vec2::ZERO`]. Note that this
107/// means that the origin may not be within the `Arc2d`'s convex hull.
108///
109/// **Warning:** Arcs with negative angle or radius, or with angle greater than an entire circle, are not officially supported.
110/// It is recommended to normalize arcs to have an angle in [0, 2π].
111#[derive(Clone, Copy, Debug, PartialEq)]
112#[doc(alias("CircularArc", "CircleArc"))]
113#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
114#[cfg_attr(
115    feature = "bevy_reflect",
116    derive(Reflect),
117    reflect(Debug, PartialEq, Default, Clone)
118)]
119#[cfg_attr(
120    all(feature = "serialize", feature = "bevy_reflect"),
121    reflect(Serialize, Deserialize)
122)]
123pub struct Arc2d {
124    /// The radius of the circle
125    pub radius: f32,
126    /// Half the angle defining the arc
127    pub half_angle: f32,
128}
129
130impl Primitive2d for Arc2d {}
131
132impl Default for Arc2d {
133    /// Returns the default [`Arc2d`] with radius `0.5`, covering one third of a circle
134    fn default() -> Self {
135        Self {
136            radius: 0.5,
137            half_angle: 2.0 * FRAC_PI_3,
138        }
139    }
140}
141
142impl Arc2d {
143    /// Create a new [`Arc2d`] from a `radius` and a `half_angle`
144    #[inline(always)]
145    pub const fn new(radius: f32, half_angle: f32) -> Self {
146        Self { radius, half_angle }
147    }
148
149    /// Create a new [`Arc2d`] from a `radius` and an `angle` in radians
150    #[inline(always)]
151    pub const fn from_radians(radius: f32, angle: f32) -> Self {
152        Self {
153            radius,
154            half_angle: angle / 2.0,
155        }
156    }
157
158    /// Create a new [`Arc2d`] from a `radius` and an `angle` in degrees.
159    #[inline(always)]
160    pub const fn from_degrees(radius: f32, angle: f32) -> Self {
161        Self {
162            radius,
163            half_angle: angle.to_radians() / 2.0,
164        }
165    }
166
167    /// Create a new [`Arc2d`] from a `radius` and a `fraction` of a single turn.
168    ///
169    /// For instance, `0.5` turns is a semicircle.
170    #[inline(always)]
171    pub const fn from_turns(radius: f32, fraction: f32) -> Self {
172        Self {
173            radius,
174            half_angle: fraction * PI,
175        }
176    }
177
178    /// Get the angle of the arc
179    #[inline(always)]
180    pub const fn angle(&self) -> f32 {
181        self.half_angle * 2.0
182    }
183
184    /// Get the length of the arc
185    #[inline(always)]
186    pub const fn length(&self) -> f32 {
187        self.angle() * self.radius
188    }
189
190    /// Get the right-hand end point of the arc
191    #[inline(always)]
192    pub fn right_endpoint(&self) -> Vec2 {
193        self.radius * Vec2::from_angle(FRAC_PI_2 - self.half_angle)
194    }
195
196    /// Get the left-hand end point of the arc
197    #[inline(always)]
198    pub fn left_endpoint(&self) -> Vec2 {
199        self.radius * Vec2::from_angle(FRAC_PI_2 + self.half_angle)
200    }
201
202    /// Get the endpoints of the arc
203    #[inline(always)]
204    pub fn endpoints(&self) -> [Vec2; 2] {
205        [self.left_endpoint(), self.right_endpoint()]
206    }
207
208    /// Get the midpoint of the arc
209    #[inline]
210    pub fn midpoint(&self) -> Vec2 {
211        self.radius * Vec2::Y
212    }
213
214    /// Get half the distance between the endpoints (half the length of the chord)
215    #[inline(always)]
216    pub fn half_chord_length(&self) -> f32 {
217        self.radius * ops::sin(self.half_angle)
218    }
219
220    /// Get the distance between the endpoints (the length of the chord)
221    #[inline(always)]
222    pub fn chord_length(&self) -> f32 {
223        2.0 * self.half_chord_length()
224    }
225
226    /// Get the midpoint of the two endpoints (the midpoint of the chord)
227    #[inline(always)]
228    pub fn chord_midpoint(&self) -> Vec2 {
229        self.apothem() * Vec2::Y
230    }
231
232    /// Get the length of the apothem of this arc, that is,
233    /// the distance from the center of the circle to the midpoint of the chord, in the direction of the midpoint of the arc.
234    /// Equivalently, the [`radius`](Self::radius) minus the [`sagitta`](Self::sagitta).
235    ///
236    /// Note that for a [`major`](Self::is_major) arc, the apothem will be negative.
237    #[inline(always)]
238    // Naming note: Various sources are inconsistent as to whether the apothem is the segment between the center and the
239    // midpoint of a chord, or the length of that segment. Given this confusion, we've opted for the definition
240    // used by Wolfram MathWorld, which is the distance rather than the segment.
241    pub fn apothem(&self) -> f32 {
242        let sign = if self.is_minor() { 1.0 } else { -1.0 };
243        sign * ops::sqrt(self.radius.squared() - self.half_chord_length().squared())
244    }
245
246    /// Get the length of the sagitta of this arc, that is,
247    /// the length of the line between the midpoints of the arc and its chord.
248    /// Equivalently, the height of the triangle whose base is the chord and whose apex is the midpoint of the arc.
249    ///
250    /// The sagitta is also the sum of the [`radius`](Self::radius) and the [`apothem`](Self::apothem).
251    pub fn sagitta(&self) -> f32 {
252        self.radius - self.apothem()
253    }
254
255    /// Produces true if the arc is at most half a circle.
256    ///
257    /// **Note:** This is not the negation of [`is_major`](Self::is_major): an exact semicircle is both major and minor.
258    #[inline(always)]
259    pub const fn is_minor(&self) -> bool {
260        self.half_angle <= FRAC_PI_2
261    }
262
263    /// Produces true if the arc is at least half a circle.
264    ///
265    /// **Note:** This is not the negation of [`is_minor`](Self::is_minor): an exact semicircle is both major and minor.
266    #[inline(always)]
267    pub const fn is_major(&self) -> bool {
268        self.half_angle >= FRAC_PI_2
269    }
270}
271
272/// A primitive representing a circular sector: a pie slice of a circle.
273///
274/// The segment is positioned so that it always includes [`Vec2::Y`] and is vertically symmetrical.
275/// To orient the sector differently, apply a rotation.
276/// The sector is drawn with the center of its circle at the origin [`Vec2::ZERO`].
277///
278/// **Warning:** Circular sectors with negative angle or radius, or with angle greater than an entire circle, are not officially supported.
279/// We recommend normalizing circular sectors to have an angle in [0, 2π].
280#[derive(Clone, Copy, Debug, PartialEq, From)]
281#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
282#[cfg_attr(
283    feature = "bevy_reflect",
284    derive(Reflect),
285    reflect(Debug, PartialEq, Default, Clone)
286)]
287#[cfg_attr(
288    all(feature = "serialize", feature = "bevy_reflect"),
289    reflect(Serialize, Deserialize)
290)]
291pub struct CircularSector {
292    /// The arc defining the sector
293    #[cfg_attr(all(feature = "serialize", feature = "alloc"), serde(flatten))]
294    pub arc: Arc2d,
295}
296
297impl Primitive2d for CircularSector {}
298
299impl Default for CircularSector {
300    /// Returns the default [`CircularSector`] with radius `0.5` and covering a third of a circle
301    fn default() -> Self {
302        Self::from(Arc2d::default())
303    }
304}
305
306impl Measured2d for CircularSector {
307    #[inline(always)]
308    fn area(&self) -> f32 {
309        self.arc.radius.squared() * self.arc.half_angle
310    }
311
312    #[inline(always)]
313    fn perimeter(&self) -> f32 {
314        if self.half_angle() >= PI {
315            self.arc.radius * 2.0 * PI
316        } else {
317            2.0 * self.radius() + self.arc_length()
318        }
319    }
320}
321
322impl CircularSector {
323    /// Create a new [`CircularSector`] from a `radius` and an `angle`
324    #[inline(always)]
325    pub const fn new(radius: f32, angle: f32) -> Self {
326        Self {
327            arc: Arc2d::new(radius, angle),
328        }
329    }
330
331    /// Create a new [`CircularSector`] from a `radius` and an `angle` in radians.
332    #[inline(always)]
333    pub const fn from_radians(radius: f32, angle: f32) -> Self {
334        Self {
335            arc: Arc2d::from_radians(radius, angle),
336        }
337    }
338
339    /// Create a new [`CircularSector`] from a `radius` and an `angle` in degrees.
340    #[inline(always)]
341    pub const fn from_degrees(radius: f32, angle: f32) -> Self {
342        Self {
343            arc: Arc2d::from_degrees(radius, angle),
344        }
345    }
346
347    /// Create a new [`CircularSector`] from a `radius` and a number of `turns` of a circle.
348    ///
349    /// For instance, `0.5` turns is a semicircle.
350    #[inline(always)]
351    pub const fn from_turns(radius: f32, fraction: f32) -> Self {
352        Self {
353            arc: Arc2d::from_turns(radius, fraction),
354        }
355    }
356
357    /// Get half the angle of the sector
358    #[inline(always)]
359    pub const fn half_angle(&self) -> f32 {
360        self.arc.half_angle
361    }
362
363    /// Get the angle of the sector
364    #[inline(always)]
365    pub const fn angle(&self) -> f32 {
366        self.arc.angle()
367    }
368
369    /// Get the radius of the sector
370    #[inline(always)]
371    pub const fn radius(&self) -> f32 {
372        self.arc.radius
373    }
374
375    /// Get the length of the arc defining the sector
376    #[inline(always)]
377    pub const fn arc_length(&self) -> f32 {
378        self.arc.length()
379    }
380
381    /// Get half the length of the chord defined by the sector
382    ///
383    /// See [`Arc2d::half_chord_length`]
384    #[inline(always)]
385    pub fn half_chord_length(&self) -> f32 {
386        self.arc.half_chord_length()
387    }
388
389    /// Get the length of the chord defined by the sector
390    ///
391    /// See [`Arc2d::chord_length`]
392    #[inline(always)]
393    pub fn chord_length(&self) -> f32 {
394        self.arc.chord_length()
395    }
396
397    /// Get the midpoint of the chord defined by the sector
398    ///
399    /// See [`Arc2d::chord_midpoint`]
400    #[inline(always)]
401    pub fn chord_midpoint(&self) -> Vec2 {
402        self.arc.chord_midpoint()
403    }
404
405    /// Get the length of the apothem of this sector
406    ///
407    /// See [`Arc2d::apothem`]
408    #[inline(always)]
409    pub fn apothem(&self) -> f32 {
410        self.arc.apothem()
411    }
412
413    /// Get the length of the sagitta of this sector
414    ///
415    /// See [`Arc2d::sagitta`]
416    #[inline(always)]
417    pub fn sagitta(&self) -> f32 {
418        self.arc.sagitta()
419    }
420}
421
422/// A primitive representing a circular segment:
423/// the area enclosed by the arc of a circle and its chord (the line between its endpoints).
424///
425/// The segment is drawn starting from [`Vec2::Y`], extending equally on either side.
426/// To orient the segment differently, apply a rotation.
427/// The segment is drawn with the center of its circle at the origin [`Vec2::ZERO`].
428/// When positioning a segment, the [`apothem`](Self::apothem) function may be particularly useful.
429///
430/// **Warning:** Circular segments with negative angle or radius, or with angle greater than an entire circle, are not officially supported.
431/// We recommend normalizing circular segments to have an angle in [0, 2π].
432#[derive(Clone, Copy, Debug, PartialEq, From)]
433#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
434#[cfg_attr(
435    feature = "bevy_reflect",
436    derive(Reflect),
437    reflect(Debug, PartialEq, Default, Clone)
438)]
439#[cfg_attr(
440    all(feature = "serialize", feature = "bevy_reflect"),
441    reflect(Serialize, Deserialize)
442)]
443pub struct CircularSegment {
444    /// The arc defining the segment
445    #[cfg_attr(all(feature = "serialize", feature = "alloc"), serde(flatten))]
446    pub arc: Arc2d,
447}
448
449impl Primitive2d for CircularSegment {}
450
451impl Default for CircularSegment {
452    /// Returns the default [`CircularSegment`] with radius `0.5` and covering a third of a circle
453    fn default() -> Self {
454        Self::from(Arc2d::default())
455    }
456}
457
458impl Measured2d for CircularSegment {
459    #[inline(always)]
460    fn area(&self) -> f32 {
461        0.5 * self.arc.radius.squared() * (self.arc.angle() - ops::sin(self.arc.angle()))
462    }
463
464    #[inline(always)]
465    fn perimeter(&self) -> f32 {
466        self.chord_length() + self.arc_length()
467    }
468}
469
470impl CircularSegment {
471    /// Create a new [`CircularSegment`] from a `radius`, and an `angle`
472    #[inline(always)]
473    pub const fn new(radius: f32, angle: f32) -> Self {
474        Self {
475            arc: Arc2d::new(radius, angle),
476        }
477    }
478
479    /// Create a new [`CircularSegment`] from a `radius` and an `angle` in radians.
480    #[inline(always)]
481    pub const fn from_radians(radius: f32, angle: f32) -> Self {
482        Self {
483            arc: Arc2d::from_radians(radius, angle),
484        }
485    }
486
487    /// Create a new [`CircularSegment`] from a `radius` and an `angle` in degrees.
488    #[inline(always)]
489    pub const fn from_degrees(radius: f32, angle: f32) -> Self {
490        Self {
491            arc: Arc2d::from_degrees(radius, angle),
492        }
493    }
494
495    /// Create a new [`CircularSegment`] from a `radius` and a number of `turns` of a circle.
496    ///
497    /// For instance, `0.5` turns is a semicircle.
498    #[inline(always)]
499    pub const fn from_turns(radius: f32, fraction: f32) -> Self {
500        Self {
501            arc: Arc2d::from_turns(radius, fraction),
502        }
503    }
504
505    /// Get the half-angle of the segment
506    #[inline(always)]
507    pub const fn half_angle(&self) -> f32 {
508        self.arc.half_angle
509    }
510
511    /// Get the angle of the segment
512    #[inline(always)]
513    pub const fn angle(&self) -> f32 {
514        self.arc.angle()
515    }
516
517    /// Get the radius of the segment
518    #[inline(always)]
519    pub const fn radius(&self) -> f32 {
520        self.arc.radius
521    }
522
523    /// Get the length of the arc defining the segment
524    #[inline(always)]
525    pub const fn arc_length(&self) -> f32 {
526        self.arc.length()
527    }
528
529    /// Get half the length of the segment's base, also known as its chord
530    #[inline(always)]
531    #[doc(alias = "half_base_length")]
532    pub fn half_chord_length(&self) -> f32 {
533        self.arc.half_chord_length()
534    }
535
536    /// Get the length of the segment's base, also known as its chord
537    #[inline(always)]
538    #[doc(alias = "base_length")]
539    #[doc(alias = "base")]
540    pub fn chord_length(&self) -> f32 {
541        self.arc.chord_length()
542    }
543
544    /// Get the midpoint of the segment's base, also known as its chord
545    #[inline(always)]
546    #[doc(alias = "base_midpoint")]
547    pub fn chord_midpoint(&self) -> Vec2 {
548        self.arc.chord_midpoint()
549    }
550
551    /// Get the length of the apothem of this segment,
552    /// which is the signed distance between the segment and the center of its circle
553    ///
554    /// See [`Arc2d::apothem`]
555    #[inline(always)]
556    pub fn apothem(&self) -> f32 {
557        self.arc.apothem()
558    }
559
560    /// Get the length of the sagitta of this segment, also known as its height
561    ///
562    /// See [`Arc2d::sagitta`]
563    #[inline(always)]
564    #[doc(alias = "height")]
565    pub fn sagitta(&self) -> f32 {
566        self.arc.sagitta()
567    }
568}
569
570#[cfg(test)]
571mod arc_tests {
572    use core::f32::consts::FRAC_PI_4;
573    use core::f32::consts::SQRT_2;
574
575    use approx::assert_abs_diff_eq;
576
577    use super::*;
578
579    struct ArcTestCase {
580        radius: f32,
581        half_angle: f32,
582        angle: f32,
583        length: f32,
584        right_endpoint: Vec2,
585        left_endpoint: Vec2,
586        endpoints: [Vec2; 2],
587        midpoint: Vec2,
588        half_chord_length: f32,
589        chord_length: f32,
590        chord_midpoint: Vec2,
591        apothem: f32,
592        sagitta: f32,
593        is_minor: bool,
594        is_major: bool,
595        sector_area: f32,
596        sector_perimeter: f32,
597        segment_area: f32,
598        segment_perimeter: f32,
599    }
600
601    impl ArcTestCase {
602        fn check_arc(&self, arc: Arc2d) {
603            assert_abs_diff_eq!(self.radius, arc.radius);
604            assert_abs_diff_eq!(self.half_angle, arc.half_angle);
605            assert_abs_diff_eq!(self.angle, arc.angle());
606            assert_abs_diff_eq!(self.length, arc.length());
607            assert_abs_diff_eq!(self.right_endpoint, arc.right_endpoint());
608            assert_abs_diff_eq!(self.left_endpoint, arc.left_endpoint());
609            assert_abs_diff_eq!(self.endpoints[0], arc.endpoints()[0]);
610            assert_abs_diff_eq!(self.endpoints[1], arc.endpoints()[1]);
611            assert_abs_diff_eq!(self.midpoint, arc.midpoint());
612            assert_abs_diff_eq!(self.half_chord_length, arc.half_chord_length());
613            assert_abs_diff_eq!(self.chord_length, arc.chord_length(), epsilon = 0.00001);
614            assert_abs_diff_eq!(self.chord_midpoint, arc.chord_midpoint());
615            assert_abs_diff_eq!(self.apothem, arc.apothem());
616            assert_abs_diff_eq!(self.sagitta, arc.sagitta());
617            assert_eq!(self.is_minor, arc.is_minor());
618            assert_eq!(self.is_major, arc.is_major());
619        }
620
621        fn check_sector(&self, sector: CircularSector) {
622            assert_abs_diff_eq!(self.radius, sector.radius());
623            assert_abs_diff_eq!(self.half_angle, sector.half_angle());
624            assert_abs_diff_eq!(self.angle, sector.angle());
625            assert_abs_diff_eq!(self.half_chord_length, sector.half_chord_length());
626            assert_abs_diff_eq!(self.chord_length, sector.chord_length(), epsilon = 0.00001);
627            assert_abs_diff_eq!(self.chord_midpoint, sector.chord_midpoint());
628            assert_abs_diff_eq!(self.apothem, sector.apothem());
629            assert_abs_diff_eq!(self.sagitta, sector.sagitta());
630            assert_abs_diff_eq!(self.sector_area, sector.area());
631            assert_abs_diff_eq!(self.sector_perimeter, sector.perimeter());
632        }
633
634        fn check_segment(&self, segment: CircularSegment) {
635            assert_abs_diff_eq!(self.radius, segment.radius());
636            assert_abs_diff_eq!(self.half_angle, segment.half_angle());
637            assert_abs_diff_eq!(self.angle, segment.angle());
638            assert_abs_diff_eq!(self.half_chord_length, segment.half_chord_length());
639            assert_abs_diff_eq!(self.chord_length, segment.chord_length(), epsilon = 0.00001);
640            assert_abs_diff_eq!(self.chord_midpoint, segment.chord_midpoint());
641            assert_abs_diff_eq!(self.apothem, segment.apothem());
642            assert_abs_diff_eq!(self.sagitta, segment.sagitta());
643            assert_abs_diff_eq!(self.segment_area, segment.area());
644            assert_abs_diff_eq!(self.segment_perimeter, segment.perimeter());
645        }
646    }
647
648    #[test]
649    fn zero_angle() {
650        let tests = ArcTestCase {
651            radius: 1.0,
652            half_angle: 0.0,
653            angle: 0.0,
654            length: 0.0,
655            left_endpoint: Vec2::Y,
656            right_endpoint: Vec2::Y,
657            endpoints: [Vec2::Y, Vec2::Y],
658            midpoint: Vec2::Y,
659            half_chord_length: 0.0,
660            chord_length: 0.0,
661            chord_midpoint: Vec2::Y,
662            apothem: 1.0,
663            sagitta: 0.0,
664            is_minor: true,
665            is_major: false,
666            sector_area: 0.0,
667            sector_perimeter: 2.0,
668            segment_area: 0.0,
669            segment_perimeter: 0.0,
670        };
671
672        tests.check_arc(Arc2d::new(1.0, 0.0));
673        tests.check_sector(CircularSector::new(1.0, 0.0));
674        tests.check_segment(CircularSegment::new(1.0, 0.0));
675    }
676
677    #[test]
678    fn zero_radius() {
679        let tests = ArcTestCase {
680            radius: 0.0,
681            half_angle: FRAC_PI_4,
682            angle: FRAC_PI_2,
683            length: 0.0,
684            left_endpoint: Vec2::ZERO,
685            right_endpoint: Vec2::ZERO,
686            endpoints: [Vec2::ZERO, Vec2::ZERO],
687            midpoint: Vec2::ZERO,
688            half_chord_length: 0.0,
689            chord_length: 0.0,
690            chord_midpoint: Vec2::ZERO,
691            apothem: 0.0,
692            sagitta: 0.0,
693            is_minor: true,
694            is_major: false,
695            sector_area: 0.0,
696            sector_perimeter: 0.0,
697            segment_area: 0.0,
698            segment_perimeter: 0.0,
699        };
700
701        tests.check_arc(Arc2d::new(0.0, FRAC_PI_4));
702        tests.check_sector(CircularSector::new(0.0, FRAC_PI_4));
703        tests.check_segment(CircularSegment::new(0.0, FRAC_PI_4));
704    }
705
706    #[test]
707    fn quarter_circle() {
708        let sqrt_half: f32 = ops::sqrt(0.5);
709        let tests = ArcTestCase {
710            radius: 1.0,
711            half_angle: FRAC_PI_4,
712            angle: FRAC_PI_2,
713            length: FRAC_PI_2,
714            left_endpoint: Vec2::new(-sqrt_half, sqrt_half),
715            right_endpoint: Vec2::splat(sqrt_half),
716            endpoints: [Vec2::new(-sqrt_half, sqrt_half), Vec2::splat(sqrt_half)],
717            midpoint: Vec2::Y,
718            half_chord_length: sqrt_half,
719            chord_length: ops::sqrt(2.0),
720            chord_midpoint: Vec2::new(0.0, sqrt_half),
721            apothem: sqrt_half,
722            sagitta: 1.0 - sqrt_half,
723            is_minor: true,
724            is_major: false,
725            sector_area: FRAC_PI_4,
726            sector_perimeter: FRAC_PI_2 + 2.0,
727            segment_area: FRAC_PI_4 - 0.5,
728            segment_perimeter: FRAC_PI_2 + SQRT_2,
729        };
730
731        tests.check_arc(Arc2d::from_turns(1.0, 0.25));
732        tests.check_sector(CircularSector::from_turns(1.0, 0.25));
733        tests.check_segment(CircularSegment::from_turns(1.0, 0.25));
734    }
735
736    #[test]
737    fn half_circle() {
738        let tests = ArcTestCase {
739            radius: 1.0,
740            half_angle: FRAC_PI_2,
741            angle: PI,
742            length: PI,
743            left_endpoint: Vec2::NEG_X,
744            right_endpoint: Vec2::X,
745            endpoints: [Vec2::NEG_X, Vec2::X],
746            midpoint: Vec2::Y,
747            half_chord_length: 1.0,
748            chord_length: 2.0,
749            chord_midpoint: Vec2::ZERO,
750            apothem: 0.0,
751            sagitta: 1.0,
752            is_minor: true,
753            is_major: true,
754            sector_area: FRAC_PI_2,
755            sector_perimeter: PI + 2.0,
756            segment_area: FRAC_PI_2,
757            segment_perimeter: PI + 2.0,
758        };
759
760        tests.check_arc(Arc2d::from_radians(1.0, PI));
761        tests.check_sector(CircularSector::from_radians(1.0, PI));
762        tests.check_segment(CircularSegment::from_radians(1.0, PI));
763    }
764
765    #[test]
766    fn full_circle() {
767        let tests = ArcTestCase {
768            radius: 1.0,
769            half_angle: PI,
770            angle: 2.0 * PI,
771            length: 2.0 * PI,
772            left_endpoint: Vec2::NEG_Y,
773            right_endpoint: Vec2::NEG_Y,
774            endpoints: [Vec2::NEG_Y, Vec2::NEG_Y],
775            midpoint: Vec2::Y,
776            half_chord_length: 0.0,
777            chord_length: 0.0,
778            chord_midpoint: Vec2::NEG_Y,
779            apothem: -1.0,
780            sagitta: 2.0,
781            is_minor: false,
782            is_major: true,
783            sector_area: PI,
784            sector_perimeter: 2.0 * PI,
785            segment_area: PI,
786            segment_perimeter: 2.0 * PI,
787        };
788
789        tests.check_arc(Arc2d::from_degrees(1.0, 360.0));
790        tests.check_sector(CircularSector::from_degrees(1.0, 360.0));
791        tests.check_segment(CircularSegment::from_degrees(1.0, 360.0));
792    }
793}
794
795/// An ellipse primitive, which is like a circle, but the width and height can be different
796#[derive(Clone, Copy, Debug, PartialEq)]
797#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
798#[cfg_attr(
799    feature = "bevy_reflect",
800    derive(Reflect),
801    reflect(Debug, PartialEq, Default, Clone)
802)]
803#[cfg_attr(
804    all(feature = "serialize", feature = "bevy_reflect"),
805    reflect(Serialize, Deserialize)
806)]
807pub struct Ellipse {
808    /// Half of the width and height of the ellipse.
809    ///
810    /// This corresponds to the two perpendicular radii defining the ellipse.
811    pub half_size: Vec2,
812}
813
814impl Primitive2d for Ellipse {}
815
816impl Default for Ellipse {
817    /// Returns the default [`Ellipse`] with a half-width of `1.0` and a half-height of `0.5`.
818    fn default() -> Self {
819        Self {
820            half_size: Vec2::new(1.0, 0.5),
821        }
822    }
823}
824
825impl Ellipse {
826    /// Create a new `Ellipse` from half of its width and height.
827    ///
828    /// This corresponds to the two perpendicular radii defining the ellipse.
829    #[inline(always)]
830    pub const fn new(half_width: f32, half_height: f32) -> Self {
831        Self {
832            half_size: Vec2::new(half_width, half_height),
833        }
834    }
835
836    /// Create a new `Ellipse` from a given full size.
837    ///
838    /// `size.x` is the diameter along the X axis, and `size.y` is the diameter along the Y axis.
839    #[inline(always)]
840    pub const fn from_size(size: Vec2) -> Self {
841        Self {
842            half_size: Vec2::new(size.x / 2.0, size.y / 2.0),
843        }
844    }
845
846    #[inline(always)]
847    /// Returns the [eccentricity](https://en.wikipedia.org/wiki/Eccentricity_(mathematics)) of the ellipse.
848    /// It can be thought of as a measure of how "stretched" or elongated the ellipse is.
849    ///
850    /// The value should be in the range [0, 1), where 0 represents a circle, and 1 represents a parabola.
851    pub fn eccentricity(&self) -> f32 {
852        let a = self.semi_major();
853        let b = self.semi_minor();
854
855        ops::sqrt(a * a - b * b) / a
856    }
857
858    #[inline(always)]
859    /// Get the focal length of the ellipse. This corresponds to the distance between one of the foci and the center of the ellipse.
860    ///
861    /// The focal length of an ellipse is related to its eccentricity by `eccentricity = focal_length / semi_major`
862    pub fn focal_length(&self) -> f32 {
863        let a = self.semi_major();
864        let b = self.semi_minor();
865
866        ops::sqrt(a * a - b * b)
867    }
868
869    /// Returns the length of the semi-major axis. This corresponds to the longest radius of the ellipse.
870    #[inline(always)]
871    pub fn semi_major(&self) -> f32 {
872        self.half_size.max_element()
873    }
874
875    /// Returns the length of the semi-minor axis. This corresponds to the shortest radius of the ellipse.
876    #[inline(always)]
877    pub fn semi_minor(&self) -> f32 {
878        self.half_size.min_element()
879    }
880}
881
882impl Measured2d for Ellipse {
883    /// Get the area of the ellipse
884    #[inline(always)]
885    fn area(&self) -> f32 {
886        PI * self.half_size.x * self.half_size.y
887    }
888
889    #[inline(always)]
890    /// Get an approximation for the perimeter or circumference of the ellipse.
891    ///
892    /// The approximation is reasonably precise with a relative error less than 0.007%, getting more precise as the eccentricity of the ellipse decreases.
893    fn perimeter(&self) -> f32 {
894        let a = self.semi_major();
895        let b = self.semi_minor();
896
897        // In the case that `a == b`, the ellipse is a circle
898        if a / b - 1. < 1e-5 {
899            return PI * (a + b);
900        };
901
902        // In the case that `a` is much larger than `b`, the ellipse is a line
903        if a / b > 1e4 {
904            return 4. * a;
905        };
906
907        // These values are  the result of (0.5 choose n)^2 where n is the index in the array
908        // They could be calculated on the fly but hardcoding them yields more accurate and faster results
909        // because the actual calculation for these values involves factorials and numbers > 10^23
910        const BINOMIAL_COEFFICIENTS: [f32; 21] = [
911            1.,
912            0.25,
913            0.015625,
914            0.00390625,
915            0.0015258789,
916            0.00074768066,
917            0.00042057037,
918            0.00025963783,
919            0.00017140154,
920            0.000119028846,
921            0.00008599834,
922            0.00006414339,
923            0.000049109784,
924            0.000038430585,
925            0.000030636627,
926            0.000024815668,
927            0.000020380836,
928            0.000016942893,
929            0.000014236736,
930            0.000012077564,
931            0.000010333865,
932        ];
933
934        // The algorithm used here is the Gauss-Kummer infinite series expansion of the elliptic integral expression for the perimeter of ellipses
935        // For more information see https://www.wolframalpha.com/input/?i=gauss-kummer+series
936        // We only use the terms up to `i == 20` for this approximation
937        let h = ((a - b) / (a + b)).squared();
938
939        PI * (a + b)
940            * (0..=20)
941                .map(|i| BINOMIAL_COEFFICIENTS[i] * ops::powf(h, i as f32))
942                .sum::<f32>()
943    }
944}
945
946/// A primitive shape formed by the region between two circles, also known as a ring.
947#[derive(Clone, Copy, Debug, PartialEq)]
948#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
949#[cfg_attr(
950    feature = "bevy_reflect",
951    derive(Reflect),
952    reflect(Debug, PartialEq, Default, Clone)
953)]
954#[cfg_attr(
955    all(feature = "serialize", feature = "bevy_reflect"),
956    reflect(Serialize, Deserialize)
957)]
958#[doc(alias = "Ring")]
959pub struct Annulus {
960    /// The inner circle of the annulus
961    pub inner_circle: Circle,
962    /// The outer circle of the annulus
963    pub outer_circle: Circle,
964}
965
966impl Primitive2d for Annulus {}
967
968impl Default for Annulus {
969    /// Returns the default [`Annulus`] with radii of `0.5` and `1.0`.
970    fn default() -> Self {
971        Self {
972            inner_circle: Circle::new(0.5),
973            outer_circle: Circle::new(1.0),
974        }
975    }
976}
977
978impl Annulus {
979    /// Create a new [`Annulus`] from the radii of the inner and outer circle
980    #[inline(always)]
981    pub const fn new(inner_radius: f32, outer_radius: f32) -> Self {
982        Self {
983            inner_circle: Circle::new(inner_radius),
984            outer_circle: Circle::new(outer_radius),
985        }
986    }
987
988    /// Get the diameter of the annulus
989    #[inline(always)]
990    pub const fn diameter(&self) -> f32 {
991        self.outer_circle.diameter()
992    }
993
994    /// Get the thickness of the annulus
995    #[inline(always)]
996    pub const fn thickness(&self) -> f32 {
997        self.outer_circle.radius - self.inner_circle.radius
998    }
999
1000    /// Finds the point on the annulus that is closest to the given `point`:
1001    ///
1002    /// - If the point is outside of the annulus completely, the returned point will be on the outer perimeter.
1003    /// - If the point is inside of the inner circle (hole) of the annulus, the returned point will be on the inner perimeter.
1004    /// - Otherwise, the returned point is overlapping the annulus and returned as is.
1005    #[inline(always)]
1006    pub fn closest_point(&self, point: Vec2) -> Vec2 {
1007        let distance_squared = point.length_squared();
1008
1009        if self.inner_circle.radius.squared() <= distance_squared {
1010            if distance_squared <= self.outer_circle.radius.squared() {
1011                // The point is inside the annulus.
1012                point
1013            } else {
1014                // The point is outside the annulus and closer to the outer perimeter.
1015                // Find the closest point on the perimeter of the annulus.
1016                let dir_to_point = point / ops::sqrt(distance_squared);
1017                self.outer_circle.radius * dir_to_point
1018            }
1019        } else {
1020            // The point is outside the annulus and closer to the inner perimeter.
1021            // Find the closest point on the perimeter of the annulus.
1022            let dir_to_point = point / ops::sqrt(distance_squared);
1023            self.inner_circle.radius * dir_to_point
1024        }
1025    }
1026}
1027
1028impl Measured2d for Annulus {
1029    /// Get the area of the annulus
1030    #[inline(always)]
1031    fn area(&self) -> f32 {
1032        PI * (self.outer_circle.radius.squared() - self.inner_circle.radius.squared())
1033    }
1034
1035    /// Get the perimeter or circumference of the annulus,
1036    /// which is the sum of the perimeters of the inner and outer circles.
1037    #[inline(always)]
1038    #[doc(alias = "circumference")]
1039    fn perimeter(&self) -> f32 {
1040        2.0 * PI * (self.outer_circle.radius + self.inner_circle.radius)
1041    }
1042}
1043
1044/// A rhombus primitive, also known as a diamond shape.
1045/// A four sided polygon, centered on the origin, where opposite sides are parallel but without
1046/// requiring right angles.
1047#[derive(Clone, Copy, Debug, PartialEq)]
1048#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1049#[cfg_attr(
1050    feature = "bevy_reflect",
1051    derive(Reflect),
1052    reflect(Debug, PartialEq, Default, Clone)
1053)]
1054#[cfg_attr(
1055    all(feature = "serialize", feature = "bevy_reflect"),
1056    reflect(Serialize, Deserialize)
1057)]
1058#[doc(alias = "Diamond")]
1059pub struct Rhombus {
1060    /// Size of the horizontal and vertical diagonals of the rhombus
1061    pub half_diagonals: Vec2,
1062}
1063
1064impl Primitive2d for Rhombus {}
1065
1066impl Default for Rhombus {
1067    /// Returns the default [`Rhombus`] with a half-horizontal and half-vertical diagonal of `0.5`.
1068    fn default() -> Self {
1069        Self {
1070            half_diagonals: Vec2::splat(0.5),
1071        }
1072    }
1073}
1074
1075impl Rhombus {
1076    /// Create a new `Rhombus` from a vertical and horizontal diagonal sizes.
1077    #[inline(always)]
1078    pub const fn new(horizontal_diagonal: f32, vertical_diagonal: f32) -> Self {
1079        Self {
1080            half_diagonals: Vec2::new(horizontal_diagonal / 2.0, vertical_diagonal / 2.0),
1081        }
1082    }
1083
1084    /// Create a new `Rhombus` from a side length with all inner angles equal.
1085    #[inline(always)]
1086    pub const fn from_side(side: f32) -> Self {
1087        Self {
1088            half_diagonals: Vec2::splat(side * FRAC_1_SQRT_2),
1089        }
1090    }
1091
1092    /// Create a new `Rhombus` from a given inradius with all inner angles equal.
1093    #[inline(always)]
1094    pub const fn from_inradius(inradius: f32) -> Self {
1095        let half_diagonal = inradius * 2.0 / core::f32::consts::SQRT_2;
1096        Self {
1097            half_diagonals: Vec2::new(half_diagonal, half_diagonal),
1098        }
1099    }
1100
1101    /// Get the length of each side of the rhombus
1102    #[inline(always)]
1103    pub fn side(&self) -> f32 {
1104        self.half_diagonals.length()
1105    }
1106
1107    /// Get the radius of the circumcircle on which all vertices
1108    /// of the rhombus lie
1109    #[inline(always)]
1110    pub const fn circumradius(&self) -> f32 {
1111        self.half_diagonals.x.max(self.half_diagonals.y)
1112    }
1113
1114    /// Get the radius of the largest circle that can
1115    /// be drawn within the rhombus
1116    #[inline(always)]
1117    #[doc(alias = "apothem")]
1118    pub fn inradius(&self) -> f32 {
1119        let side = self.side();
1120        if side == 0.0 {
1121            0.0
1122        } else {
1123            (self.half_diagonals.x * self.half_diagonals.y) / side
1124        }
1125    }
1126
1127    /// Finds the point on the rhombus that is closest to the given `point`.
1128    ///
1129    /// If the point is outside the rhombus, the returned point will be on the perimeter of the rhombus.
1130    /// Otherwise, it will be inside the rhombus and returned as is.
1131    #[inline(always)]
1132    pub fn closest_point(&self, point: Vec2) -> Vec2 {
1133        // Fold the problem into the positive quadrant
1134        let point_abs = point.abs();
1135        let half_diagonals = self.half_diagonals.abs(); // to ensure correct sign
1136
1137        // The unnormalised normal vector perpendicular to the side of the rhombus
1138        let normal = Vec2::new(half_diagonals.y, half_diagonals.x);
1139        let normal_magnitude_squared = normal.length_squared();
1140        if normal_magnitude_squared == 0.0 {
1141            return Vec2::ZERO; // A null Rhombus has only one point anyway.
1142        }
1143
1144        // The last term corresponds to normal.dot(rhombus_vertex)
1145        let distance_unnormalised = normal.dot(point_abs) - half_diagonals.x * half_diagonals.y;
1146
1147        // The point is already inside so we simply return it.
1148        if distance_unnormalised <= 0.0 {
1149            return point;
1150        }
1151
1152        // Clamp the point to the edge
1153        let mut result = point_abs - normal * distance_unnormalised / normal_magnitude_squared;
1154
1155        // Clamp the point back to the positive quadrant
1156        // if it's outside, it needs to be clamped to either vertex
1157        if result.x <= 0.0 {
1158            result = Vec2::new(0.0, half_diagonals.y);
1159        } else if result.y <= 0.0 {
1160            result = Vec2::new(half_diagonals.x, 0.0);
1161        }
1162
1163        // Finally, we restore the signs of the original vector
1164        result.copysign(point)
1165    }
1166}
1167
1168impl Measured2d for Rhombus {
1169    /// Get the area of the rhombus
1170    #[inline(always)]
1171    fn area(&self) -> f32 {
1172        2.0 * self.half_diagonals.x * self.half_diagonals.y
1173    }
1174
1175    /// Get the perimeter of the rhombus
1176    #[inline(always)]
1177    fn perimeter(&self) -> f32 {
1178        4.0 * self.side()
1179    }
1180}
1181
1182/// An unbounded plane in 2D space. It forms a separating surface through the origin,
1183/// stretching infinitely far
1184#[derive(Clone, Copy, Debug, PartialEq)]
1185#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1186#[cfg_attr(
1187    feature = "bevy_reflect",
1188    derive(Reflect),
1189    reflect(Debug, PartialEq, Default, Clone)
1190)]
1191#[cfg_attr(
1192    all(feature = "serialize", feature = "bevy_reflect"),
1193    reflect(Serialize, Deserialize)
1194)]
1195pub struct Plane2d {
1196    /// The normal of the plane. The plane will be placed perpendicular to this direction
1197    pub normal: Dir2,
1198}
1199
1200impl Primitive2d for Plane2d {}
1201
1202impl Default for Plane2d {
1203    /// Returns the default [`Plane2d`] with a normal pointing in the `+Y` direction.
1204    fn default() -> Self {
1205        Self { normal: Dir2::Y }
1206    }
1207}
1208
1209impl Plane2d {
1210    /// Create a new `Plane2d` from a normal
1211    ///
1212    /// # Panics
1213    ///
1214    /// Panics if the given `normal` is zero (or very close to zero), or non-finite.
1215    #[inline(always)]
1216    pub fn new(normal: Vec2) -> Self {
1217        Self {
1218            normal: Dir2::new(normal).expect("normal must be nonzero and finite"),
1219        }
1220    }
1221}
1222
1223/// An infinite line going through the origin along a direction in 2D space.
1224///
1225/// For a finite line: [`Segment2d`]
1226#[derive(Clone, Copy, Debug, PartialEq)]
1227#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1228#[cfg_attr(
1229    feature = "bevy_reflect",
1230    derive(Reflect),
1231    reflect(Debug, PartialEq, Clone)
1232)]
1233#[cfg_attr(
1234    all(feature = "serialize", feature = "bevy_reflect"),
1235    reflect(Serialize, Deserialize)
1236)]
1237pub struct Line2d {
1238    /// The direction of the line. The line extends infinitely in both the given direction
1239    /// and its opposite direction
1240    pub direction: Dir2,
1241}
1242
1243impl Primitive2d for Line2d {}
1244
1245/// A line segment defined by two endpoints in 2D space.
1246#[derive(Clone, Copy, Debug, PartialEq)]
1247#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1248#[cfg_attr(
1249    feature = "bevy_reflect",
1250    derive(Reflect),
1251    reflect(Debug, PartialEq, Clone)
1252)]
1253#[cfg_attr(
1254    all(feature = "serialize", feature = "bevy_reflect"),
1255    reflect(Serialize, Deserialize)
1256)]
1257#[doc(alias = "LineSegment2d")]
1258pub struct Segment2d {
1259    /// The endpoints of the line segment.
1260    pub vertices: [Vec2; 2],
1261}
1262
1263impl Primitive2d for Segment2d {}
1264
1265impl Default for Segment2d {
1266    fn default() -> Self {
1267        Self {
1268            vertices: [Vec2::new(-0.5, 0.0), Vec2::new(0.5, 0.0)],
1269        }
1270    }
1271}
1272
1273impl Segment2d {
1274    /// Create a new `Segment2d` from its endpoints.
1275    #[inline(always)]
1276    pub const fn new(point1: Vec2, point2: Vec2) -> Self {
1277        Self {
1278            vertices: [point1, point2],
1279        }
1280    }
1281
1282    /// Create a new `Segment2d` centered at the origin with the given direction and length.
1283    ///
1284    /// The endpoints will be at `-direction * length / 2.0` and `direction * length / 2.0`.
1285    #[inline(always)]
1286    pub fn from_direction_and_length(direction: Dir2, length: f32) -> Self {
1287        let endpoint = 0.5 * length * direction;
1288        Self {
1289            vertices: [-endpoint, endpoint],
1290        }
1291    }
1292
1293    /// Create a new `Segment2d` centered at the origin from a vector representing
1294    /// the direction and length of the line segment.
1295    ///
1296    /// The endpoints will be at `-scaled_direction / 2.0` and `scaled_direction / 2.0`.
1297    #[inline(always)]
1298    pub fn from_scaled_direction(scaled_direction: Vec2) -> Self {
1299        let endpoint = 0.5 * scaled_direction;
1300        Self {
1301            vertices: [-endpoint, endpoint],
1302        }
1303    }
1304
1305    /// Create a new `Segment2d` starting from the origin of the given `ray`,
1306    /// going in the direction of the ray for the given `length`.
1307    ///
1308    /// The endpoints will be at `ray.origin` and `ray.origin + length * ray.direction`.
1309    #[inline(always)]
1310    pub fn from_ray_and_length(ray: Ray2d, length: f32) -> Self {
1311        Self {
1312            vertices: [ray.origin, ray.get_point(length)],
1313        }
1314    }
1315
1316    /// Get the position of the first endpoint of the line segment.
1317    #[inline(always)]
1318    pub const fn point1(&self) -> Vec2 {
1319        self.vertices[0]
1320    }
1321
1322    /// Get the position of the second endpoint of the line segment.
1323    #[inline(always)]
1324    pub const fn point2(&self) -> Vec2 {
1325        self.vertices[1]
1326    }
1327
1328    /// Compute the midpoint between the two endpoints of the line segment.
1329    #[inline(always)]
1330    #[doc(alias = "midpoint")]
1331    pub fn center(&self) -> Vec2 {
1332        self.point1().midpoint(self.point2())
1333    }
1334
1335    /// Compute the length of the line segment.
1336    #[inline(always)]
1337    pub fn length(&self) -> f32 {
1338        self.point1().distance(self.point2())
1339    }
1340
1341    /// Compute the squared length of the line segment.
1342    #[inline(always)]
1343    pub fn length_squared(&self) -> f32 {
1344        self.point1().distance_squared(self.point2())
1345    }
1346
1347    /// Compute the normalized direction pointing from the first endpoint to the second endpoint.
1348    ///
1349    /// For the non-panicking version, see [`Segment2d::try_direction`].
1350    ///
1351    /// # Panics
1352    ///
1353    /// Panics if a valid direction could not be computed, for example when the endpoints are coincident, NaN, or infinite.
1354    #[inline(always)]
1355    pub fn direction(&self) -> Dir2 {
1356        self.try_direction().unwrap_or_else(|err| {
1357            panic!("Failed to compute the direction of a line segment: {err}")
1358        })
1359    }
1360
1361    /// Try to compute the normalized direction pointing from the first endpoint to the second endpoint.
1362    ///
1363    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if a valid direction could not be computed,
1364    /// for example when the endpoints are coincident, NaN, or infinite.
1365    #[inline(always)]
1366    pub fn try_direction(&self) -> Result<Dir2, InvalidDirectionError> {
1367        Dir2::new(self.scaled_direction())
1368    }
1369
1370    /// Compute the vector from the first endpoint to the second endpoint.
1371    #[inline(always)]
1372    pub fn scaled_direction(&self) -> Vec2 {
1373        self.point2() - self.point1()
1374    }
1375
1376    /// Compute the normalized counterclockwise normal on the left-hand side of the line segment.
1377    ///
1378    /// For the non-panicking version, see [`Segment2d::try_left_normal`].
1379    ///
1380    /// # Panics
1381    ///
1382    /// Panics if a valid normal could not be computed, for example when the endpoints are coincident, NaN, or infinite.
1383    #[inline(always)]
1384    pub fn left_normal(&self) -> Dir2 {
1385        self.try_left_normal().unwrap_or_else(|err| {
1386            panic!("Failed to compute the left-hand side normal of a line segment: {err}")
1387        })
1388    }
1389
1390    /// Try to compute the normalized counterclockwise normal on the left-hand side of the line segment.
1391    ///
1392    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if a valid normal could not be computed,
1393    /// for example when the endpoints are coincident, NaN, or infinite.
1394    #[inline(always)]
1395    pub fn try_left_normal(&self) -> Result<Dir2, InvalidDirectionError> {
1396        Dir2::new(self.scaled_left_normal())
1397    }
1398
1399    /// Compute the non-normalized counterclockwise normal on the left-hand side of the line segment.
1400    ///
1401    /// The length of the normal is the distance between the endpoints.
1402    #[inline(always)]
1403    pub fn scaled_left_normal(&self) -> Vec2 {
1404        let scaled_direction = self.scaled_direction();
1405        Vec2::new(-scaled_direction.y, scaled_direction.x)
1406    }
1407
1408    /// Compute the normalized clockwise normal on the right-hand side of the line segment.
1409    ///
1410    /// For the non-panicking version, see [`Segment2d::try_right_normal`].
1411    ///
1412    /// # Panics
1413    ///
1414    /// Panics if a valid normal could not be computed, for example when the endpoints are coincident, NaN, or infinite.
1415    #[inline(always)]
1416    pub fn right_normal(&self) -> Dir2 {
1417        self.try_right_normal().unwrap_or_else(|err| {
1418            panic!("Failed to compute the right-hand side normal of a line segment: {err}")
1419        })
1420    }
1421
1422    /// Try to compute the normalized clockwise normal on the right-hand side of the line segment.
1423    ///
1424    /// Returns [`Err(InvalidDirectionError)`](InvalidDirectionError) if a valid normal could not be computed,
1425    /// for example when the endpoints are coincident, NaN, or infinite.
1426    #[inline(always)]
1427    pub fn try_right_normal(&self) -> Result<Dir2, InvalidDirectionError> {
1428        Dir2::new(self.scaled_right_normal())
1429    }
1430
1431    /// Compute the non-normalized clockwise normal on the right-hand side of the line segment.
1432    ///
1433    /// The length of the normal is the distance between the endpoints.
1434    #[inline(always)]
1435    pub fn scaled_right_normal(&self) -> Vec2 {
1436        let scaled_direction = self.scaled_direction();
1437        Vec2::new(scaled_direction.y, -scaled_direction.x)
1438    }
1439
1440    /// Compute the segment transformed by the given [`Isometry2d`].
1441    #[inline(always)]
1442    pub fn transformed(&self, isometry: impl Into<Isometry2d>) -> Self {
1443        let isometry: Isometry2d = isometry.into();
1444        Self::new(
1445            isometry.transform_point(self.point1()),
1446            isometry.transform_point(self.point2()),
1447        )
1448    }
1449
1450    /// Compute the segment translated by the given vector.
1451    #[inline(always)]
1452    pub fn translated(&self, translation: Vec2) -> Segment2d {
1453        Self::new(self.point1() + translation, self.point2() + translation)
1454    }
1455
1456    /// Compute the segment rotated around the origin by the given rotation.
1457    #[inline(always)]
1458    pub fn rotated(&self, rotation: Rot2) -> Segment2d {
1459        Segment2d::new(rotation * self.point1(), rotation * self.point2())
1460    }
1461
1462    /// Compute the segment rotated around the given point by the given rotation.
1463    #[inline(always)]
1464    pub fn rotated_around(&self, rotation: Rot2, point: Vec2) -> Segment2d {
1465        // We offset our segment so that our segment is rotated as if from the origin, then we can apply the offset back
1466        let offset = self.translated(-point);
1467        let rotated = offset.rotated(rotation);
1468        rotated.translated(point)
1469    }
1470
1471    /// Compute the segment rotated around its own center.
1472    #[inline(always)]
1473    pub fn rotated_around_center(&self, rotation: Rot2) -> Segment2d {
1474        self.rotated_around(rotation, self.center())
1475    }
1476
1477    /// Compute the segment with its center at the origin, keeping the same direction and length.
1478    #[inline(always)]
1479    pub fn centered(&self) -> Segment2d {
1480        let center = self.center();
1481        self.translated(-center)
1482    }
1483
1484    /// Compute the segment with a new length, keeping the same direction and center.
1485    #[inline(always)]
1486    pub fn resized(&self, length: f32) -> Segment2d {
1487        let offset_from_origin = self.center();
1488        let centered = self.translated(-offset_from_origin);
1489        let ratio = length / self.length();
1490        let segment = Segment2d::new(centered.point1() * ratio, centered.point2() * ratio);
1491        segment.translated(offset_from_origin)
1492    }
1493
1494    /// Reverses the direction of the line segment by swapping the endpoints.
1495    #[inline(always)]
1496    pub fn reverse(&mut self) {
1497        let [point1, point2] = &mut self.vertices;
1498        core::mem::swap(point1, point2);
1499    }
1500
1501    /// Returns the line segment with its direction reversed by swapping the endpoints.
1502    #[inline(always)]
1503    #[must_use]
1504    pub fn reversed(mut self) -> Self {
1505        self.reverse();
1506        self
1507    }
1508
1509    /// Returns the point on the [`Segment2d`] that is closest to the specified `point`.
1510    #[inline(always)]
1511    pub fn closest_point(&self, point: Vec2) -> Vec2 {
1512        //       `point`
1513        //           x
1514        //          ^|
1515        //         / |
1516        //`offset`/  |
1517        //       /   |  `segment_vector`
1518        //      x----.-------------->x
1519        //      0    t               1
1520        let segment_vector = self.vertices[1] - self.vertices[0];
1521        let offset = point - self.vertices[0];
1522        // The signed projection of `offset` onto `segment_vector`, scaled by the length of the segment.
1523        let projection_scaled = segment_vector.dot(offset);
1524
1525        // `point` is too far "left" in the picture
1526        if projection_scaled <= 0.0 {
1527            return self.vertices[0];
1528        }
1529
1530        let length_squared = segment_vector.length_squared();
1531        // `point` is too far "right" in the picture
1532        if projection_scaled >= length_squared {
1533            return self.vertices[1];
1534        }
1535
1536        // Point lies somewhere in the middle, we compute the closest point by finding the parameter along the line.
1537        let t = projection_scaled / length_squared;
1538        self.vertices[0] + t * segment_vector
1539    }
1540}
1541
1542impl From<[Vec2; 2]> for Segment2d {
1543    #[inline(always)]
1544    fn from(vertices: [Vec2; 2]) -> Self {
1545        Self { vertices }
1546    }
1547}
1548
1549impl From<(Vec2, Vec2)> for Segment2d {
1550    #[inline(always)]
1551    fn from((point1, point2): (Vec2, Vec2)) -> Self {
1552        Self::new(point1, point2)
1553    }
1554}
1555
1556/// A series of connected line segments in 2D space.
1557#[cfg(feature = "alloc")]
1558#[derive(Clone, Debug, PartialEq)]
1559#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1560#[cfg_attr(
1561    feature = "bevy_reflect",
1562    derive(Reflect),
1563    reflect(Debug, PartialEq, Clone)
1564)]
1565#[cfg_attr(
1566    all(feature = "serialize", feature = "bevy_reflect"),
1567    reflect(Serialize, Deserialize)
1568)]
1569pub struct Polyline2d {
1570    /// The vertices of the polyline
1571    pub vertices: Vec<Vec2>,
1572}
1573
1574#[cfg(feature = "alloc")]
1575impl Primitive2d for Polyline2d {}
1576
1577#[cfg(feature = "alloc")]
1578impl FromIterator<Vec2> for Polyline2d {
1579    fn from_iter<I: IntoIterator<Item = Vec2>>(iter: I) -> Self {
1580        Self {
1581            vertices: iter.into_iter().collect(),
1582        }
1583    }
1584}
1585
1586#[cfg(feature = "alloc")]
1587impl Default for Polyline2d {
1588    fn default() -> Self {
1589        Self {
1590            vertices: Vec::from([Vec2::new(-0.5, 0.0), Vec2::new(0.5, 0.0)]),
1591        }
1592    }
1593}
1594
1595#[cfg(feature = "alloc")]
1596impl Polyline2d {
1597    /// Create a new `Polyline2d` from its vertices
1598    pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Self {
1599        Self::from_iter(vertices)
1600    }
1601
1602    /// Create a new `Polyline2d` from two endpoints with subdivision points.
1603    /// `subdivisions = 0` creates a simple line with just start and end points.
1604    /// `subdivisions = 1` adds one point in the middle, creating 2 segments, etc.
1605    pub fn with_subdivisions(start: Vec2, end: Vec2, subdivisions: usize) -> Self {
1606        let total_vertices = subdivisions + 2;
1607        let mut vertices = Vec::with_capacity(total_vertices);
1608
1609        let step = (end - start) / (subdivisions + 1) as f32;
1610        for i in 0..total_vertices {
1611            vertices.push(start + step * i as f32);
1612        }
1613
1614        Self { vertices }
1615    }
1616}
1617
1618/// A triangle in 2D space
1619#[derive(Clone, Copy, Debug, PartialEq)]
1620#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1621#[cfg_attr(
1622    feature = "bevy_reflect",
1623    derive(Reflect),
1624    reflect(Debug, PartialEq, Default, Clone)
1625)]
1626#[cfg_attr(
1627    all(feature = "serialize", feature = "bevy_reflect"),
1628    reflect(Serialize, Deserialize)
1629)]
1630pub struct Triangle2d {
1631    /// The vertices of the triangle
1632    pub vertices: [Vec2; 3],
1633}
1634
1635impl Primitive2d for Triangle2d {}
1636
1637impl Default for Triangle2d {
1638    /// Returns the default [`Triangle2d`] with the vertices `[0.0, 0.5]`, `[-0.5, -0.5]`, and `[0.5, -0.5]`.
1639    fn default() -> Self {
1640        Self {
1641            vertices: [Vec2::Y * 0.5, Vec2::new(-0.5, -0.5), Vec2::new(0.5, -0.5)],
1642        }
1643    }
1644}
1645
1646impl Triangle2d {
1647    /// Create a new `Triangle2d` from points `a`, `b`, and `c`
1648    #[inline(always)]
1649    pub const fn new(a: Vec2, b: Vec2, c: Vec2) -> Self {
1650        Self {
1651            vertices: [a, b, c],
1652        }
1653    }
1654
1655    /// Get the [`WindingOrder`] of the triangle
1656    #[inline(always)]
1657    #[doc(alias = "orientation")]
1658    pub fn winding_order(&self) -> WindingOrder {
1659        let [a, b, c] = self.vertices;
1660        let area = (b - a).perp_dot(c - a);
1661        if area > f32::EPSILON {
1662            WindingOrder::CounterClockwise
1663        } else if area < -f32::EPSILON {
1664            WindingOrder::Clockwise
1665        } else {
1666            WindingOrder::Invalid
1667        }
1668    }
1669
1670    /// Compute the circle passing through all three vertices of the triangle.
1671    /// The vector in the returned tuple is the circumcenter.
1672    pub fn circumcircle(&self) -> (Circle, Vec2) {
1673        // We treat the triangle as translated so that vertex A is at the origin. This simplifies calculations.
1674        //
1675        //     A = (0, 0)
1676        //        *
1677        //       / \
1678        //      /   \
1679        //     /     \
1680        //    /       \
1681        //   /    U    \
1682        //  /           \
1683        // *-------------*
1684        // B             C
1685
1686        let a = self.vertices[0];
1687        let (b, c) = (self.vertices[1] - a, self.vertices[2] - a);
1688        let b_length_sq = b.length_squared();
1689        let c_length_sq = c.length_squared();
1690
1691        // Reference: https://en.wikipedia.org/wiki/Circumcircle#Cartesian_coordinates_2
1692        let inv_d = (2.0 * (b.x * c.y - b.y * c.x)).recip();
1693        let ux = inv_d * (c.y * b_length_sq - b.y * c_length_sq);
1694        let uy = inv_d * (b.x * c_length_sq - c.x * b_length_sq);
1695        let u = Vec2::new(ux, uy);
1696
1697        // Compute true circumcenter and circumradius, adding the tip coordinate so that
1698        // A is translated back to its actual coordinate.
1699        let center = u + a;
1700        let radius = u.length();
1701
1702        (Circle { radius }, center)
1703    }
1704
1705    /// Checks if the triangle is degenerate, meaning it has zero area.
1706    ///
1707    /// A triangle is degenerate if the cross product of the vectors `ab` and `ac` has a length less than `10e-7`.
1708    /// This indicates that the three vertices are collinear or nearly collinear.
1709    #[inline(always)]
1710    pub fn is_degenerate(&self) -> bool {
1711        let [a, b, c] = self.vertices;
1712        let ab = (b - a).extend(0.);
1713        let ac = (c - a).extend(0.);
1714        ab.cross(ac).length() < 10e-7
1715    }
1716
1717    /// Checks if the triangle is acute, meaning all angles are less than 90 degrees
1718    #[inline(always)]
1719    pub fn is_acute(&self) -> bool {
1720        let [a, b, c] = self.vertices;
1721        let ab = b - a;
1722        let bc = c - b;
1723        let ca = a - c;
1724
1725        // a^2 + b^2 < c^2 for an acute triangle
1726        let mut side_lengths = [
1727            ab.length_squared(),
1728            bc.length_squared(),
1729            ca.length_squared(),
1730        ];
1731        side_lengths.sort_by(|a, b| a.partial_cmp(b).unwrap());
1732        side_lengths[0] + side_lengths[1] > side_lengths[2]
1733    }
1734
1735    /// Checks if the triangle is obtuse, meaning one angle is greater than 90 degrees
1736    #[inline(always)]
1737    pub fn is_obtuse(&self) -> bool {
1738        let [a, b, c] = self.vertices;
1739        let ab = b - a;
1740        let bc = c - b;
1741        let ca = a - c;
1742
1743        // a^2 + b^2 > c^2 for an obtuse triangle
1744        let mut side_lengths = [
1745            ab.length_squared(),
1746            bc.length_squared(),
1747            ca.length_squared(),
1748        ];
1749        side_lengths.sort_by(|a, b| a.partial_cmp(b).unwrap());
1750        side_lengths[0] + side_lengths[1] < side_lengths[2]
1751    }
1752
1753    /// Reverse the [`WindingOrder`] of the triangle
1754    /// by swapping the first and last vertices.
1755    #[inline(always)]
1756    pub fn reverse(&mut self) {
1757        self.vertices.swap(0, 2);
1758    }
1759
1760    /// This triangle but reversed.
1761    #[inline(always)]
1762    #[must_use]
1763    pub fn reversed(mut self) -> Self {
1764        self.reverse();
1765        self
1766    }
1767}
1768
1769impl Measured2d for Triangle2d {
1770    /// Get the area of the triangle
1771    #[inline(always)]
1772    fn area(&self) -> f32 {
1773        let [a, b, c] = self.vertices;
1774        ops::abs(a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) / 2.0
1775    }
1776
1777    /// Get the perimeter of the triangle
1778    #[inline(always)]
1779    fn perimeter(&self) -> f32 {
1780        let [a, b, c] = self.vertices;
1781
1782        let ab = a.distance(b);
1783        let bc = b.distance(c);
1784        let ca = c.distance(a);
1785
1786        ab + bc + ca
1787    }
1788}
1789
1790/// A rectangle primitive, which is like a square, except that the width and height can be different
1791#[derive(Clone, Copy, Debug, PartialEq)]
1792#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1793#[cfg_attr(
1794    feature = "bevy_reflect",
1795    derive(Reflect),
1796    reflect(Debug, PartialEq, Default, Clone)
1797)]
1798#[cfg_attr(
1799    all(feature = "serialize", feature = "bevy_reflect"),
1800    reflect(Serialize, Deserialize)
1801)]
1802#[doc(alias = "Quad")]
1803pub struct Rectangle {
1804    /// Half of the width and height of the rectangle
1805    pub half_size: Vec2,
1806}
1807
1808impl Primitive2d for Rectangle {}
1809
1810impl Default for Rectangle {
1811    /// Returns the default [`Rectangle`] with a half-width and half-height of `0.5`.
1812    fn default() -> Self {
1813        Self {
1814            half_size: Vec2::splat(0.5),
1815        }
1816    }
1817}
1818
1819impl Rectangle {
1820    /// Create a new `Rectangle` from a full width and height
1821    #[inline(always)]
1822    pub const fn new(width: f32, height: f32) -> Self {
1823        Self::from_size(Vec2::new(width, height))
1824    }
1825
1826    /// Create a new `Rectangle` from a given full size
1827    #[inline(always)]
1828    pub const fn from_size(size: Vec2) -> Self {
1829        Self {
1830            half_size: Vec2::new(size.x / 2.0, size.y / 2.0),
1831        }
1832    }
1833
1834    /// Create a new `Rectangle` from two corner points
1835    #[inline(always)]
1836    pub fn from_corners(point1: Vec2, point2: Vec2) -> Self {
1837        Self {
1838            half_size: (point2 - point1).abs() / 2.0,
1839        }
1840    }
1841
1842    /// Create a `Rectangle` from a single length.
1843    /// The resulting `Rectangle` will be the same size in every direction.
1844    #[inline(always)]
1845    pub const fn from_length(length: f32) -> Self {
1846        Self {
1847            half_size: Vec2::splat(length / 2.0),
1848        }
1849    }
1850
1851    /// Get the size of the rectangle
1852    #[inline(always)]
1853    pub fn size(&self) -> Vec2 {
1854        2.0 * self.half_size
1855    }
1856
1857    /// Finds the point on the rectangle that is closest to the given `point`.
1858    ///
1859    /// If the point is outside the rectangle, the returned point will be on the perimeter of the rectangle.
1860    /// Otherwise, it will be inside the rectangle and returned as is.
1861    #[inline(always)]
1862    pub fn closest_point(&self, point: Vec2) -> Vec2 {
1863        // Clamp point coordinates to the rectangle
1864        point.clamp(-self.half_size, self.half_size)
1865    }
1866}
1867
1868impl Measured2d for Rectangle {
1869    /// Get the area of the rectangle
1870    #[inline(always)]
1871    fn area(&self) -> f32 {
1872        4.0 * self.half_size.x * self.half_size.y
1873    }
1874
1875    /// Get the perimeter of the rectangle
1876    #[inline(always)]
1877    fn perimeter(&self) -> f32 {
1878        4.0 * (self.half_size.x + self.half_size.y)
1879    }
1880}
1881
1882/// A polygon with N vertices.
1883#[cfg(feature = "alloc")]
1884#[derive(Clone, Debug, PartialEq)]
1885#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1886#[cfg_attr(
1887    feature = "bevy_reflect",
1888    derive(Reflect),
1889    reflect(Debug, PartialEq, Clone)
1890)]
1891#[cfg_attr(
1892    all(feature = "serialize", feature = "bevy_reflect"),
1893    reflect(Serialize, Deserialize)
1894)]
1895pub struct Polygon {
1896    /// The vertices of the `Polygon`
1897    pub vertices: Vec<Vec2>,
1898}
1899
1900#[cfg(feature = "alloc")]
1901impl Primitive2d for Polygon {}
1902
1903#[cfg(feature = "alloc")]
1904impl FromIterator<Vec2> for Polygon {
1905    fn from_iter<I: IntoIterator<Item = Vec2>>(iter: I) -> Self {
1906        Self {
1907            vertices: iter.into_iter().collect(),
1908        }
1909    }
1910}
1911
1912#[cfg(feature = "alloc")]
1913impl Polygon {
1914    /// Create a new `Polygon` from its vertices
1915    pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Self {
1916        Self::from_iter(vertices)
1917    }
1918
1919    /// Tests if the polygon is simple.
1920    ///
1921    /// A polygon is simple if it is not self intersecting and not self tangent.
1922    /// As such, no two edges of the polygon may cross each other and each vertex must not lie on another edge.
1923    #[cfg(feature = "alloc")]
1924    pub fn is_simple(&self) -> bool {
1925        is_polygon_simple(&self.vertices)
1926    }
1927}
1928
1929#[cfg(feature = "alloc")]
1930impl From<ConvexPolygon> for Polygon {
1931    fn from(val: ConvexPolygon) -> Self {
1932        Polygon {
1933            vertices: val.vertices,
1934        }
1935    }
1936}
1937
1938/// A convex polygon with `N` vertices.
1939#[cfg(feature = "alloc")]
1940#[derive(Clone, Debug, PartialEq)]
1941#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1942#[cfg_attr(
1943    feature = "bevy_reflect",
1944    derive(Reflect),
1945    reflect(Debug, PartialEq, Clone)
1946)]
1947#[cfg_attr(
1948    all(feature = "serialize", feature = "bevy_reflect"),
1949    reflect(Serialize, Deserialize)
1950)]
1951pub struct ConvexPolygon {
1952    /// The vertices of the [`ConvexPolygon`].
1953    vertices: Vec<Vec2>,
1954}
1955
1956#[cfg(feature = "alloc")]
1957impl Primitive2d for ConvexPolygon {}
1958
1959/// An error that happens when creating a [`ConvexPolygon`].
1960#[cfg(feature = "alloc")]
1961#[derive(Error, Debug, Clone)]
1962pub enum ConvexPolygonError {
1963    /// The created polygon is not convex.
1964    #[error("The created polygon is not convex")]
1965    Concave,
1966}
1967
1968#[cfg(feature = "alloc")]
1969impl ConvexPolygon {
1970    fn triangle_winding_order(
1971        &self,
1972        a_index: usize,
1973        b_index: usize,
1974        c_index: usize,
1975    ) -> WindingOrder {
1976        let a = self.vertices[a_index];
1977        let b = self.vertices[b_index];
1978        let c = self.vertices[c_index];
1979        Triangle2d::new(a, b, c).winding_order()
1980    }
1981
1982    /// Create a [`ConvexPolygon`] from its `vertices`.
1983    ///
1984    /// # Errors
1985    ///
1986    /// Returns [`ConvexPolygonError::Concave`] if the `vertices` do not form a convex polygon.
1987    pub fn new(vertices: impl IntoIterator<Item = Vec2>) -> Result<Self, ConvexPolygonError> {
1988        let polygon = Self::new_unchecked(vertices);
1989        let len = polygon.vertices.len();
1990        let ref_winding_order = polygon.triangle_winding_order(len - 1, 0, 1);
1991        for i in 1..len {
1992            let winding_order = polygon.triangle_winding_order(i - 1, i, (i + 1) % len);
1993            if winding_order != ref_winding_order {
1994                return Err(ConvexPolygonError::Concave);
1995            }
1996        }
1997        Ok(polygon)
1998    }
1999
2000    /// Create a [`ConvexPolygon`] from its `vertices`, without checks.
2001    /// Use this version only if you know that the `vertices` make up a convex polygon.
2002    #[inline(always)]
2003    pub fn new_unchecked(vertices: impl IntoIterator<Item = Vec2>) -> Self {
2004        Self {
2005            vertices: vertices.into_iter().collect(),
2006        }
2007    }
2008
2009    /// Get the vertices of this polygon
2010    #[inline(always)]
2011    pub fn vertices(&self) -> &[Vec2] {
2012        &self.vertices
2013    }
2014}
2015
2016#[cfg(feature = "alloc")]
2017impl TryFrom<Polygon> for ConvexPolygon {
2018    type Error = ConvexPolygonError;
2019
2020    fn try_from(val: Polygon) -> Result<Self, Self::Error> {
2021        ConvexPolygon::new(val.vertices)
2022    }
2023}
2024
2025/// A polygon centered on the origin where all vertices lie on a circle, equally far apart.
2026#[derive(Clone, Copy, Debug, PartialEq)]
2027#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2028#[cfg_attr(
2029    feature = "bevy_reflect",
2030    derive(Reflect),
2031    reflect(Debug, PartialEq, Default, Clone)
2032)]
2033#[cfg_attr(
2034    all(feature = "serialize", feature = "bevy_reflect"),
2035    reflect(Serialize, Deserialize)
2036)]
2037pub struct RegularPolygon {
2038    /// The circumcircle on which all vertices lie
2039    pub circumcircle: Circle,
2040    /// The number of sides
2041    pub sides: u32,
2042}
2043
2044impl Primitive2d for RegularPolygon {}
2045
2046impl Default for RegularPolygon {
2047    /// Returns the default [`RegularPolygon`] with six sides (a hexagon) and a circumradius of `0.5`.
2048    fn default() -> Self {
2049        Self {
2050            circumcircle: Circle { radius: 0.5 },
2051            sides: 6,
2052        }
2053    }
2054}
2055
2056impl RegularPolygon {
2057    /// Create a new `RegularPolygon`
2058    /// from the radius of the circumcircle and a number of sides
2059    ///
2060    /// # Panics
2061    ///
2062    /// Panics if `circumradius` is negative
2063    #[inline(always)]
2064    pub const fn new(circumradius: f32, sides: u32) -> Self {
2065        assert!(
2066            circumradius.is_sign_positive(),
2067            "polygon has a negative radius"
2068        );
2069        assert!(sides > 2, "polygon has less than 3 sides");
2070
2071        Self {
2072            circumcircle: Circle {
2073                radius: circumradius,
2074            },
2075            sides,
2076        }
2077    }
2078
2079    /// Get the radius of the circumcircle on which all vertices
2080    /// of the regular polygon lie
2081    #[inline(always)]
2082    pub const fn circumradius(&self) -> f32 {
2083        self.circumcircle.radius
2084    }
2085
2086    /// Get the inradius or apothem of the regular polygon.
2087    /// This is the radius of the largest circle that can
2088    /// be drawn within the polygon
2089    #[inline(always)]
2090    #[doc(alias = "apothem")]
2091    pub fn inradius(&self) -> f32 {
2092        self.circumradius() * ops::cos(PI / self.sides as f32)
2093    }
2094
2095    /// Get the length of one side of the regular polygon
2096    #[inline(always)]
2097    pub fn side_length(&self) -> f32 {
2098        2.0 * self.circumradius() * ops::sin(PI / self.sides as f32)
2099    }
2100
2101    /// Get the internal angle of the regular polygon in degrees.
2102    ///
2103    /// This is the angle formed by two adjacent sides with points
2104    /// within the angle being in the interior of the polygon
2105    #[inline(always)]
2106    pub const fn internal_angle_degrees(&self) -> f32 {
2107        (self.sides - 2) as f32 / self.sides as f32 * 180.0
2108    }
2109
2110    /// Get the internal angle of the regular polygon in radians.
2111    ///
2112    /// This is the angle formed by two adjacent sides with points
2113    /// within the angle being in the interior of the polygon
2114    #[inline(always)]
2115    pub const fn internal_angle_radians(&self) -> f32 {
2116        (self.sides - 2) as f32 * PI / self.sides as f32
2117    }
2118
2119    /// Get the external angle of the regular polygon in degrees.
2120    ///
2121    /// This is the angle formed by two adjacent sides with points
2122    /// within the angle being in the exterior of the polygon
2123    #[inline(always)]
2124    pub const fn external_angle_degrees(&self) -> f32 {
2125        360.0 / self.sides as f32
2126    }
2127
2128    /// Get the external angle of the regular polygon in radians.
2129    ///
2130    /// This is the angle formed by two adjacent sides with points
2131    /// within the angle being in the exterior of the polygon
2132    #[inline(always)]
2133    pub const fn external_angle_radians(&self) -> f32 {
2134        2.0 * PI / self.sides as f32
2135    }
2136
2137    /// Returns an iterator over the vertices of the regular polygon,
2138    /// rotated counterclockwise by the given angle in radians.
2139    ///
2140    /// With a rotation of 0, a vertex will be placed at the top `(0.0, circumradius)`.
2141    pub fn vertices(self, rotation: f32) -> impl IntoIterator<Item = Vec2> {
2142        // Add pi/2 so that the polygon has a vertex at the top (sin is 1.0 and cos is 0.0)
2143        let start_angle = rotation + FRAC_PI_2;
2144        let step = core::f32::consts::TAU / self.sides as f32;
2145
2146        (0..self.sides).map(move |i| {
2147            let theta = start_angle + i as f32 * step;
2148            let (sin, cos) = ops::sin_cos(theta);
2149            Vec2::new(cos, sin) * self.circumcircle.radius
2150        })
2151    }
2152}
2153
2154impl Measured2d for RegularPolygon {
2155    /// Get the area of the regular polygon
2156    #[inline(always)]
2157    fn area(&self) -> f32 {
2158        let angle: f32 = 2.0 * PI / (self.sides as f32);
2159        (self.sides as f32) * self.circumradius().squared() * ops::sin(angle) / 2.0
2160    }
2161
2162    /// Get the perimeter of the regular polygon.
2163    /// This is the sum of its sides
2164    #[inline(always)]
2165    fn perimeter(&self) -> f32 {
2166        self.sides as f32 * self.side_length()
2167    }
2168}
2169
2170/// A 2D capsule primitive, also known as a stadium or pill shape.
2171///
2172/// A two-dimensional capsule is defined as a neighborhood of points at a distance (radius) from a line
2173#[derive(Clone, Copy, Debug, PartialEq)]
2174#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
2175#[cfg_attr(
2176    feature = "bevy_reflect",
2177    derive(Reflect),
2178    reflect(Debug, PartialEq, Default, Clone)
2179)]
2180#[cfg_attr(
2181    all(feature = "serialize", feature = "bevy_reflect"),
2182    reflect(Serialize, Deserialize)
2183)]
2184#[doc(alias = "stadium", alias = "pill")]
2185pub struct Capsule2d {
2186    /// The radius of the capsule
2187    pub radius: f32,
2188    /// Half the height of the capsule, excluding the semicircles
2189    pub half_length: f32,
2190}
2191
2192impl Primitive2d for Capsule2d {}
2193
2194impl Default for Capsule2d {
2195    /// Returns the default [`Capsule2d`] with a radius of `0.5` and a half-height of `0.5`,
2196    /// excluding the semicircles.
2197    fn default() -> Self {
2198        Self {
2199            radius: 0.5,
2200            half_length: 0.5,
2201        }
2202    }
2203}
2204
2205impl Capsule2d {
2206    /// Create a new `Capsule2d` from a radius and length
2207    pub const fn new(radius: f32, length: f32) -> Self {
2208        Self {
2209            radius,
2210            half_length: length / 2.0,
2211        }
2212    }
2213
2214    /// Get the part connecting the semicircular ends of the capsule as a [`Rectangle`]
2215    #[inline]
2216    pub const fn to_inner_rectangle(&self) -> Rectangle {
2217        Rectangle::new(self.radius * 2.0, self.half_length * 2.0)
2218    }
2219}
2220
2221impl Measured2d for Capsule2d {
2222    /// Get the area of the capsule
2223    #[inline]
2224    fn area(&self) -> f32 {
2225        // pi*r^2 + (2r)*l
2226        PI * self.radius.squared() + self.to_inner_rectangle().area()
2227    }
2228
2229    /// Get the perimeter of the capsule
2230    #[inline]
2231    fn perimeter(&self) -> f32 {
2232        // 2pi*r + 2l
2233        2.0 * PI * self.radius + 4.0 * self.half_length
2234    }
2235}
2236
2237#[cfg(test)]
2238mod tests {
2239    // Reference values were computed by hand and/or with external tools
2240
2241    use super::*;
2242    use approx::{assert_abs_diff_eq, assert_relative_eq};
2243
2244    #[test]
2245    fn rectangle_closest_point() {
2246        let rectangle = Rectangle::new(2.0, 2.0);
2247        assert_eq!(rectangle.closest_point(Vec2::X * 10.0), Vec2::X);
2248        assert_eq!(rectangle.closest_point(Vec2::NEG_ONE * 10.0), Vec2::NEG_ONE);
2249        assert_eq!(
2250            rectangle.closest_point(Vec2::new(0.25, 0.1)),
2251            Vec2::new(0.25, 0.1)
2252        );
2253    }
2254
2255    #[test]
2256    fn circle_closest_point() {
2257        let circle = Circle { radius: 1.0 };
2258        assert_eq!(circle.closest_point(Vec2::X * 10.0), Vec2::X);
2259        assert_eq!(
2260            circle.closest_point(Vec2::NEG_ONE * 10.0),
2261            Vec2::NEG_ONE.normalize()
2262        );
2263        assert_eq!(
2264            circle.closest_point(Vec2::new(0.25, 0.1)),
2265            Vec2::new(0.25, 0.1)
2266        );
2267    }
2268
2269    #[test]
2270    fn annulus_closest_point() {
2271        let annulus = Annulus::new(1.5, 2.0);
2272        assert_eq!(annulus.closest_point(Vec2::X * 10.0), Vec2::X * 2.0);
2273        assert_eq!(
2274            annulus.closest_point(Vec2::NEG_ONE),
2275            Vec2::NEG_ONE.normalize() * 1.5
2276        );
2277        assert_eq!(
2278            annulus.closest_point(Vec2::new(1.55, 0.85)),
2279            Vec2::new(1.55, 0.85)
2280        );
2281    }
2282
2283    #[test]
2284    fn rhombus_closest_point() {
2285        let rhombus = Rhombus::new(2.0, 1.0);
2286        assert_eq!(rhombus.closest_point(Vec2::X * 10.0), Vec2::X);
2287        assert_eq!(
2288            rhombus.closest_point(Vec2::NEG_ONE * 0.2),
2289            Vec2::NEG_ONE * 0.2
2290        );
2291        assert_eq!(
2292            rhombus.closest_point(Vec2::new(-0.55, 0.35)),
2293            Vec2::new(-0.5, 0.25)
2294        );
2295
2296        let rhombus = Rhombus::new(0.0, 0.0);
2297        assert_eq!(rhombus.closest_point(Vec2::X * 10.0), Vec2::ZERO);
2298        assert_eq!(rhombus.closest_point(Vec2::NEG_ONE * 0.2), Vec2::ZERO);
2299        assert_eq!(rhombus.closest_point(Vec2::new(-0.55, 0.35)), Vec2::ZERO);
2300    }
2301
2302    #[test]
2303    fn segment_closest_point() {
2304        assert_eq!(
2305            Segment2d::new(Vec2::new(0.0, 0.0), Vec2::new(3.0, 0.0))
2306                .closest_point(Vec2::new(1.0, 6.0)),
2307            Vec2::new(1.0, 0.0)
2308        );
2309
2310        let segments = [
2311            Segment2d::new(Vec2::new(0.0, 0.0), Vec2::new(0.0, 0.0)),
2312            Segment2d::new(Vec2::new(0.0, 0.0), Vec2::new(1.0, 0.0)),
2313            Segment2d::new(Vec2::new(1.0, 0.0), Vec2::new(0.0, 1.0)),
2314            Segment2d::new(Vec2::new(1.0, 0.0), Vec2::new(1.0, 5.0 * f32::EPSILON)),
2315        ];
2316        let points = [
2317            Vec2::new(0.0, 0.0),
2318            Vec2::new(1.0, 0.0),
2319            Vec2::new(-1.0, 1.0),
2320            Vec2::new(1.0, 1.0),
2321            Vec2::new(-1.0, 0.0),
2322            Vec2::new(5.0, -1.0),
2323            Vec2::new(1.0, f32::EPSILON),
2324        ];
2325
2326        for point in points.iter() {
2327            for segment in segments.iter() {
2328                let closest = segment.closest_point(*point);
2329                assert!(
2330                    point.distance_squared(closest) <= point.distance_squared(segment.point1()),
2331                    "Closest point must always be at least as close as either vertex."
2332                );
2333                assert!(
2334                    point.distance_squared(closest) <= point.distance_squared(segment.point2()),
2335                    "Closest point must always be at least as close as either vertex."
2336                );
2337                assert!(
2338                    point.distance_squared(closest) <= point.distance_squared(segment.center()),
2339                    "Closest point must always be at least as close as the center."
2340                );
2341                let closest_to_closest = segment.closest_point(closest);
2342                // Closest point must already be on the segment
2343                assert_relative_eq!(closest_to_closest, closest);
2344            }
2345        }
2346    }
2347
2348    #[test]
2349    fn circle_math() {
2350        let circle = Circle { radius: 3.0 };
2351        assert_eq!(circle.diameter(), 6.0, "incorrect diameter");
2352        assert_eq!(circle.area(), 28.274334, "incorrect area");
2353        assert_eq!(circle.perimeter(), 18.849556, "incorrect perimeter");
2354    }
2355
2356    #[test]
2357    fn capsule_math() {
2358        let capsule = Capsule2d::new(2.0, 9.0);
2359        assert_eq!(
2360            capsule.to_inner_rectangle(),
2361            Rectangle::new(4.0, 9.0),
2362            "rectangle wasn't created correctly from a capsule"
2363        );
2364        assert_eq!(capsule.area(), 48.566371, "incorrect area");
2365        assert_eq!(capsule.perimeter(), 30.566371, "incorrect perimeter");
2366    }
2367
2368    #[test]
2369    fn annulus_math() {
2370        let annulus = Annulus::new(2.5, 3.5);
2371        assert_eq!(annulus.diameter(), 7.0, "incorrect diameter");
2372        assert_eq!(annulus.thickness(), 1.0, "incorrect thickness");
2373        assert_eq!(annulus.area(), 18.849556, "incorrect area");
2374        assert_eq!(annulus.perimeter(), 37.699112, "incorrect perimeter");
2375    }
2376
2377    #[test]
2378    fn rhombus_math() {
2379        let rhombus = Rhombus::new(3.0, 4.0);
2380        assert_eq!(rhombus.area(), 6.0, "incorrect area");
2381        assert_eq!(rhombus.perimeter(), 10.0, "incorrect perimeter");
2382        assert_eq!(rhombus.side(), 2.5, "incorrect side");
2383        assert_eq!(rhombus.inradius(), 1.2, "incorrect inradius");
2384        assert_eq!(rhombus.circumradius(), 2.0, "incorrect circumradius");
2385        let rhombus = Rhombus::new(0.0, 0.0);
2386        assert_eq!(rhombus.area(), 0.0, "incorrect area");
2387        assert_eq!(rhombus.perimeter(), 0.0, "incorrect perimeter");
2388        assert_eq!(rhombus.side(), 0.0, "incorrect side");
2389        assert_eq!(rhombus.inradius(), 0.0, "incorrect inradius");
2390        assert_eq!(rhombus.circumradius(), 0.0, "incorrect circumradius");
2391        let rhombus = Rhombus::from_side(core::f32::consts::SQRT_2);
2392        assert_abs_diff_eq!(rhombus.half_diagonals, Vec2::new(1.0, 1.0));
2393        assert_abs_diff_eq!(
2394            rhombus.half_diagonals,
2395            Rhombus::from_inradius(FRAC_1_SQRT_2).half_diagonals
2396        );
2397    }
2398
2399    #[test]
2400    fn ellipse_math() {
2401        let ellipse = Ellipse::new(3.0, 1.0);
2402        assert_eq!(ellipse.area(), 9.424778, "incorrect area");
2403
2404        assert_eq!(ellipse.eccentricity(), 0.94280905, "incorrect eccentricity");
2405
2406        let line = Ellipse::new(1., 0.);
2407        assert_eq!(line.eccentricity(), 1., "incorrect line eccentricity");
2408
2409        let circle = Ellipse::new(2., 2.);
2410        assert_eq!(circle.eccentricity(), 0., "incorrect circle eccentricity");
2411    }
2412
2413    #[test]
2414    fn ellipse_perimeter() {
2415        let circle = Ellipse::new(1., 1.);
2416        assert_relative_eq!(circle.perimeter(), 6.2831855);
2417
2418        let line = Ellipse::new(75_000., 0.5);
2419        assert_relative_eq!(line.perimeter(), 300_000.);
2420
2421        let ellipse = Ellipse::new(0.5, 2.);
2422        assert_relative_eq!(ellipse.perimeter(), 8.578423);
2423
2424        let ellipse = Ellipse::new(5., 3.);
2425        assert_relative_eq!(ellipse.perimeter(), 25.526999);
2426    }
2427
2428    #[test]
2429    fn triangle_math() {
2430        let triangle = Triangle2d::new(
2431            Vec2::new(-2.0, -1.0),
2432            Vec2::new(1.0, 4.0),
2433            Vec2::new(7.0, 0.0),
2434        );
2435        assert_eq!(triangle.area(), 21.0, "incorrect area");
2436        assert_eq!(triangle.perimeter(), 22.097439, "incorrect perimeter");
2437
2438        let degenerate_triangle =
2439            Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(0., 0.), Vec2::new(1., 0.));
2440        assert!(degenerate_triangle.is_degenerate());
2441
2442        let acute_triangle =
2443            Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(1., 0.), Vec2::new(0., 5.));
2444        let obtuse_triangle =
2445            Triangle2d::new(Vec2::new(-1., 0.), Vec2::new(1., 0.), Vec2::new(0., 0.5));
2446
2447        assert!(acute_triangle.is_acute());
2448        assert!(!acute_triangle.is_obtuse());
2449        assert!(!obtuse_triangle.is_acute());
2450        assert!(obtuse_triangle.is_obtuse());
2451    }
2452
2453    #[test]
2454    fn triangle_winding_order() {
2455        let mut cw_triangle = Triangle2d::new(
2456            Vec2::new(0.0, 2.0),
2457            Vec2::new(-0.5, -1.2),
2458            Vec2::new(-1.0, -1.0),
2459        );
2460        assert_eq!(cw_triangle.winding_order(), WindingOrder::Clockwise);
2461
2462        let ccw_triangle = Triangle2d::new(
2463            Vec2::new(-1.0, -1.0),
2464            Vec2::new(-0.5, -1.2),
2465            Vec2::new(0.0, 2.0),
2466        );
2467        assert_eq!(ccw_triangle.winding_order(), WindingOrder::CounterClockwise);
2468
2469        // The clockwise triangle should be the same as the counterclockwise
2470        // triangle when reversed
2471        cw_triangle.reverse();
2472        assert_eq!(cw_triangle, ccw_triangle);
2473
2474        let invalid_triangle = Triangle2d::new(
2475            Vec2::new(0.0, 2.0),
2476            Vec2::new(0.0, -1.0),
2477            Vec2::new(0.0, -1.2),
2478        );
2479        assert_eq!(invalid_triangle.winding_order(), WindingOrder::Invalid);
2480    }
2481
2482    #[test]
2483    fn rectangle_math() {
2484        let rectangle = Rectangle::new(3.0, 7.0);
2485        assert_eq!(
2486            rectangle,
2487            Rectangle::from_corners(Vec2::new(-1.5, -3.5), Vec2::new(1.5, 3.5))
2488        );
2489        assert_eq!(rectangle.area(), 21.0, "incorrect area");
2490        assert_eq!(rectangle.perimeter(), 20.0, "incorrect perimeter");
2491    }
2492
2493    #[test]
2494    fn regular_polygon_math() {
2495        let polygon = RegularPolygon::new(3.0, 6);
2496        assert_eq!(polygon.inradius(), 2.598076, "incorrect inradius");
2497        assert_eq!(polygon.side_length(), 3.0, "incorrect side length");
2498        assert_relative_eq!(polygon.area(), 23.38268, epsilon = 0.00001);
2499        assert_eq!(polygon.perimeter(), 18.0, "incorrect perimeter");
2500        assert_eq!(
2501            polygon.internal_angle_degrees(),
2502            120.0,
2503            "incorrect internal angle"
2504        );
2505        assert_eq!(
2506            polygon.internal_angle_radians(),
2507            120_f32.to_radians(),
2508            "incorrect internal angle"
2509        );
2510        assert_eq!(
2511            polygon.external_angle_degrees(),
2512            60.0,
2513            "incorrect external angle"
2514        );
2515        assert_eq!(
2516            polygon.external_angle_radians(),
2517            60_f32.to_radians(),
2518            "incorrect external angle"
2519        );
2520    }
2521
2522    #[test]
2523    fn triangle_circumcenter() {
2524        let triangle = Triangle2d::new(
2525            Vec2::new(10.0, 2.0),
2526            Vec2::new(-5.0, -3.0),
2527            Vec2::new(2.0, -1.0),
2528        );
2529        let (Circle { radius }, circumcenter) = triangle.circumcircle();
2530
2531        // Calculated with external calculator
2532        assert_eq!(radius, 98.34887);
2533        assert_eq!(circumcenter, Vec2::new(-28.5, 92.5));
2534    }
2535
2536    #[test]
2537    fn regular_polygon_vertices() {
2538        let polygon = RegularPolygon::new(1.0, 4);
2539
2540        // Regular polygons have a vertex at the top by default
2541        let mut vertices = polygon.vertices(0.0).into_iter();
2542        assert!((vertices.next().unwrap() - Vec2::Y).length() < 1e-7);
2543
2544        // Rotate by 45 degrees, forming an axis-aligned square
2545        let mut rotated_vertices = polygon.vertices(core::f32::consts::FRAC_PI_4).into_iter();
2546
2547        // Distance from the origin to the middle of a side, derived using Pythagorean theorem
2548        let side_distance = FRAC_1_SQRT_2;
2549        assert!(
2550            (rotated_vertices.next().unwrap() - Vec2::new(-side_distance, side_distance)).length()
2551                < 1e-7,
2552        );
2553    }
2554}