use crate::astro::orbit::Orbit;
use qtty::*;
type RadiansPerDay = qtty::Quantity<qtty::Per<Radian, Day>>;
const GAUSSIAN_GRAVITATIONAL_CONSTANT: RadiansPerDay = Quantity::new(0.017_202_098_95);
#[derive(Clone, Debug)]
pub struct Planet {
pub mass: Kilograms,
pub radius: Kilometers,
pub orbit: Orbit,
}
impl Planet {
pub const fn new_const(mass: Kilograms, radius: Kilometers, orbit: Orbit) -> Self {
Self {
mass,
radius,
orbit,
}
}
pub fn builder() -> PlanetBuilder {
PlanetBuilder::default()
}
}
#[derive(Debug, Clone)]
pub enum PlanetBuilderError {
MissingMass,
MissingRadius,
MissingOrbit,
}
#[derive(Debug, Default, Clone)]
pub struct PlanetBuilder {
mass: Option<Kilograms>,
radius: Option<Kilometers>,
orbit: Option<Orbit>,
}
impl PlanetBuilder {
pub fn mass(mut self, mass: impl Into<Kilograms>) -> Self {
self.mass = Some(mass.into());
self
}
pub fn radius(mut self, radius: impl Into<Kilometers>) -> Self {
self.radius = Some(radius.into());
self
}
pub fn orbit(mut self, orbit: Orbit) -> Self {
self.orbit = Some(orbit);
self
}
pub fn try_build(self) -> Result<Planet, PlanetBuilderError> {
Ok(Planet {
mass: self.mass.ok_or(PlanetBuilderError::MissingMass)?,
radius: self.radius.ok_or(PlanetBuilderError::MissingRadius)?,
orbit: self.orbit.ok_or(PlanetBuilderError::MissingOrbit)?,
})
}
pub fn build(self) -> Planet {
self.try_build().expect("incomplete PlanetBuilder")
}
pub const fn build_unchecked(self) -> Planet {
match (self.mass, self.radius, self.orbit) {
(Some(mass), Some(radius), Some(orbit)) => Planet {
mass,
radius,
orbit,
},
_ => panic!("PlanetBuilder::build_unchecked called with missing fields"),
}
}
}
pub trait OrbitExt {
fn period(&self) -> Seconds;
}
impl OrbitExt for Orbit {
fn period(&self) -> Seconds {
use std::f64::consts::PI;
let a_au = self.semi_major_axis.to::<AstronomicalUnit>().value();
let k = GAUSSIAN_GRAVITATIONAL_CONSTANT.value();
let t_days = (2.0 * PI / k) * a_au.powf(1.5);
Seconds::new(t_days * 86_400.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::time::JulianDate;
use qtty::{AstronomicalUnits, Degrees, Kilograms, Kilometers};
#[test]
fn builder_roundtrip() {
let p = Planet::builder()
.mass(Kilograms::new(1.0))
.radius(Kilometers::new(1.0))
.orbit(Orbit::new(
AstronomicalUnits::new(1.0),
0.0,
Degrees::new(0.0),
Degrees::new(0.0),
Degrees::new(0.0),
Degrees::new(0.0),
JulianDate::J2000,
))
.build();
assert_eq!(p.mass, 1.0);
}
}