use crate::config::parameters::*;
use crate::engine::orbits::OrbitalElements;
use crate::types::captured::{CapturedComposition, CapturedSatellite};
use crate::types::co_orbital_moon::{CoOrbitalMoon, CoOrbitalType};
use crate::types::exomoon::Exomoon;
use crate::types::icy_moon::IcyMoon;
use crate::types::regular::{RegularSatellite, SatelliteClass};
use crate::types::ring_moon::{RingAssociation, RingMoon};
use crate::types::subsurface_ocean_moon::SubsurfaceOceanMoon;
use crate::types::volcanic_moon::VolcanicMoon;
pub struct SystemGeneratorConfig {
pub parent_name: String,
pub parent_mass: f64,
pub parent_radius: f64,
pub n_regular: usize,
pub n_captured: usize,
pub n_ring_moons: usize,
pub n_volcanic: usize,
pub n_icy: usize,
pub n_ocean: usize,
pub n_exomoon: usize,
pub n_co_orbital: usize,
pub inner_edge: f64,
pub outer_edge: f64,
pub seed: u64,
}
impl SystemGeneratorConfig {
pub fn new(
parent_name: &str,
parent_mass: f64,
parent_radius: f64,
inner_edge: f64,
outer_edge: f64,
) -> Self {
Self {
parent_name: parent_name.to_string(),
parent_mass,
parent_radius,
n_regular: 0,
n_captured: 0,
n_ring_moons: 0,
n_volcanic: 0,
n_icy: 0,
n_ocean: 0,
n_exomoon: 0,
n_co_orbital: 0,
inner_edge,
outer_edge,
seed: 42,
}
}
pub fn with_counts(
mut self,
n_regular: usize,
n_captured: usize,
n_ring_moons: usize,
n_volcanic: usize,
n_icy: usize,
) -> Self {
self.n_regular = n_regular;
self.n_captured = n_captured;
self.n_ring_moons = n_ring_moons;
self.n_volcanic = n_volcanic;
self.n_icy = n_icy;
self
}
pub fn with_exotic(mut self, n_ocean: usize, n_exomoon: usize, n_co_orbital: usize) -> Self {
self.n_ocean = n_ocean;
self.n_exomoon = n_exomoon;
self.n_co_orbital = n_co_orbital;
self
}
pub fn with_seed(mut self, seed: u64) -> Self {
self.seed = seed;
self
}
}
#[derive(Debug, Clone)]
pub enum GeneratedSatellite {
Regular(RegularSatellite),
Captured(CapturedSatellite),
Ring(RingMoon),
Volcanic(VolcanicMoon),
Icy(IcyMoon),
SubsurfaceOcean(SubsurfaceOceanMoon),
Exo(Exomoon),
CoOrbital(CoOrbitalMoon),
}
fn simple_hash(seed: u64, index: u64) -> f64 {
let mut x = seed
.wrapping_mul(6364136223846793005)
.wrapping_add(index.wrapping_mul(1442695040888963407));
x ^= x >> 33;
x = x.wrapping_mul(0xff51afd7ed558ccd);
x ^= x >> 33;
(x as f64) / (u64::MAX as f64)
}
pub fn generate_system(config: &SystemGeneratorConfig) -> Vec<GeneratedSatellite> {
let mut satellites = Vec::new();
let range = config.outer_edge - config.inner_edge;
for idx in 0..config.n_regular {
let t = simple_hash(config.seed, idx as u64);
let a = config.inner_edge + range * (0.2 + 0.6 * t);
let mass =
config.parent_mass * 1e-4 * (0.01 + 0.99 * simple_hash(config.seed + 1, idx as u64));
let density = 1500.0 + 2000.0 * simple_hash(config.seed + 2, idx as u64);
let radius = (3.0 * mass / (4.0 * std::f64::consts::PI * density)).cbrt();
let e = 0.001 + 0.01 * simple_hash(config.seed + 3, idx as u64);
let inc = 0.5 * simple_hash(config.seed + 4, idx as u64);
let ice_frac = 0.3 * simple_hash(config.seed + 5, idx as u64);
let rock_frac = 0.5 + 0.3 * simple_hash(config.seed + 6, idx as u64);
satellites.push(GeneratedSatellite::Regular(RegularSatellite {
name: format!("{}-R{}", config.parent_name, idx + 1),
parent: config.parent_name.clone(),
mass,
radius,
j2: 0.0,
obliquity: 0.0,
elements: OrbitalElements::new(a, e, inc, 0.0, 0.0, 0.0),
class: SatelliteClass::Regular,
albedo: 0.2 + 0.6 * simple_hash(config.seed + 7, idx as u64),
ice_fraction: ice_frac,
rock_fraction: rock_frac.min(1.0 - ice_frac),
}));
}
for idx in 0..config.n_captured {
let t = simple_hash(config.seed + 100, idx as u64);
let a = config.outer_edge * (1.0 + 2.0 * t);
let mass = 1e15 + 1e18 * simple_hash(config.seed + 101, idx as u64);
let density = 1200.0 + 1500.0 * simple_hash(config.seed + 102, idx as u64);
let radius = (3.0 * mass / (4.0 * std::f64::consts::PI * density)).cbrt();
let e = 0.1 + 0.4 * simple_hash(config.seed + 103, idx as u64);
let inc = std::f64::consts::PI * simple_hash(config.seed + 104, idx as u64);
let retrograde = simple_hash(config.seed + 105, idx as u64) > 0.5;
satellites.push(GeneratedSatellite::Captured(CapturedSatellite {
name: format!("{}-C{}", config.parent_name, idx + 1),
parent: config.parent_name.clone(),
mass,
radius,
j2: 0.0,
obliquity: 0.0,
elements: OrbitalElements::new(a, e, inc, 0.0, 0.0, 0.0),
albedo: 0.04 + 0.08 * simple_hash(config.seed + 106, idx as u64),
retrograde,
capture_velocity_excess: 100.0 + 400.0 * simple_hash(config.seed + 107, idx as u64),
composition: if simple_hash(config.seed + 108, idx as u64) > 0.5 {
CapturedComposition::Carbonaceous
} else {
CapturedComposition::Silicate
},
}));
}
for idx in 0..config.n_ring_moons {
let t = simple_hash(config.seed + 200, idx as u64);
let a = config.inner_edge * (0.8 + 0.4 * t);
let mass = 1e14 + 1e17 * simple_hash(config.seed + 201, idx as u64);
let density = 400.0 + 600.0 * simple_hash(config.seed + 202, idx as u64);
let radius = (3.0 * mass / (4.0 * std::f64::consts::PI * density)).cbrt();
satellites.push(GeneratedSatellite::Ring(RingMoon {
name: format!("{}-S{}", config.parent_name, idx + 1),
parent: config.parent_name.clone(),
mass,
radius,
elements: OrbitalElements::new(a, 0.001, 0.001, 0.0, 0.0, 0.0),
albedo: 0.5 + 0.4 * simple_hash(config.seed + 203, idx as u64),
ring_association: RingAssociation::Shepherd,
cryovolcanic: false,
ice_fraction: 0.7 + 0.2 * simple_hash(config.seed + 204, idx as u64),
}));
}
for idx in 0..config.n_volcanic {
let t = simple_hash(config.seed + 300, idx as u64);
let a = config.inner_edge + range * (0.1 + 0.3 * t);
let mass =
config.parent_mass * 1e-4 * (0.1 + 0.9 * simple_hash(config.seed + 301, idx as u64));
let density = 2500.0 + 1500.0 * simple_hash(config.seed + 302, idx as u64);
let radius = (3.0 * mass / (4.0 * std::f64::consts::PI * density)).cbrt();
let e = 0.003 + 0.01 * simple_hash(config.seed + 303, idx as u64);
let inc = 0.1 * simple_hash(config.seed + 304, idx as u64);
satellites.push(GeneratedSatellite::Volcanic(VolcanicMoon {
name: format!("{}-V{}", config.parent_name, idx + 1),
parent: config.parent_name.clone(),
mass,
radius,
j2: 0.0,
obliquity: 0.0,
elements: OrbitalElements::new(a, e, inc, 0.0, 0.0, 0.0),
albedo: 0.3 + 0.5 * simple_hash(config.seed + 305, idx as u64),
silicate_fraction: 0.85 + 0.1 * simple_hash(config.seed + 306, idx as u64),
tidal_q: 20.0 + 180.0 * simple_hash(config.seed + 307, idx as u64),
eruption_rate: 1e3 + 1e5 * simple_hash(config.seed + 308, idx as u64),
}));
}
for idx in 0..config.n_icy {
let t = simple_hash(config.seed + 400, idx as u64);
let a = config.inner_edge + range * (0.4 + 0.5 * t);
let mass =
config.parent_mass * 1e-5 * (0.1 + 0.9 * simple_hash(config.seed + 401, idx as u64));
let density = 1000.0 + 1500.0 * simple_hash(config.seed + 402, idx as u64);
let radius = (3.0 * mass / (4.0 * std::f64::consts::PI * density)).cbrt();
let e = 0.001 + 0.01 * simple_hash(config.seed + 403, idx as u64);
let inc = 0.2 * simple_hash(config.seed + 404, idx as u64);
let ice = 0.3 + 0.5 * simple_hash(config.seed + 405, idx as u64);
let rock = (0.6 * (1.0 - ice)).max(0.1);
satellites.push(GeneratedSatellite::Icy(IcyMoon {
name: format!("{}-I{}", config.parent_name, idx + 1),
parent: config.parent_name.clone(),
mass,
radius,
j2: 0.0,
obliquity: 0.0,
elements: OrbitalElements::new(a, e, inc, 0.0, 0.0, 0.0),
albedo: 0.4 + 0.5 * simple_hash(config.seed + 406, idx as u64),
ice_fraction: ice,
rock_fraction: rock,
surface_temperature: 50.0 + 100.0 * simple_hash(config.seed + 407, idx as u64),
}));
}
for idx in 0..config.n_ocean {
let t = simple_hash(config.seed + 500, idx as u64);
let a = config.inner_edge + range * (0.3 + 0.4 * t);
let mass =
config.parent_mass * 1e-5 * (0.5 + 0.5 * simple_hash(config.seed + 501, idx as u64));
let density = 1800.0 + 1200.0 * simple_hash(config.seed + 502, idx as u64);
let radius = (3.0 * mass / (4.0 * std::f64::consts::PI * density)).cbrt();
let e = 0.002 + 0.01 * simple_hash(config.seed + 503, idx as u64);
let inc = 0.1 * simple_hash(config.seed + 504, idx as u64);
satellites.push(GeneratedSatellite::SubsurfaceOcean(SubsurfaceOceanMoon {
name: format!("{}-O{}", config.parent_name, idx + 1),
parent: config.parent_name.clone(),
mass,
radius,
j2: 0.0,
obliquity: 0.0,
elements: OrbitalElements::new(a, e, inc, 0.0, 0.0, 0.0),
albedo: 0.5 + 0.4 * simple_hash(config.seed + 505, idx as u64),
ice_shell_thickness: 5.0e3 + 50.0e3 * simple_hash(config.seed + 506, idx as u64),
ocean_salinity: 10.0 + 80.0 * simple_hash(config.seed + 507, idx as u64),
tidal_q: 20.0 + 100.0 * simple_hash(config.seed + 508, idx as u64),
}));
}
for idx in 0..config.n_exomoon {
let t = simple_hash(config.seed + 600, idx as u64);
let a = config.inner_edge + range * (0.2 + 0.6 * t);
let mass =
config.parent_mass * 1e-4 * (0.01 + 0.99 * simple_hash(config.seed + 601, idx as u64));
let density = 1500.0 + 3000.0 * simple_hash(config.seed + 602, idx as u64);
let radius = (3.0 * mass / (4.0 * std::f64::consts::PI * density)).cbrt();
let e = 0.001 + 0.05 * simple_hash(config.seed + 603, idx as u64);
let inc = 0.5 * simple_hash(config.seed + 604, idx as u64);
satellites.push(GeneratedSatellite::Exo(Exomoon {
name: format!("{}-X{}", config.parent_name, idx + 1),
parent: config.parent_name.clone(),
mass,
radius,
j2: 0.0,
obliquity: 0.0,
elements: OrbitalElements::new(a, e, inc, 0.0, 0.0, 0.0),
albedo: 0.1 + 0.7 * simple_hash(config.seed + 605, idx as u64),
parent_mass: config.parent_mass,
star_mass: M_SUN,
star_planet_distance: AU,
}));
}
for idx in 0..config.n_co_orbital {
let t = simple_hash(config.seed + 700, idx as u64);
let a = config.inner_edge + range * (0.3 + 0.2 * t);
let mass = 1e15 + 1e18 * simple_hash(config.seed + 701, idx as u64);
let density = 500.0 + 800.0 * simple_hash(config.seed + 702, idx as u64);
let radius = (3.0 * mass / (4.0 * std::f64::consts::PI * density)).cbrt();
let co_type = if simple_hash(config.seed + 703, idx as u64) > 0.5 {
CoOrbitalType::Trojan
} else {
CoOrbitalType::Horseshoe
};
satellites.push(GeneratedSatellite::CoOrbital(CoOrbitalMoon {
name: format!("{}-T{}", config.parent_name, idx + 1),
parent: config.parent_name.clone(),
mass,
radius,
elements: OrbitalElements::new(a, 0.007, 0.001, 0.0, 0.0, 0.0),
albedo: 0.3 + 0.5 * simple_hash(config.seed + 704, idx as u64),
co_type,
partner_mass: 1e15 + 1e18 * simple_hash(config.seed + 705, idx as u64),
libration_amplitude: 0.1 + 0.5 * simple_hash(config.seed + 706, idx as u64),
}));
}
satellites
}