use std::f64::consts::PI;
use asteroidsfactory::types::main_belt::{MainBeltAsteroid, SpectralType};
use crate::config::parameters::MU_SUN;
use crate::orbits::keplerian::OrbitalElements;
use crate::{BodyId, CelestialBody, Vec3};
const AU: f64 = 1.495978707e11;
struct Rng(u64);
impl Rng {
fn next_u64(&mut self) -> u64 {
self.0 ^= self.0 << 13;
self.0 ^= self.0 >> 7;
self.0 ^= self.0 << 17;
self.0
}
fn f64(&mut self) -> f64 {
(self.next_u64() >> 11) as f64 / ((1u64 << 53) as f64)
}
fn range(&mut self, lo: f64, hi: f64) -> f64 {
lo + self.f64() * (hi - lo)
}
}
fn random_spectral_type(rng: &mut Rng) -> SpectralType {
let r = rng.f64();
if r < 0.40 {
SpectralType::C
} else if r < 0.70 {
SpectralType::S
} else if r < 0.80 {
SpectralType::M
} else if r < 0.85 {
SpectralType::B
} else if r < 0.90 {
SpectralType::D
} else if r < 0.95 {
SpectralType::V
} else {
SpectralType::X
}
}
fn density_for_type(st: SpectralType, rng: &mut Rng) -> f64 {
match st {
SpectralType::C | SpectralType::B | SpectralType::D => rng.range(1200.0, 2500.0),
SpectralType::S | SpectralType::V => rng.range(2500.0, 3800.0),
SpectralType::M | SpectralType::X => rng.range(3500.0, 5500.0),
}
}
pub fn generate_main_belt(count: u16) -> Vec<CelestialBody> {
let mut rng = Rng(0xCE1E_5714_A573_B317);
let mut bodies = Vec::with_capacity(count as usize);
for idx in 0..count {
let spectral = random_spectral_type(&mut rng);
let density = density_for_type(spectral, &mut rng);
let radius = 10.0_f64.powf(rng.range(3.0, 5.5));
let a_au = loop {
let candidate = rng.range(2.1, 3.3);
let test = MainBeltAsteroid::new(radius, density, candidate);
if !test.is_in_kirkwood_gap() {
break candidate;
}
};
let ecc = rng.range(0.0, 0.3);
let inc_deg = rng.range(0.0, 20.0);
let asteroid = MainBeltAsteroid::new(radius, density, a_au)
.with_eccentricity(ecc)
.with_inclination(inc_deg)
.with_spectral_type(spectral)
.with_rotation_period(rng.range(2.0, 24.0));
let raan = rng.range(0.0, 2.0 * PI);
let arg_periapsis = rng.range(0.0, 2.0 * PI);
let true_anomaly = rng.range(0.0, 2.0 * PI);
let elements = OrbitalElements {
semi_major_axis: asteroid.semi_major_axis * AU,
eccentricity: asteroid.eccentricity,
inclination: asteroid.inclination, longitude_ascending_node: raan,
argument_periapsis: arg_periapsis,
true_anomaly,
};
let (position, velocity) = elements.to_state_vectors(MU_SUN);
bodies.push(CelestialBody {
id: BodyId::BeltAsteroid(idx),
name: "Belt Asteroid",
mass: asteroid.mass,
radius: asteroid.radius,
position,
velocity,
acceleration: Vec3::ZERO,
j2: 0.0,
});
}
bodies
}