use serde::{Deserialize, Serialize};
use snafu::ResultExt;
use std::fmt;
use crate::almanac::Almanac;
use crate::analysis::PhysicsVecExprSnafu;
use crate::math::Vector3;
use crate::prelude::Epoch;
use super::specs::{OrthogonalFrame, Plane, StateSpec, StateSpecTrait};
use super::{AnalysisError, DcmExpr};
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum VectorExpr {
Fixed {
x: f64,
y: f64,
z: f64,
},
Radius(StateSpec),
Velocity(StateSpec),
OrbitalMomentum(StateSpec),
EccentricityVector(StateSpec),
CrossProduct {
a: Box<Self>,
b: Box<Self>,
},
Unit(Box<Self>),
Add {
a: Box<Self>,
b: Box<Self>,
},
Negate(Box<Self>),
VecProjection {
a: Box<Self>,
b: Box<Self>,
},
Project {
v: Box<Self>,
frame: Box<OrthogonalFrame>,
plane: Option<Plane>,
},
Rotate {
v: Box<Self>,
dcm: Box<DcmExpr>,
},
}
impl fmt::Display for VectorExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Fixed { x, y, z } => write!(f, "[{x}, {y}, {z}]"),
Self::Radius(state) => write!(f, "Radius({state})"),
Self::Velocity(state) => write!(f, "Velocity({state})"),
Self::OrbitalMomentum(state) => write!(f, "OrbitalMomentum({state})"),
Self::EccentricityVector(state) => write!(f, "EccentricityVector({state})"),
Self::CrossProduct { a, b } => write!(f, "{a} тип {b}"),
Self::Unit(v) => write!(f, "unit({v})"),
Self::Add { a, b } => write!(f, "{a} + {b}"),
Self::Negate(v) => write!(f, "-{v}"),
Self::VecProjection { a, b } => write!(f, "proj {a} onto {b}"),
Self::Project { v, frame, plane } => {
if let Some(plane) = plane {
write!(f, "proj {v} onto {plane:?} of {frame}")
} else {
write!(f, "{v} dot {frame}")
}
}
Self::Rotate { v, dcm } => write!(f, "{v} rotated by {dcm}"),
}
}
}
impl VectorExpr {
pub fn evaluate(&self, epoch: Epoch, almanac: &Almanac) -> Result<Vector3, AnalysisError> {
match self {
Self::Fixed { x, y, z } => Ok(Vector3::new(*x, *y, *z)),
Self::Radius(spec) => Ok(spec.evaluate(epoch, almanac)?.radius_km),
Self::Velocity(spec) => Ok(spec.evaluate(epoch, almanac)?.velocity_km_s),
Self::OrbitalMomentum(spec) => {
spec.evaluate(epoch, almanac)?
.hvec()
.context(PhysicsVecExprSnafu {
expr: Box::new(self.clone()),
epoch,
})
}
Self::EccentricityVector(spec) => {
spec.evaluate(epoch, almanac)?
.evec()
.context(PhysicsVecExprSnafu {
expr: Box::new(self.clone()),
epoch,
})
}
Self::CrossProduct { a, b } => {
let vec_a = a.evaluate(epoch, almanac)?;
let vec_b = b.evaluate(epoch, almanac)?;
Ok(vec_a.cross(&vec_b))
}
Self::Unit(v) => Ok(v
.evaluate(epoch, almanac)?
.try_normalize(1e-12)
.unwrap_or(Vector3::zeros())),
Self::Add { a, b } => {
let vec_a = a.evaluate(epoch, almanac)?;
let vec_b = b.evaluate(epoch, almanac)?;
Ok(vec_a + vec_b)
}
Self::Negate(v) => Ok(-v.evaluate(epoch, almanac)?),
Self::VecProjection { a, b } => {
let vec_a = a.evaluate(epoch, almanac)?;
let vec_b = b.evaluate(epoch, almanac)?;
Ok(vec_a.dot(&vec_b) * vec_b)
}
Self::Project { v, frame, plane } => {
let dcm = frame.evaluate(epoch, almanac)?;
let vector = v.evaluate(epoch, almanac)?;
if let Some(plane) = plane {
Ok(dcm * plane.mask() * vector)
} else {
Ok(dcm * vector)
}
}
Self::Rotate { v, dcm } => {
let dcm = dcm.evaluate(epoch, almanac)?;
let vector = v.evaluate(epoch, almanac)?;
Ok(dcm * vector)
}
}
}
}