Skip to main content

use_conic/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use use_circle::Circle;
5use use_point::Point2;
6
7/// The high-level family of a conic section.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum ConicKind {
10    /// A circle.
11    Circle,
12    /// An ellipse.
13    Ellipse,
14    /// A parabola.
15    Parabola,
16    /// A hyperbola.
17    Hyperbola,
18}
19
20/// A small conic descriptor.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct Conic {
23    kind: ConicKind,
24}
25
26impl Conic {
27    /// Creates a conic descriptor.
28    #[must_use]
29    pub const fn new(kind: ConicKind) -> Self {
30        Self { kind }
31    }
32
33    /// Returns the conic family.
34    #[must_use]
35    pub const fn kind(self) -> ConicKind {
36        self.kind
37    }
38}
39
40/// An ellipse represented by a center and two positive radii.
41#[derive(Debug, Clone, Copy, PartialEq)]
42pub struct Ellipse {
43    center: Point2,
44    major_radius: f64,
45    minor_radius: f64,
46}
47
48impl Ellipse {
49    /// Creates an ellipse with positive finite radii.
50    #[must_use]
51    pub fn new(center: Point2, major_radius: f64, minor_radius: f64) -> Option<Self> {
52        if major_radius.is_finite()
53            && minor_radius.is_finite()
54            && major_radius > 0.0
55            && minor_radius > 0.0
56        {
57            Some(Self {
58                center,
59                major_radius,
60                minor_radius,
61            })
62        } else {
63            None
64        }
65    }
66
67    /// Returns the conic kind.
68    #[must_use]
69    pub const fn kind(self) -> ConicKind {
70        ConicKind::Ellipse
71    }
72
73    /// Returns the center.
74    #[must_use]
75    pub const fn center(self) -> Point2 {
76        self.center
77    }
78
79    /// Returns the major radius.
80    #[must_use]
81    pub const fn major_radius(self) -> f64 {
82        self.major_radius
83    }
84
85    /// Returns the minor radius.
86    #[must_use]
87    pub const fn minor_radius(self) -> f64 {
88        self.minor_radius
89    }
90}
91
92impl From<Circle> for Ellipse {
93    fn from(circle: Circle) -> Self {
94        Self {
95            center: circle.center(),
96            major_radius: circle.radius(),
97            minor_radius: circle.radius(),
98        }
99    }
100}
101
102/// A parabola descriptor using a vertex and focal parameter.
103#[derive(Debug, Clone, Copy, PartialEq)]
104pub struct Parabola {
105    vertex: Point2,
106    focal_parameter: f64,
107}
108
109impl Parabola {
110    /// Creates a parabola with a finite, non-zero focal parameter.
111    #[must_use]
112    pub fn new(vertex: Point2, focal_parameter: f64) -> Option<Self> {
113        if focal_parameter.is_finite() && focal_parameter != 0.0 {
114            Some(Self {
115                vertex,
116                focal_parameter,
117            })
118        } else {
119            None
120        }
121    }
122
123    /// Returns the conic kind.
124    #[must_use]
125    pub const fn kind(self) -> ConicKind {
126        ConicKind::Parabola
127    }
128
129    /// Returns the vertex.
130    #[must_use]
131    pub const fn vertex(self) -> Point2 {
132        self.vertex
133    }
134
135    /// Returns the focal parameter.
136    #[must_use]
137    pub const fn focal_parameter(self) -> f64 {
138        self.focal_parameter
139    }
140}
141
142/// A hyperbola represented by a center and positive transverse/conjugate radii.
143#[derive(Debug, Clone, Copy, PartialEq)]
144pub struct Hyperbola {
145    center: Point2,
146    transverse_radius: f64,
147    conjugate_radius: f64,
148}
149
150impl Hyperbola {
151    /// Creates a hyperbola with positive finite radii.
152    #[must_use]
153    pub fn new(center: Point2, transverse_radius: f64, conjugate_radius: f64) -> Option<Self> {
154        if transverse_radius.is_finite()
155            && conjugate_radius.is_finite()
156            && transverse_radius > 0.0
157            && conjugate_radius > 0.0
158        {
159            Some(Self {
160                center,
161                transverse_radius,
162                conjugate_radius,
163            })
164        } else {
165            None
166        }
167    }
168
169    /// Returns the conic kind.
170    #[must_use]
171    pub const fn kind(self) -> ConicKind {
172        ConicKind::Hyperbola
173    }
174
175    /// Returns the center.
176    #[must_use]
177    pub const fn center(self) -> Point2 {
178        self.center
179    }
180
181    /// Returns the transverse radius.
182    #[must_use]
183    pub const fn transverse_radius(self) -> f64 {
184        self.transverse_radius
185    }
186
187    /// Returns the conjugate radius.
188    #[must_use]
189    pub const fn conjugate_radius(self) -> f64 {
190        self.conjugate_radius
191    }
192}
193
194#[cfg(test)]
195mod tests {
196    use super::{Conic, ConicKind, Ellipse, Hyperbola, Parabola};
197    use use_circle::Circle;
198    use use_point::Point2;
199
200    #[test]
201    fn creates_conic_descriptors() {
202        assert_eq!(Conic::new(ConicKind::Ellipse).kind(), ConicKind::Ellipse);
203    }
204
205    #[test]
206    fn creates_named_conics() {
207        let ellipse = Ellipse::new(Point2::origin(), 4.0, 2.0).expect("valid ellipse");
208        let parabola = Parabola::new(Point2::origin(), 1.0).expect("valid parabola");
209        let hyperbola = Hyperbola::new(Point2::origin(), 3.0, 2.0).expect("valid hyperbola");
210        let circle = Circle::try_new(Point2::origin(), 2.0).expect("valid circle");
211        let circle_as_ellipse = Ellipse::from(circle);
212
213        assert_eq!(ellipse.kind(), ConicKind::Ellipse);
214        assert_eq!(ellipse.major_radius(), 4.0);
215        assert_eq!(parabola.kind(), ConicKind::Parabola);
216        assert_eq!(hyperbola.kind(), ConicKind::Hyperbola);
217        assert_eq!(circle_as_ellipse.minor_radius(), 2.0);
218        assert_eq!(Ellipse::new(Point2::origin(), 0.0, 2.0), None);
219    }
220}