use crate::config::parameters::*;
use crate::engine::orbits::OrbitalElements;
use crate::types::gas_giant::GasGiant;
use crate::types::ice_giant::IceGiant;
use crate::types::lava_world::LavaWorld;
use crate::types::ocean_world::OceanWorld;
use crate::types::rogue::RoguePlanet;
use crate::types::sub_neptune::SubNeptune;
use crate::types::super_earth::SuperEarth;
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 metallicity: 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,
metallicity: 0.02,
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,
metallicity: 0.02,
seed: 42,
}
}
pub fn hot_star(n_planets: usize) -> Self {
Self {
star_name: String::from("Star"),
star_mass: 2.0 * SOLAR_MASS,
star_temperature: 8500.0,
star_radius: 1.8 * SOLAR_RADIUS,
n_planets,
inner_limit_au: 0.5,
outer_limit_au: 80.0,
frost_line_au: 5.0,
metallicity: 0.01,
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),
SuperEarth(SuperEarth),
SubNeptune(SubNeptune),
IceGiant(IceGiant),
GasGiant(GasGiant),
LavaWorld(LavaWorld),
OceanWorld(OceanWorld),
Rogue(RoguePlanet),
}
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,
);
let u_mass = simple_hash(config.seed, 100 + idx as u64);
let u_type = simple_hash(config.seed, 400 + idx as u64);
let t_eq = equilibrium_temperature(config.star_temperature, config.star_radius, a_au * AU);
let planet = classify_and_build(config, &name, a_au, t_eq, u_mass, u_type, elements);
planets.push(planet);
}
planets
}
fn classify_and_build(
config: &SystemGeneratorConfig,
name: &str,
a_au: f64,
t_eq: f64,
u_mass: f64,
u_type: f64,
elements: OrbitalElements,
) -> GeneratedPlanet {
let frost = config.frost_line_au;
let parent = &config.star_name;
if t_eq > 1500.0 && a_au < 0.1 {
let mass = EARTH_MASS * (0.5 + 4.5 * u_mass);
let radius = radius_from_mass(mass);
return GeneratedPlanet::LavaWorld(
LavaWorld::new(name, parent, mass, radius, 1e-3, 0.0, elements)
.with_surface(0.10, t_eq, 200.0e3),
);
}
if a_au < frost * 0.3 {
let mass_e = TERRESTRIAL_MAX_MASS + (SUPER_EARTH_MAX_MASS - TERRESTRIAL_MAX_MASS) * u_mass;
let mass = mass_e * EARTH_MASS;
let radius = radius_from_mass(mass);
if u_type < 0.4 {
return GeneratedPlanet::SuperEarth(
SuperEarth::new(
name,
parent,
mass,
radius,
1e-3,
inc_from_u(u_type),
elements,
)
.with_composition(
0.3,
0.30,
0.01 + 0.05 * config.metallicity / 0.02,
),
);
}
let tm = EARTH_MASS * (0.1 + 1.9 * u_mass);
return GeneratedPlanet::Terrestrial(
TerrestrialPlanet::new(
name,
parent,
tm,
radius_from_mass(tm),
1e-3,
inc_from_u(u_type),
elements,
)
.with_surface(0.2 + 0.3 * u_type, 0.25 + 0.20 * u_mass),
);
}
if a_au < frost {
let mass_e = 1.0 + (SUPER_EARTH_MAX_MASS - 1.0) * u_mass;
let mass = mass_e * EARTH_MASS;
let radius = radius_from_mass(mass);
if u_type < 0.3 && mass_e > 2.0 {
let water_frac = 0.05 + 0.15 * u_type / 0.3;
return GeneratedPlanet::OceanWorld(
OceanWorld::new(
name,
parent,
mass,
radius,
1e-3,
inc_from_u(u_type),
elements,
)
.with_hydrosphere(0.35, water_frac, 0.0),
);
}
if mass_e > TERRESTRIAL_MAX_MASS {
return GeneratedPlanet::SuperEarth(SuperEarth::new(
name,
parent,
mass,
radius,
1e-3,
inc_from_u(u_type),
elements,
));
}
return GeneratedPlanet::Terrestrial(TerrestrialPlanet::new(
name,
parent,
mass,
radius,
1e-3,
inc_from_u(u_type),
elements,
));
}
if a_au < frost * 2.0 {
let mass_e = SUPER_EARTH_MAX_MASS + (MINI_NEPTUNE_MAX_MASS - SUPER_EARTH_MAX_MASS) * u_mass;
let mass = mass_e * EARTH_MASS;
let radius = radius_from_mass(mass);
if mass_e < MINI_NEPTUNE_MAX_MASS {
return GeneratedPlanet::SubNeptune(
SubNeptune::new(
name,
parent,
mass,
radius,
2e-3,
inc_from_u(u_type),
elements,
)
.with_envelope(0.30, 0.02 + 0.08 * u_type, 0.50),
);
}
}
if a_au < frost * 5.0 {
let mass_j = 0.1 + 5.0 * u_mass;
let mass = mass_j * JUPITER_MASS;
let radius = radius_from_mass(mass);
return GeneratedPlanet::GasGiant(GasGiant::new(
name,
parent,
mass,
radius,
1.5e-2,
3.0 * DEG,
elements,
));
}
let mass_e = ICE_GIANT_MAX_MASS * (0.3 + 0.7 * u_mass);
let mass = mass_e * EARTH_MASS;
let radius = radius_from_mass(mass);
GeneratedPlanet::IceGiant(IceGiant::new(
name,
parent,
mass,
radius,
3.3e-3,
30.0 * DEG,
elements,
))
}
fn inc_from_u(u: f64) -> f64 {
u * 30.0 * DEG
}
pub fn generate_rogue_planets(n: usize, seed: u64) -> Vec<GeneratedPlanet> {
let mut rogues = Vec::with_capacity(n);
for i in 0..n {
let u = simple_hash(seed, 500 + i as u64);
let mass = EARTH_MASS * (0.01 + 20.0 * u * u);
let radius = radius_from_mass(mass);
let age = 1e9 * YEAR * (1.0 + 9.0 * simple_hash(seed, 600 + i as u64));
let vel = 10.0e3 + 50.0e3 * simple_hash(seed, 700 + i as u64);
let name = format!("Rogue-{}", i + 1);
rogues.push(GeneratedPlanet::Rogue(RoguePlanet::with_details(
&name, mass, radius, age, 0.1, 0.30, vel,
)));
}
rogues
}