#[allow(unused_imports)]
use num_traits::Float as _;
use crate::constants::{COLINEARITY_TOL, MIN_POSITION_NORM};
use crate::error::{LambertError, NonFiniteParameter, Position};
use crate::vec3::{self, Vec3};
#[derive(Debug, Clone, Copy)]
pub(crate) struct Geometry {
pub lambda: f64,
pub big_t: f64,
pub gamma: f64,
pub rho: f64,
pub sigma: f64,
pub r1n: f64,
pub r2n: f64,
pub ir1: Vec3,
pub ir2: Vec3,
pub it1: Vec3,
pub it2: Vec3,
}
impl Geometry {
#[allow(clippy::similar_names)] pub(crate) fn from_inputs(
r1: Vec3,
r2: Vec3,
tof: f64,
mu: f64,
way: crate::TransferWay,
) -> Result<Self, LambertError> {
validate_finite_r1(r1)?;
validate_finite_r2(r2)?;
validate_finite_scalar(NonFiniteParameter::Tof, tof)?;
validate_finite_scalar(NonFiniteParameter::Mu, mu)?;
if tof <= 0.0 {
return Err(LambertError::NonPositiveTimeOfFlight { tof });
}
if mu <= 0.0 {
return Err(LambertError::NonPositiveMu { mu });
}
let r1n = vec3::norm(r1);
let r2n = vec3::norm(r2);
if r1n < MIN_POSITION_NORM {
return Err(LambertError::DegeneratePositionVector {
position: Position::R1,
norm: r1n,
});
}
if r2n < MIN_POSITION_NORM {
return Err(LambertError::DegeneratePositionVector {
position: Position::R2,
norm: r2n,
});
}
let chord = [r2[0] - r1[0], r2[1] - r1[1], r2[2] - r1[2]];
let c = vec3::norm(chord);
let s = 0.5 * (r1n + r2n + c);
let ir1 = vec3::scale(r1, 1.0 / r1n);
let ir2 = vec3::scale(r2, 1.0 / r2n);
let ih_raw = vec3::cross(ir1, ir2);
let sin_angle = vec3::norm(ih_raw);
if sin_angle < COLINEARITY_TOL {
return Err(LambertError::CollinearGeometry { sin_angle });
}
let ih = vec3::scale(ih_raw, 1.0 / sin_angle);
let mut lambda = (1.0 - c / s).max(0.0).sqrt();
let (it1_raw, it2_raw) = if matches!(way, crate::TransferWay::Long) {
lambda = -lambda;
(vec3::cross(ir1, ih), vec3::cross(ir2, ih))
} else {
(vec3::cross(ih, ir1), vec3::cross(ih, ir2))
};
let it1 = vec3::scale(it1_raw, 1.0 / vec3::norm(it1_raw));
let it2 = vec3::scale(it2_raw, 1.0 / vec3::norm(it2_raw));
let big_t = (2.0 * mu / (s * s * s)).sqrt() * tof;
let gamma = (mu * s / 2.0).sqrt();
let rho = (r1n - r2n) / c;
let sigma = (1.0 - rho * rho).max(0.0).sqrt();
Ok(Self {
lambda,
big_t,
gamma,
rho,
sigma,
r1n,
r2n,
ir1,
ir2,
it1,
it2,
})
}
}
fn validate_finite_scalar(parameter: NonFiniteParameter, value: f64) -> Result<(), LambertError> {
if value.is_finite() {
Ok(())
} else {
Err(LambertError::NonFiniteInput { parameter, value })
}
}
fn validate_finite_r1(value: Vec3) -> Result<(), LambertError> {
validate_finite_scalar(NonFiniteParameter::R1X, value[0])?;
validate_finite_scalar(NonFiniteParameter::R1Y, value[1])?;
validate_finite_scalar(NonFiniteParameter::R1Z, value[2])
}
fn validate_finite_r2(value: Vec3) -> Result<(), LambertError> {
validate_finite_scalar(NonFiniteParameter::R2X, value[0])?;
validate_finite_scalar(NonFiniteParameter::R2Y, value[1])?;
validate_finite_scalar(NonFiniteParameter::R2Z, value[2])
}