use crate::coordinates::cartesian::Direction;
use crate::coordinates::frames::{self, MutableFrame};
use crate::coordinates::transform::frames::{bias, galactic};
use crate::coordinates::transform::TransformFrame;
use affn::Rotation3;
impl<F: MutableFrame> TransformFrame<Direction<F>> for Direction<F> {
fn to_frame(&self) -> Direction<F> {
Direction::<F>::from_array(self.as_array())
}
}
impl TransformFrame<Direction<frames::EquatorialMeanJ2000>>
for Direction<frames::EclipticMeanJ2000>
{
fn to_frame(&self) -> Direction<frames::EquatorialMeanJ2000> {
let rot = bias::obliquity_ecl_to_eq();
let [x, y, z] = rot.apply_array([self.x(), self.y(), self.z()]);
Direction::<frames::EquatorialMeanJ2000>::from_array([x, y, z])
}
}
impl TransformFrame<Direction<frames::EclipticMeanJ2000>>
for Direction<frames::EquatorialMeanJ2000>
{
fn to_frame(&self) -> Direction<frames::EclipticMeanJ2000> {
let rot = bias::obliquity_eq_to_ecl();
let [x, y, z] = rot.apply_array([self.x(), self.y(), self.z()]);
Direction::<frames::EclipticMeanJ2000>::from_array([x, y, z])
}
}
impl TransformFrame<Direction<frames::EquatorialMeanJ2000>> for Direction<frames::ICRS> {
fn to_frame(&self) -> Direction<frames::EquatorialMeanJ2000> {
let rot: Rotation3 = bias::frame_bias_icrs_to_j2000();
let [x, y, z] = rot.apply_array([self.x(), self.y(), self.z()]);
Direction::<frames::EquatorialMeanJ2000>::from_array([x, y, z])
}
}
impl TransformFrame<Direction<frames::ICRS>> for Direction<frames::EquatorialMeanJ2000> {
fn to_frame(&self) -> Direction<frames::ICRS> {
let rot: Rotation3 = bias::frame_bias_j2000_to_icrs();
let [x, y, z] = rot.apply_array([self.x(), self.y(), self.z()]);
Direction::<frames::ICRS>::from_array([x, y, z])
}
}
impl TransformFrame<Direction<frames::Galactic>> for Direction<frames::ICRS> {
fn to_frame(&self) -> Direction<frames::Galactic> {
let [x, y, z] = galactic::icrs_to_galactic().apply_array([self.x(), self.y(), self.z()]);
Direction::<frames::Galactic>::from_array([x, y, z])
}
}
impl TransformFrame<Direction<frames::ICRS>> for Direction<frames::Galactic> {
fn to_frame(&self) -> Direction<frames::ICRS> {
let [x, y, z] = galactic::galactic_to_icrs().apply_array([self.x(), self.y(), self.z()]);
Direction::<frames::ICRS>::from_array([x, y, z])
}
}
impl TransformFrame<Direction<frames::Galactic>> for Direction<frames::EquatorialMeanJ2000> {
fn to_frame(&self) -> Direction<frames::Galactic> {
let [x, y, z] =
galactic::equatorial_j2000_to_galactic().apply_array([self.x(), self.y(), self.z()]);
Direction::<frames::Galactic>::from_array([x, y, z])
}
}
impl TransformFrame<Direction<frames::EquatorialMeanJ2000>> for Direction<frames::Galactic> {
fn to_frame(&self) -> Direction<frames::EquatorialMeanJ2000> {
let [x, y, z] =
galactic::galactic_to_equatorial_j2000().apply_array([self.x(), self.y(), self.z()]);
Direction::<frames::EquatorialMeanJ2000>::from_array([x, y, z])
}
}
impl TransformFrame<Direction<frames::Galactic>> for Direction<frames::EclipticMeanJ2000> {
fn to_frame(&self) -> Direction<frames::Galactic> {
let [x, y, z] =
galactic::ecliptic_j2000_to_galactic().apply_array([self.x(), self.y(), self.z()]);
Direction::<frames::Galactic>::from_array([x, y, z])
}
}
impl TransformFrame<Direction<frames::EclipticMeanJ2000>> for Direction<frames::Galactic> {
fn to_frame(&self) -> Direction<frames::EclipticMeanJ2000> {
let [x, y, z] =
galactic::galactic_to_ecliptic_j2000().apply_array([self.x(), self.y(), self.z()]);
Direction::<frames::EclipticMeanJ2000>::from_array([x, y, z])
}
}
impl TransformFrame<Direction<frames::ICRS>> for Direction<frames::EclipticMeanJ2000> {
fn to_frame(&self) -> Direction<frames::ICRS> {
let eq: Direction<frames::EquatorialMeanJ2000> = self.to_frame();
eq.to_frame()
}
}
impl TransformFrame<Direction<frames::EclipticMeanJ2000>> for Direction<frames::ICRS> {
fn to_frame(&self) -> Direction<frames::EclipticMeanJ2000> {
let eq: Direction<frames::EquatorialMeanJ2000> = self.to_frame();
eq.to_frame()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::coordinates::spherical;
use crate::qtty::Degrees;
fn assert_arr3_close(a: [f64; 3], b: [f64; 3], eps: f64) {
assert!((a[0] - b[0]).abs() < eps);
assert!((a[1] - b[1]).abs() < eps);
assert!((a[2] - b[2]).abs() < eps);
}
#[test]
fn identity_direction_transform_is_noop() {
let dir = Direction::<frames::EclipticMeanJ2000>::from_array([0.0, 0.6, 0.8]);
let same: Direction<frames::EclipticMeanJ2000> = dir.to_frame();
assert_arr3_close(dir.as_array(), same.as_array(), 1e-15);
}
#[test]
fn ecliptic_to_icrs_roundtrip_matches_original() {
let dir_ecl = Direction::<frames::EclipticMeanJ2000>::from_array([0.1, 0.2, 0.3]);
let icrs: Direction<frames::ICRS> = dir_ecl.to_frame();
let back: Direction<frames::EclipticMeanJ2000> = icrs.to_frame();
assert_arr3_close(dir_ecl.as_array(), back.as_array(), 1e-12);
}
#[test]
fn icrs_to_equatorial_bias_and_back_is_stable() {
let icrs = Direction::<frames::ICRS>::from_array([-0.3, 0.4, -0.5]);
let eq: Direction<frames::EquatorialMeanJ2000> = icrs.to_frame();
let back: Direction<frames::ICRS> = eq.to_frame();
assert_arr3_close(icrs.as_array(), back.as_array(), 1e-12);
}
#[test]
fn icrs_to_galactic_roundtrip_matches_original() {
let icrs = Direction::<frames::ICRS>::from_array([0.2, -0.4, 0.7]);
let galactic: Direction<frames::Galactic> = icrs.to_frame();
let back: Direction<frames::ICRS> = galactic.to_frame();
assert_arr3_close(icrs.as_array(), back.as_array(), 1e-12);
}
#[test]
fn galactic_north_pole_maps_to_positive_galactic_z() {
let ra = 192.859_48_f64.to_radians();
let dec = 27.128_25_f64.to_radians();
let icrs = Direction::<frames::ICRS>::from_array([
dec.cos() * ra.cos(),
dec.cos() * ra.sin(),
dec.sin(),
]);
let galactic: Direction<frames::Galactic> = icrs.to_frame();
assert!((galactic.x()).abs() < 1e-9);
assert!((galactic.y()).abs() < 1e-9);
assert!((galactic.z() - 1.0).abs() < 1e-12);
}
#[test]
fn spherical_direction_blanket_impl_supports_galactic_frame() {
let icrs = spherical::Direction::<frames::ICRS>::new_unchecked(
Degrees::new(27.128_25),
Degrees::new(192.859_48),
);
let galactic: spherical::Direction<frames::Galactic> = icrs.to_frame();
assert!((galactic.polar.value() - 90.0).abs() < 1e-9);
}
}