use astrodyn::{IntegOrigin, IntegrationFrame, Planet, RootInertial};
use bevy::prelude::*;
use glam::DVec3;
use crate::components::*;
use crate::frame_param::FrameOrigin;
use crate::{AtmosphereModelR, SimulationTimeR};
use super::util::body_integ_origin_in_root;
#[allow(clippy::type_complexity)]
pub fn gravity_computation_system<P: Planet>(
frame_origin: FrameOrigin,
root_frame_entity: Res<crate::RootFrameEntityR>,
parents: Query<&ChildOf>,
mut bodies: Query<
(
Entity,
&TranslationalStateC<P>,
&GravityControlsC,
&mut GravityAccelerationC,
Option<&FrameEntityC>,
),
Without<crate::DetachedSubtreeStateC>,
>,
sources: Query<(
&GravitySourceC,
Option<&PlanetFixedRotationC<P>>,
&SourceInertialPositionC,
Option<&SourceInertialVelocityC>,
Option<&TidalDeltaC20C>,
Option<&TidalConfigC>,
)>,
) {
let body_iter = bodies
.iter_mut()
.map(|(entity, state, controls, accel, body_frame)| {
let (integ_pos, integ_vel) =
body_integ_origin_in_root(body_frame, &parents, root_frame_entity.0, &frame_origin);
let inputs = astrodyn::GravityBodyInputs {
position: state.position.relabel_to::<IntegrationFrame>(),
velocity: state.velocity.relabel_to::<IntegrationFrame>(),
integ_origin: IntegOrigin {
position: integ_pos,
velocity: integ_vel,
},
controls: &controls.0,
};
let mut accel = accel;
let store = move |result: astrodyn::GravityAccelerationTyped<RootInertial>| {
accel.0 = result;
};
(entity, inputs, store)
});
astrodyn::run_gravity_stage(
body_iter,
|entity, source_entity| match sources.get(source_entity) {
Ok((source, rot, pos, _, tidal, tidal_config)) => Some(astrodyn::ResolvedSource {
source: &source.0,
rotation: rot.map(|r| r.0.matrix_ref()),
position: pos.0.raw_si(),
delta_c20: tidal.map_or(0.0, |t| t.0.value),
has_delta_coeffs: tidal_config.is_some(),
}),
Err(_) => {
panic!(
"Entity {entity:?}: GravityControl references source \
{source_entity:?} which does not exist or lacks \
GravitySourceC + SourceInertialPositionC."
);
}
},
|_entity, source_entity| {
sources.get(source_entity).ok().map(|(s, _, p, v, _, _)| {
let velocity = v.map(|v| v.0.raw_si()).unwrap_or(DVec3::ZERO);
astrodyn::ResolvedRelativisticSource {
mu: s.mu,
position: p.0.raw_si(),
velocity,
}
})
},
);
}
pub fn atmosphere_update_system<P: Planet>(
atmos_model: Option<Res<AtmosphereModelR>>,
sim_time: Option<Res<SimulationTimeR>>,
planet_query: Query<&PlanetFixedRotationC<P>>,
mut query: Query<(&TranslationalStateC<P>, &mut AtmosphericStateC<P>)>,
) {
let Some(model) = atmos_model else {
return;
};
let entity = model.planet_entity;
let Ok(r) = planet_query.get(entity) else {
panic!(
"AtmosphereModelR.planet_entity ({entity:?}) has no PlanetFixedRotationC. \
In JEOD, the planet-fixed frame is always available for atmosphere \
computation. Add PlanetFixedRotationC to the planet entity (use an \
identity FrameTransform when no real rotation is desired — the geodetic \
conversion is bit-identical to the previous `planet_entity = None` \
spherical fallback)."
);
};
let t_inertial_pfix = *r.0.matrix_ref();
let tai_tjt = sim_time.as_ref().map(|t| t.tai_tjt);
if tai_tjt.is_none() {
if let astrodyn::AtmosphereModel::Met(_) = &model.config.model {
if !query.is_empty() {
panic!(
"MET atmosphere requires SimulationTimeR resource for TJT. \
Ensure AstrodynPlugin is added (it provides SimulationTimeR)."
);
}
}
}
let body_iter = query.iter_mut().map(|(state, atmos)| {
let inputs = astrodyn::AtmosphereBodyInputs {
position: state.position,
};
let mut atmos = atmos;
let store = move |result: astrodyn::AtmosphereState<P>| {
**atmos = result;
};
((), inputs, store)
});
astrodyn::run_atmosphere_stage::<P, _, _, _>(
body_iter,
&model.config,
Some(&t_inertial_pfix),
tai_tjt,
);
}