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