dwarfplanetsfactory 0.0.2

Dwarf planet factory — classify, build and catalogue dwarf planets of any type: Kuiper belt, scattered disk, plutino, cold classical, detached, binary, Ceres-type, and sednoid.
Documentation
use crate::config::parameters;

/// Plutino: KBO in a 2:3 mean-motion resonance with Neptune.
/// Pluto is the archetype. Semi-major axis ~39.4 AU.
#[derive(Debug)]
pub struct Plutino {
    pub radius: f64,
    pub density: f64,
    pub albedo: f64,
    pub semi_major_axis: f64,
    pub eccentricity: f64,
    pub inclination: f64,
    pub rotation_period: f64,
    pub has_atmosphere: bool,
    pub resonance_libration_amplitude: f64,
}

impl Plutino {
    pub fn new(radius: f64, eccentricity: f64) -> Self {
        Self {
            radius: radius.clamp(parameters::MIN_DWARF_RADIUS, parameters::MAX_DWARF_RADIUS),
            density: 1900.0,
            albedo: 0.15,
            semi_major_axis: 39.4,
            eccentricity: eccentricity.clamp(0.0, 0.35),
            inclination: 17.0,
            rotation_period: 24.0 * 3600.0,
            has_atmosphere: false,
            resonance_libration_amplitude: 80.0,
        }
    }

    pub fn with_density(mut self, density: f64) -> Self {
        self.density = density.clamp(500.0, 3500.0);
        self
    }

    pub fn with_albedo(mut self, albedo: f64) -> Self {
        self.albedo = albedo.clamp(0.01, 0.99);
        self
    }

    pub fn with_inclination(mut self, inc: f64) -> Self {
        self.inclination = inc.clamp(0.0, 40.0);
        self
    }

    pub fn with_rotation_period(mut self, period_s: f64) -> Self {
        self.rotation_period = period_s.clamp(3600.0, 1.0e7);
        self
    }

    pub fn with_atmosphere(mut self, atm: bool) -> Self {
        self.has_atmosphere = atm;
        self
    }

    pub fn with_libration_amplitude(mut self, amp_deg: f64) -> Self {
        self.resonance_libration_amplitude = amp_deg.clamp(10.0, 180.0);
        self
    }

    pub fn mass(&self) -> f64 {
        parameters::sphere_mass(self.radius, self.density)
    }

    pub fn perihelion(&self) -> f64 {
        parameters::perihelion_au(self.semi_major_axis, self.eccentricity)
    }

    pub fn aphelion(&self) -> f64 {
        parameters::aphelion_au(self.semi_major_axis, self.eccentricity)
    }

    pub fn orbital_period_years(&self) -> f64 {
        parameters::orbital_period(self.semi_major_axis, parameters::SOLAR_MASS) / parameters::YEAR
    }

    /// Neptune orbital period (yr) for comparison: resonance ratio = P_plutino / P_neptune ≈ 1.5
    pub fn resonance_ratio(&self) -> f64 {
        let p_neptune = parameters::orbital_period(parameters::NEPTUNE_A, parameters::SOLAR_MASS)
            / parameters::YEAR;
        self.orbital_period_years() / p_neptune
    }

    /// Whether orbit crosses Neptune's orbit (perihelion < 30 AU)
    pub fn is_neptune_crossing(&self) -> bool {
        self.perihelion() < parameters::NEPTUNE_A
    }

    pub fn surface_gravity(&self) -> f64 {
        parameters::surface_gravity(self.mass(), self.radius)
    }

    pub fn escape_velocity(&self) -> f64 {
        parameters::escape_velocity(self.mass(), self.radius)
    }

    pub fn equilibrium_temp(&self) -> f64 {
        parameters::equilibrium_temperature(self.semi_major_axis * parameters::AU, self.albedo)
    }
}