planetsfactory 0.0.1

Planet factory — classify, build and catalogue planets for any star system: Solar System, TRAPPIST-1, Kepler-90, Proxima Centauri, or fully custom worlds.
Documentation
use crate::config::parameters::*;
use crate::engine::orbits::OrbitalElements;
use crate::types::gas_giant::GasGiant;
use crate::types::ice_giant::IceGiant;
use crate::types::terrestrial::TerrestrialPlanet;

#[derive(Debug, Clone)]
pub struct SystemGeneratorConfig {
    pub star_name: String,
    pub star_mass: f64,
    pub star_temperature: f64,
    pub star_radius: f64,
    pub n_planets: usize,
    pub inner_limit_au: f64,
    pub outer_limit_au: f64,
    pub frost_line_au: f64,
    pub seed: u64,
}

impl SystemGeneratorConfig {
    pub fn solar_like(n_planets: usize) -> Self {
        Self {
            star_name: String::from("Star"),
            star_mass: SOLAR_MASS,
            star_temperature: SOLAR_TEMPERATURE,
            star_radius: SOLAR_RADIUS,
            n_planets,
            inner_limit_au: 0.3,
            outer_limit_au: 40.0,
            frost_line_au: 2.7,
            seed: 42,
        }
    }

    pub fn m_dwarf(n_planets: usize) -> Self {
        Self {
            star_name: String::from("Star"),
            star_mass: 0.089 * SOLAR_MASS,
            star_temperature: 2566.0,
            star_radius: 0.121 * SOLAR_RADIUS,
            n_planets,
            inner_limit_au: 0.01,
            outer_limit_au: 0.1,
            frost_line_au: 0.03,
            seed: 42,
        }
    }
}

pub fn titius_bode_au(n_planets: usize, a0: f64, b: f64, c: f64) -> Vec<f64> {
    (0..n_planets).map(|n| a0 + b * c.powi(n as i32)).collect()
}

pub fn titius_bode_solar(n_planets: usize) -> Vec<f64> {
    titius_bode_au(n_planets, 0.0, 0.3, 2.0)
}

fn simple_hash(seed: u64, idx: u64) -> f64 {
    let mut x = seed.wrapping_mul(6364136223846793005).wrapping_add(idx);
    x ^= x >> 33;
    x = x.wrapping_mul(0xff51afd7ed558ccd);
    x ^= x >> 33;
    (x as f64) / (u64::MAX as f64)
}

pub enum GeneratedPlanet {
    Terrestrial(TerrestrialPlanet),
    GasGiant(GasGiant),
    IceGiant(IceGiant),
}

pub fn generate_system(config: &SystemGeneratorConfig) -> Vec<GeneratedPlanet> {
    let n = config.n_planets;
    let ln_inner = config.inner_limit_au.ln();
    let ln_outer = config.outer_limit_au.ln();

    let axes_au: Vec<f64> = (0..n)
        .map(|i| {
            let frac = if n > 1 {
                i as f64 / (n - 1) as f64
            } else {
                0.5
            };
            (ln_inner + frac * (ln_outer - ln_inner)).exp()
        })
        .collect();

    let mut planets = Vec::with_capacity(n);

    for (idx, &a_au) in axes_au.iter().enumerate() {
        let name = format!("{} {}", config.star_name, (b'b' + idx as u8) as char);
        let ecc = simple_hash(config.seed, 10 + idx as u64) * 0.1;
        let inc_deg = simple_hash(config.seed, 20 + idx as u64) * 5.0;
        let omega_big_deg = (360.0 / n as f64) * idx as f64;
        let omega_small_deg = simple_hash(config.seed, 200 + idx as u64) * 360.0;
        let m0_deg = simple_hash(config.seed, 300 + idx as u64) * 360.0;

        let elements = OrbitalElements::from_au_deg(
            a_au,
            ecc,
            inc_deg,
            omega_big_deg,
            omega_small_deg,
            m0_deg,
        );

        if a_au < config.frost_line_au {
            let u = simple_hash(config.seed, 100 + idx as u64);
            let mass = EARTH_MASS * (0.1 + 4.9 * u);
            let radius = radius_from_mass(mass);
            planets.push(GeneratedPlanet::Terrestrial(TerrestrialPlanet::new(
                &name,
                &config.star_name,
                mass,
                radius,
                1.0e-3,
                inc_deg * DEG,
                elements,
            )));
        } else if a_au < config.frost_line_au * 5.0 {
            let u = simple_hash(config.seed, 100 + idx as u64);
            let mass = JUPITER_MASS * (0.1 + 5.0 * u);
            let radius = radius_from_mass(mass);
            planets.push(GeneratedPlanet::GasGiant(GasGiant::new(
                &name,
                &config.star_name,
                mass,
                radius,
                1.5e-2,
                3.0 * DEG,
                elements,
            )));
        } else {
            let u = simple_hash(config.seed, 100 + idx as u64);
            let mass = EARTH_MASS * (10.0 + 30.0 * u);
            let radius = radius_from_mass(mass);
            planets.push(GeneratedPlanet::IceGiant(IceGiant::new(
                &name,
                &config.star_name,
                mass,
                radius,
                3.3e-3,
                30.0 * DEG,
                elements,
            )));
        }
    }

    planets
}