1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use use_circle::Circle;
5use use_point::Point2;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum ConicKind {
10 Circle,
12 Ellipse,
14 Parabola,
16 Hyperbola,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct Conic {
23 kind: ConicKind,
24}
25
26impl Conic {
27 #[must_use]
29 pub const fn new(kind: ConicKind) -> Self {
30 Self { kind }
31 }
32
33 #[must_use]
35 pub const fn kind(self) -> ConicKind {
36 self.kind
37 }
38}
39
40#[derive(Debug, Clone, Copy, PartialEq)]
42pub struct Ellipse {
43 center: Point2,
44 major_radius: f64,
45 minor_radius: f64,
46}
47
48impl Ellipse {
49 #[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 #[must_use]
69 pub const fn kind(self) -> ConicKind {
70 ConicKind::Ellipse
71 }
72
73 #[must_use]
75 pub const fn center(self) -> Point2 {
76 self.center
77 }
78
79 #[must_use]
81 pub const fn major_radius(self) -> f64 {
82 self.major_radius
83 }
84
85 #[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#[derive(Debug, Clone, Copy, PartialEq)]
104pub struct Parabola {
105 vertex: Point2,
106 focal_parameter: f64,
107}
108
109impl Parabola {
110 #[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 #[must_use]
125 pub const fn kind(self) -> ConicKind {
126 ConicKind::Parabola
127 }
128
129 #[must_use]
131 pub const fn vertex(self) -> Point2 {
132 self.vertex
133 }
134
135 #[must_use]
137 pub const fn focal_parameter(self) -> f64 {
138 self.focal_parameter
139 }
140}
141
142#[derive(Debug, Clone, Copy, PartialEq)]
144pub struct Hyperbola {
145 center: Point2,
146 transverse_radius: f64,
147 conjugate_radius: f64,
148}
149
150impl Hyperbola {
151 #[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 #[must_use]
171 pub const fn kind(self) -> ConicKind {
172 ConicKind::Hyperbola
173 }
174
175 #[must_use]
177 pub const fn center(self) -> Point2 {
178 self.center
179 }
180
181 #[must_use]
183 pub const fn transverse_radius(self) -> f64 {
184 self.transverse_radius
185 }
186
187 #[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}