#![forbid(unsafe_code)]
#![deny(clippy::all)]
#![deny(clippy::pedantic)]
#![allow(clippy::float_cmp)]
#![deny(clippy::std_instead_of_core)]
#![deny(clippy::alloc_instead_of_core)]
#![deny(clippy::arithmetic_side_effects)]
#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(not(any(feature = "std", feature = "libm")))]
compile_error!("Either std or libm must be used for math operations");
use glam::{DVec2, DVec3};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "libm")]
mod math;
#[cfg(feature = "libm")]
#[allow(unused_imports)]
use math::F64Math;
mod dim2;
mod dim3;
pub mod reexports;
mod solvers;
pub use dim2::{CompactOrbit2D, MuSetterMode2D, Orbit2D, OrbitTrait2D, StateVectors2D};
pub use dim3::{CompactOrbit, MuSetterMode, Orbit, OrbitTrait, StateVectors};
const B: f64 = 0.999_999;
const N_U32: u32 = 5;
const N_F64: f64 = N_U32 as f64;
const NUMERIC_MAX_ITERS: u32 = 1000;
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Matrix3x2 {
pub e11: f64,
pub e12: f64,
pub e21: f64,
pub e22: f64,
pub e31: f64,
pub e32: f64,
}
impl Matrix3x2 {
pub const ZERO: Self = Self {
e11: 0.0,
e12: 0.0,
e21: 0.0,
e22: 0.0,
e31: 0.0,
e32: 0.0,
};
pub const IDENTITY: Self = Self {
e11: 1.0,
e22: 1.0,
..Self::ZERO
};
#[must_use]
pub fn dot_vec(&self, vec: DVec2) -> DVec3 {
DVec3::new(
vec.x * self.e11 + vec.y * self.e12,
vec.x * self.e21 + vec.y * self.e22,
vec.x * self.e31 + vec.y * self.e32,
)
}
}
#[cfg(feature = "mint")]
impl From<Matrix3x2> for mint::RowMatrix3x2<f64> {
fn from(value: Matrix3x2) -> Self {
mint::RowMatrix3x2::<f64> {
x: mint::Vector2::<f64> {
x: value.e11,
y: value.e12,
},
y: mint::Vector2::<f64> {
x: value.e21,
y: value.e22,
},
z: mint::Vector2::<f64> {
x: value.e31,
y: value.e32,
},
}
}
}
#[cfg(feature = "mint")]
impl mint::IntoMint for Matrix3x2 {
type MintType = mint::RowMatrix3x2<f64>;
}
#[cfg(feature = "mint")]
impl From<mint::RowMatrix3x2<f64>> for Matrix3x2 {
fn from(value: mint::RowMatrix3x2<f64>) -> Self {
Self {
e11: value.x.x,
e12: value.x.y,
e21: value.y.x,
e22: value.y.y,
e31: value.z.x,
e32: value.z.y,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ApoapsisSetterError {
ApoapsisLessThanPeriapsis,
ApoapsisNegative,
}
#[cfg(test)]
mod tests;
#[inline]
fn keplers_equation(mean_anomaly: f64, eccentric_anomaly: f64, eccentricity: f64) -> f64 {
eccentric_anomaly - (eccentricity * eccentric_anomaly.sin()) - mean_anomaly
}
#[inline]
fn keplers_equation_derivative(eccentric_anomaly: f64, eccentricity: f64) -> f64 {
1.0 - (eccentricity * eccentric_anomaly.cos())
}
#[inline]
fn keplers_equation_second_derivative(eccentric_anomaly: f64, eccentricity: f64) -> f64 {
eccentricity * eccentric_anomaly.sin()
}
#[must_use]
pub fn sinhcosh(x: f64) -> (f64, f64) {
let e_x = x.exp();
let e_neg_x = e_x.recip();
((e_x - e_neg_x) * 0.5, (e_x + e_neg_x) * 0.5)
}
#[expect(clippy::many_single_char_names)]
fn solve_monotone_cubic(a: f64, b: f64, c: f64, d: f64) -> f64 {
let b = b / a;
let c = c / a;
let d = d / a;
let b_sq = b * b;
let p = (3.0 * c - b_sq) / 3.0;
let q = (2.0 * b_sq * b - 9.0 * b * c + 27.0 * d) / 27.0;
let q_div_two = q / 2.0;
let p_div_three = p / 3.0;
let p_div_three_cubed = p_div_three * p_div_three * p_div_three;
let discriminant = q_div_two * q_div_two + p_div_three_cubed;
if discriminant < 0.0 {
return f64::NAN;
}
let t = {
let sqrt_discriminant = discriminant.sqrt();
let neg_q_div_two = -q_div_two;
let u = (neg_q_div_two + sqrt_discriminant).cbrt();
let v = (neg_q_div_two - sqrt_discriminant).cbrt();
u + v
};
t - b / 3.0
}
mod generated_sinh_approximator;