use core::marker::PhantomData;
use glam::DVec3;
use crate::{Simulation, VehicleOutput};
use astrodyn::{
GravitySourceEntry, MassProperties, SimulationTime, ValidationError, VehicleConfig,
};
pub(crate) type Brand<'sim> = PhantomData<fn(&'sim ()) -> &'sim ()>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SourceIdx<'sim> {
raw: usize,
_brand: Brand<'sim>,
}
impl SourceIdx<'_> {
#[inline]
pub fn into_raw(self) -> usize {
self.raw
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BodyIdx<'sim> {
raw: usize,
_brand: Brand<'sim>,
}
impl BodyIdx<'_> {
#[inline]
pub fn into_raw(self) -> usize {
self.raw
}
}
pub struct BrandedSimulation<'sim> {
inner: Simulation,
_brand: Brand<'sim>,
}
impl<'sim> BrandedSimulation<'sim> {
pub(crate) fn from_inner(inner: Simulation) -> Self {
Self {
inner,
_brand: PhantomData,
}
}
pub fn add_source(
&mut self,
name: impl Into<String>,
entry: GravitySourceEntry,
) -> SourceIdx<'sim> {
SourceIdx {
raw: self.inner.add_source(name, entry),
_brand: PhantomData,
}
}
pub fn add_body(&mut self, config: VehicleConfig) -> BodyIdx<'sim> {
BodyIdx {
raw: self.inner.add_body(config),
_brand: PhantomData,
}
}
pub fn set_source_position(&mut self, idx: SourceIdx<'sim>, position: DVec3) {
self.inner.set_source_position(idx.raw, position);
}
pub fn set_source_state(&mut self, idx: SourceIdx<'sim>, position: DVec3, velocity: DVec3) {
self.inner.set_source_state(idx.raw, position, velocity);
}
pub fn source_position(&self, idx: SourceIdx<'sim>) -> DVec3 {
self.inner.source_position(idx.raw)
}
pub fn body(&self, idx: BodyIdx<'sim>) -> VehicleOutput {
self.inner.body(idx.raw)
}
pub fn set_body_external_force(&mut self, idx: BodyIdx<'sim>, force: DVec3) {
self.inner.set_body_external_force(idx.raw, force);
}
pub fn set_body_external_torque(&mut self, idx: BodyIdx<'sim>, torque: DVec3) {
self.inner.set_body_external_torque(idx.raw, torque);
}
pub fn set_body_position(&mut self, idx: BodyIdx<'sim>, position: DVec3) {
self.inner.set_body_position(idx.raw, position);
}
pub fn set_body_velocity(&mut self, idx: BodyIdx<'sim>, velocity: DVec3) {
self.inner.set_body_velocity(idx.raw, velocity);
}
pub fn set_body_mass(&mut self, idx: BodyIdx<'sim>, mass: MassProperties) {
self.inner.set_body_mass(idx.raw, mass);
}
pub fn step(&mut self) -> Result<(), crate::StepError> {
self.inner.step()
}
pub fn step_until(&mut self, target_time: f64) -> Result<(), crate::StepError> {
self.inner.step_until(target_time)
}
pub fn validate(&mut self) -> Result<(), Vec<ValidationError>> {
self.inner.validate()
}
pub fn num_sources(&self) -> usize {
self.inner.num_sources()
}
pub fn time(&self) -> &SimulationTime {
&self.inner.time
}
pub fn unbranded(&self) -> &Simulation {
&self.inner
}
pub fn unbranded_mut(&mut self) -> &mut Simulation {
&mut self.inner
}
pub fn into_inner(self) -> Simulation {
self.inner
}
}
impl Simulation {
pub fn run<F, R>(time: SimulationTime, dt: f64, f: F) -> R
where
F: for<'sim> FnOnce(BrandedSimulation<'sim>) -> R,
{
f(BrandedSimulation::from_inner(Simulation::new(time, dt)))
}
}
#[allow(dead_code)]
fn _doc_compile_fail_marker() {}
#[cfg(test)]
mod tests {
use super::*;
use astrodyn::default_leap_second_table;
#[test]
fn run_round_trip() {
let n = Simulation::run(
SimulationTime::at_j2000(default_leap_second_table()),
10.0,
|sim| sim.num_sources(),
);
assert_eq!(n, 0);
}
#[test]
fn into_raw_strips_brand() {
Simulation::run(
SimulationTime::at_j2000(default_leap_second_table()),
10.0,
|mut sim| {
let entry = astrodyn::GravitySourceEntry {
source: astrodyn::GravitySource {
mu: 3.986e14,
model: astrodyn::GravityModel::PointMass,
},
position: astrodyn::Position::<astrodyn::RootInertial>::zero(),
velocity: astrodyn::Velocity::<astrodyn::RootInertial>::zero(),
t_inertial_pfix: None,
delta_c20: 0.0,
rotation_model: astrodyn::RotationModel::default(),
tidal_config: None,
planet_omega: 0.0,
central: true,
marker_only: false,
};
let idx = sim.add_source("Earth", entry);
assert_eq!(idx.into_raw(), 0);
assert_eq!(sim.num_sources(), 1);
},
);
}
#[test]
fn brand_types_present() {
fn _check<'a, 'b>(_: SourceIdx<'a>, _: BodyIdx<'b>) {}
}
}