#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
use use_circle::Circle;
use use_point::Point2;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConicKind {
Circle,
Ellipse,
Parabola,
Hyperbola,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Conic {
kind: ConicKind,
}
impl Conic {
#[must_use]
pub const fn new(kind: ConicKind) -> Self {
Self { kind }
}
#[must_use]
pub const fn kind(self) -> ConicKind {
self.kind
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Ellipse {
center: Point2,
major_radius: f64,
minor_radius: f64,
}
impl Ellipse {
#[must_use]
pub fn new(center: Point2, major_radius: f64, minor_radius: f64) -> Option<Self> {
if major_radius.is_finite()
&& minor_radius.is_finite()
&& major_radius > 0.0
&& minor_radius > 0.0
{
Some(Self {
center,
major_radius,
minor_radius,
})
} else {
None
}
}
#[must_use]
pub const fn kind(self) -> ConicKind {
ConicKind::Ellipse
}
#[must_use]
pub const fn center(self) -> Point2 {
self.center
}
#[must_use]
pub const fn major_radius(self) -> f64 {
self.major_radius
}
#[must_use]
pub const fn minor_radius(self) -> f64 {
self.minor_radius
}
}
impl From<Circle> for Ellipse {
fn from(circle: Circle) -> Self {
Self {
center: circle.center(),
major_radius: circle.radius(),
minor_radius: circle.radius(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Parabola {
vertex: Point2,
focal_parameter: f64,
}
impl Parabola {
#[must_use]
pub fn new(vertex: Point2, focal_parameter: f64) -> Option<Self> {
if focal_parameter.is_finite() && focal_parameter != 0.0 {
Some(Self {
vertex,
focal_parameter,
})
} else {
None
}
}
#[must_use]
pub const fn kind(self) -> ConicKind {
ConicKind::Parabola
}
#[must_use]
pub const fn vertex(self) -> Point2 {
self.vertex
}
#[must_use]
pub const fn focal_parameter(self) -> f64 {
self.focal_parameter
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Hyperbola {
center: Point2,
transverse_radius: f64,
conjugate_radius: f64,
}
impl Hyperbola {
#[must_use]
pub fn new(center: Point2, transverse_radius: f64, conjugate_radius: f64) -> Option<Self> {
if transverse_radius.is_finite()
&& conjugate_radius.is_finite()
&& transverse_radius > 0.0
&& conjugate_radius > 0.0
{
Some(Self {
center,
transverse_radius,
conjugate_radius,
})
} else {
None
}
}
#[must_use]
pub const fn kind(self) -> ConicKind {
ConicKind::Hyperbola
}
#[must_use]
pub const fn center(self) -> Point2 {
self.center
}
#[must_use]
pub const fn transverse_radius(self) -> f64 {
self.transverse_radius
}
#[must_use]
pub const fn conjugate_radius(self) -> f64 {
self.conjugate_radius
}
}
#[cfg(test)]
mod tests {
use super::{Conic, ConicKind, Ellipse, Hyperbola, Parabola};
use use_circle::Circle;
use use_point::Point2;
#[test]
fn creates_conic_descriptors() {
assert_eq!(Conic::new(ConicKind::Ellipse).kind(), ConicKind::Ellipse);
}
#[test]
fn creates_named_conics() {
let ellipse = Ellipse::new(Point2::origin(), 4.0, 2.0).expect("valid ellipse");
let parabola = Parabola::new(Point2::origin(), 1.0).expect("valid parabola");
let hyperbola = Hyperbola::new(Point2::origin(), 3.0, 2.0).expect("valid hyperbola");
let circle = Circle::try_new(Point2::origin(), 2.0).expect("valid circle");
let circle_as_ellipse = Ellipse::from(circle);
assert_eq!(ellipse.kind(), ConicKind::Ellipse);
assert_eq!(ellipse.major_radius(), 4.0);
assert_eq!(parabola.kind(), ConicKind::Parabola);
assert_eq!(hyperbola.kind(), ConicKind::Hyperbola);
assert_eq!(circle_as_ellipse.minor_radius(), 2.0);
assert_eq!(Ellipse::new(Point2::origin(), 0.0, 2.0), None);
}
}