solarsystems 0.0.1

N-body solar system engine — gravitational dynamics, orbital mechanics, perturbations, event detection, and full celestial orchestration
Documentation
use crate::{BodyId, CelestialBody, Vec3};
use sciforge::hub::domain::common::constants::G;

#[derive(Clone, Debug)]
pub struct State3D {
    pub id: BodyId,
    pub position: Vec3,
}

#[derive(Clone, Debug)]
pub struct State5D {
    pub id: BodyId,
    pub position: Vec3,
    pub speed: f64,
    pub mass: f64,
}

#[derive(Clone, Debug)]
pub struct State6D {
    pub id: BodyId,
    pub position: Vec3,
    pub velocity: Vec3,
}

#[derive(Clone, Debug)]
pub struct State7D {
    pub id: BodyId,
    pub position: Vec3,
    pub velocity: Vec3,
    pub mass: f64,
}

#[derive(Clone, Debug)]
pub struct State8D {
    pub id: BodyId,
    pub position: Vec3,
    pub velocity: Vec3,
    pub mass: f64,
    pub radius: f64,
}

#[derive(Clone, Debug)]
pub struct FullState {
    pub id: BodyId,
    pub name: &'static str,
    pub position: Vec3,
    pub velocity: Vec3,
    pub acceleration: Vec3,
    pub mass: f64,
    pub radius: f64,
    pub gravitational_parameter: f64,
    pub kinetic_energy: f64,
    pub speed: f64,
    pub distance_from_origin: f64,
}

pub fn extract_3d(bodies: &[CelestialBody]) -> Vec<State3D> {
    bodies
        .iter()
        .map(|b| State3D {
            id: b.id,
            position: b.position,
        })
        .collect()
}

pub fn extract_5d(bodies: &[CelestialBody]) -> Vec<State5D> {
    bodies
        .iter()
        .map(|b| State5D {
            id: b.id,
            position: b.position,
            speed: b.velocity.magnitude(),
            mass: b.mass,
        })
        .collect()
}

pub fn extract_6d(bodies: &[CelestialBody]) -> Vec<State6D> {
    bodies
        .iter()
        .map(|b| State6D {
            id: b.id,
            position: b.position,
            velocity: b.velocity,
        })
        .collect()
}

pub fn extract_7d(bodies: &[CelestialBody]) -> Vec<State7D> {
    bodies
        .iter()
        .map(|b| State7D {
            id: b.id,
            position: b.position,
            velocity: b.velocity,
            mass: b.mass,
        })
        .collect()
}

pub fn extract_8d(bodies: &[CelestialBody]) -> Vec<State8D> {
    bodies
        .iter()
        .map(|b| State8D {
            id: b.id,
            position: b.position,
            velocity: b.velocity,
            mass: b.mass,
            radius: b.radius,
        })
        .collect()
}

pub fn extract_full(bodies: &[CelestialBody]) -> Vec<FullState> {
    bodies
        .iter()
        .map(|b| FullState {
            id: b.id,
            name: b.name,
            position: b.position,
            velocity: b.velocity,
            acceleration: b.acceleration,
            mass: b.mass,
            radius: b.radius,
            gravitational_parameter: G * b.mass,
            kinetic_energy: b.kinetic_energy(),
            speed: b.velocity.magnitude(),
            distance_from_origin: b.position.magnitude(),
        })
        .collect()
}

pub fn extract_positions_flat(bodies: &[CelestialBody]) -> Vec<f64> {
    let mut out = Vec::with_capacity(bodies.len() * 3);
    for b in bodies {
        out.push(b.position.x);
        out.push(b.position.y);
        out.push(b.position.z);
    }
    out
}

pub fn extract_state_vectors_flat(bodies: &[CelestialBody]) -> Vec<f64> {
    let mut out = Vec::with_capacity(bodies.len() * 6);
    for b in bodies {
        out.push(b.position.x);
        out.push(b.position.y);
        out.push(b.position.z);
        out.push(b.velocity.x);
        out.push(b.velocity.y);
        out.push(b.velocity.z);
    }
    out
}

pub fn extract_full_flat(bodies: &[CelestialBody]) -> Vec<f64> {
    let mut out = Vec::with_capacity(bodies.len() * 8);
    for b in bodies {
        out.push(b.position.x);
        out.push(b.position.y);
        out.push(b.position.z);
        out.push(b.velocity.x);
        out.push(b.velocity.y);
        out.push(b.velocity.z);
        out.push(b.mass);
        out.push(b.radius);
    }
    out
}

pub fn extract_by_id(bodies: &[CelestialBody], id: BodyId) -> Option<FullState> {
    bodies.iter().find(|b| b.id == id).map(|b| FullState {
        id: b.id,
        name: b.name,
        position: b.position,
        velocity: b.velocity,
        acceleration: b.acceleration,
        mass: b.mass,
        radius: b.radius,
        gravitational_parameter: G * b.mass,
        kinetic_energy: b.kinetic_energy(),
        speed: b.velocity.magnitude(),
        distance_from_origin: b.position.magnitude(),
    })
}

pub fn extract_trajectory(
    snapshots: &[(f64, Vec<CelestialBody>)],
    id: BodyId,
) -> Vec<(f64, Vec3, Vec3)> {
    snapshots
        .iter()
        .filter_map(|(t, bodies)| {
            bodies
                .iter()
                .find(|b| b.id == id)
                .map(|b| (*t, b.position, b.velocity))
        })
        .collect()
}

pub fn extract_pairwise_distances(bodies: &[CelestialBody]) -> Vec<(BodyId, BodyId, f64)> {
    let mut pairs = Vec::new();
    for i in 0..bodies.len() {
        for j in (i + 1)..bodies.len() {
            pairs.push((
                bodies[i].id,
                bodies[j].id,
                bodies[i].position.distance(&bodies[j].position),
            ));
        }
    }
    pairs
}

pub fn relative_state(
    bodies: &[CelestialBody],
    target: BodyId,
    reference: BodyId,
) -> Option<State6D> {
    let t = bodies.iter().find(|b| b.id == target)?;
    let r = bodies.iter().find(|b| b.id == reference)?;
    Some(State6D {
        id: target,
        position: t.position - r.position,
        velocity: t.velocity - r.velocity,
    })
}