#![allow(unused_macros)]
macro_rules! vec3_bridge {
($geo:ident { $a:ident, $b:ident, $c:ident } <=> $foreign:ty : $ctor:expr, |$v:ident| $rev:expr) => {
impl From<crate::$geo> for $foreign {
#[inline]
fn from(g: crate::$geo) -> Self {
let ctor = $ctor;
ctor(g.$a, g.$b, g.$c)
}
}
impl From<$foreign> for crate::$geo {
#[inline]
fn from($v: $foreign) -> Self {
let (x, y, z) = $rev;
crate::$geo::new(x, y, z)
}
}
};
}
#[cfg(feature = "mint")]
mod mint_impls {
vec3_bridge!(Vec3 { x, y, z } <=> mint::Vector3<f64> : |x, y, z| mint::Vector3 { x, y, z }, |v| (v.x, v.y, v.z));
vec3_bridge!(Enu { east, north, up } <=> mint::Vector3<f64> : |x, y, z| mint::Vector3 { x, y, z }, |v| (v.x, v.y, v.z));
vec3_bridge!(Ned { north, east, down } <=> mint::Vector3<f64> : |x, y, z| mint::Vector3 { x, y, z }, |v| (v.x, v.y, v.z));
vec3_bridge!(Frd { forward, right, down } <=> mint::Vector3<f64> : |x, y, z| mint::Vector3 { x, y, z }, |v| (v.x, v.y, v.z));
vec3_bridge!(Ecef { x, y, z } <=> mint::Vector3<f64> : |x, y, z| mint::Vector3 { x, y, z }, |v| (v.x, v.y, v.z));
vec3_bridge!(Eci { x, y, z } <=> mint::Vector3<f64> : |x, y, z| mint::Vector3 { x, y, z }, |v| (v.x, v.y, v.z));
vec3_bridge!(Ecef { x, y, z } <=> mint::Point3<f64> : |x, y, z| mint::Point3 { x, y, z }, |v| (v.x, v.y, v.z));
vec3_bridge!(Eci { x, y, z } <=> mint::Point3<f64> : |x, y, z| mint::Point3 { x, y, z }, |v| (v.x, v.y, v.z));
use crate::Cov3;
use mint::{ColumnMatrix3, RowMatrix3, Vector3};
fn v([x, y, z]: [f64; 3]) -> Vector3<f64> {
Vector3 { x, y, z }
}
impl From<Cov3> for RowMatrix3<f64> {
fn from(c: Cov3) -> Self {
let [x, y, z] = c.0.map(v);
RowMatrix3 { x, y, z }
}
}
impl From<RowMatrix3<f64>> for Cov3 {
fn from(m: RowMatrix3<f64>) -> Self {
Cov3([m.x, m.y, m.z].map(|r| [r.x, r.y, r.z]))
}
}
impl From<Cov3> for ColumnMatrix3<f64> {
fn from(c: Cov3) -> Self {
let m = c.0;
let col = |j: usize| v([m[0][j], m[1][j], m[2][j]]);
ColumnMatrix3 {
x: col(0),
y: col(1),
z: col(2),
}
}
}
impl From<crate::Quaternion> for mint::Quaternion<f64> {
fn from(q: crate::Quaternion) -> Self {
mint::Quaternion {
s: q.w,
v: v([q.x, q.y, q.z]),
}
}
}
impl From<mint::Quaternion<f64>> for crate::Quaternion {
fn from(q: mint::Quaternion<f64>) -> Self {
crate::Quaternion::new(q.s, q.v.x, q.v.y, q.v.z)
}
}
impl From<ColumnMatrix3<f64>> for Cov3 {
fn from(m: ColumnMatrix3<f64>) -> Self {
Cov3([[m.x.x, m.y.x, m.z.x], [m.x.y, m.y.y, m.z.y], [m.x.z, m.y.z, m.z.z]])
}
}
}
#[cfg(feature = "glam")]
mod glam_impls {
vec3_bridge!(Vec3 { x, y, z } <=> glam::DVec3 : |x, y, z| glam::DVec3::new(x, y, z), |v| (v.x, v.y, v.z));
vec3_bridge!(Enu { east, north, up } <=> glam::DVec3 : |x, y, z| glam::DVec3::new(x, y, z), |v| (v.x, v.y, v.z));
vec3_bridge!(Ned { north, east, down } <=> glam::DVec3 : |x, y, z| glam::DVec3::new(x, y, z), |v| (v.x, v.y, v.z));
vec3_bridge!(Frd { forward, right, down } <=> glam::DVec3 : |x, y, z| glam::DVec3::new(x, y, z), |v| (v.x, v.y, v.z));
vec3_bridge!(Ecef { x, y, z } <=> glam::DVec3 : |x, y, z| glam::DVec3::new(x, y, z), |v| (v.x, v.y, v.z));
vec3_bridge!(Eci { x, y, z } <=> glam::DVec3 : |x, y, z| glam::DVec3::new(x, y, z), |v| (v.x, v.y, v.z));
use crate::Cov3;
use glam::DMat3;
impl From<Cov3> for DMat3 {
fn from(c: Cov3) -> Self {
DMat3::from_cols_array_2d(&c.0).transpose()
}
}
impl From<DMat3> for Cov3 {
fn from(m: DMat3) -> Self {
Cov3(m.transpose().to_cols_array_2d())
}
}
impl From<crate::Quaternion> for glam::DQuat {
fn from(q: crate::Quaternion) -> Self {
glam::DQuat::from_xyzw(q.x, q.y, q.z, q.w)
}
}
impl From<glam::DQuat> for crate::Quaternion {
fn from(q: glam::DQuat) -> Self {
crate::Quaternion::new(q.w, q.x, q.y, q.z)
}
}
}
#[cfg(feature = "nalgebra")]
mod nalgebra_impls {
vec3_bridge!(Vec3 { x, y, z } <=> nalgebra::Vector3<f64> : |x, y, z| nalgebra::Vector3::new(x, y, z), |v| (v.x, v.y, v.z));
vec3_bridge!(Enu { east, north, up } <=> nalgebra::Vector3<f64> : |x, y, z| nalgebra::Vector3::new(x, y, z), |v| (v.x, v.y, v.z));
vec3_bridge!(Ned { north, east, down } <=> nalgebra::Vector3<f64> : |x, y, z| nalgebra::Vector3::new(x, y, z), |v| (v.x, v.y, v.z));
vec3_bridge!(Frd { forward, right, down } <=> nalgebra::Vector3<f64> : |x, y, z| nalgebra::Vector3::new(x, y, z), |v| (v.x, v.y, v.z));
vec3_bridge!(Ecef { x, y, z } <=> nalgebra::Vector3<f64> : |x, y, z| nalgebra::Vector3::new(x, y, z), |v| (v.x, v.y, v.z));
vec3_bridge!(Eci { x, y, z } <=> nalgebra::Vector3<f64> : |x, y, z| nalgebra::Vector3::new(x, y, z), |v| (v.x, v.y, v.z));
vec3_bridge!(Ecef { x, y, z } <=> nalgebra::Point3<f64> : |x, y, z| nalgebra::Point3::new(x, y, z), |v| (v.coords.x, v.coords.y, v.coords.z));
vec3_bridge!(Eci { x, y, z } <=> nalgebra::Point3<f64> : |x, y, z| nalgebra::Point3::new(x, y, z), |v| (v.coords.x, v.coords.y, v.coords.z));
use crate::Cov3;
use nalgebra::Matrix3;
impl From<Cov3> for Matrix3<f64> {
fn from(c: Cov3) -> Self {
Matrix3::from_fn(|r, col| c.0[r][col])
}
}
impl From<Matrix3<f64>> for Cov3 {
fn from(m: Matrix3<f64>) -> Self {
Cov3(core::array::from_fn(|r| core::array::from_fn(|col| m[(r, col)])))
}
}
impl From<crate::Quaternion> for nalgebra::Quaternion<f64> {
fn from(q: crate::Quaternion) -> Self {
nalgebra::Quaternion::new(q.w, q.x, q.y, q.z)
}
}
impl From<nalgebra::Quaternion<f64>> for crate::Quaternion {
fn from(q: nalgebra::Quaternion<f64>) -> Self {
crate::Quaternion::new(q.w, q.i, q.j, q.k)
}
}
impl From<nalgebra::UnitQuaternion<f64>> for crate::Quaternion {
fn from(q: nalgebra::UnitQuaternion<f64>) -> Self {
crate::Quaternion::from(q.into_inner())
}
}
}
#[cfg(feature = "geo-types")]
mod geo_types_impls {
use crate::Geodetic;
impl From<Geodetic> for geo_types::Point<f64> {
#[inline]
fn from(g: Geodetic) -> Self {
geo_types::Point::new(g.lon_deg, g.lat_deg)
}
}
impl From<Geodetic> for geo_types::Coord<f64> {
#[inline]
fn from(g: Geodetic) -> Self {
geo_types::Coord {
x: g.lon_deg,
y: g.lat_deg,
}
}
}
impl From<geo_types::Point<f64>> for Geodetic {
#[inline]
fn from(p: geo_types::Point<f64>) -> Self {
Geodetic::new(p.y(), p.x(), 0.0)
}
}
impl From<geo_types::Coord<f64>> for Geodetic {
#[inline]
fn from(c: geo_types::Coord<f64>) -> Self {
Geodetic::new(c.y, c.x, 0.0)
}
}
}
#[cfg(all(
test,
any(feature = "mint", feature = "glam", feature = "nalgebra", feature = "geo-types")
))]
mod tests {
#[allow(unused_imports)]
use crate::{Ecef, Eci, Enu, Geodetic, Ned, Vec3};
#[cfg(feature = "mint")]
#[test]
fn mint_round_trips_and_orders_fields() {
let v = Vec3::new(1.0, 2.0, 3.0);
let m: mint::Vector3<f64> = v.into();
assert_eq!((m.x, m.y, m.z), (1.0, 2.0, 3.0));
assert_eq!(Vec3::from(m), v);
let enu = Enu::new(4.0, 5.0, 6.0);
let me: mint::Vector3<f64> = enu.into();
assert_eq!((me.x, me.y, me.z), (4.0, 5.0, 6.0));
assert_eq!(Enu::from(me), enu);
let ned = Ned::new(7.0, 8.0, 9.0);
let mn: mint::Vector3<f64> = ned.into();
assert_eq!((mn.x, mn.y, mn.z), (7.0, 8.0, 9.0));
assert_eq!(Ned::from(mn), ned);
let e = Ecef::new(10.0, 20.0, 30.0);
let p: mint::Point3<f64> = e.into();
assert_eq!(Ecef::from(p), e);
let i = Eci::new(-1.0, -2.0, -3.0);
let pi: mint::Point3<f64> = i.into();
assert_eq!(Eci::from(pi), i);
}
#[cfg(feature = "glam")]
#[test]
fn glam_round_trips() {
let e = Ecef::new(1.5, -2.5, 3.5);
let g: glam::DVec3 = e.into();
assert_eq!((g.x, g.y, g.z), (1.5, -2.5, 3.5));
assert_eq!(Ecef::from(g), e);
let enu = Enu::new(1.0, 2.0, 3.0);
assert_eq!(Enu::from(glam::DVec3::from(enu)), enu);
}
#[cfg(feature = "nalgebra")]
#[test]
fn nalgebra_round_trips_vector_and_point() {
let v = Vec3::new(1.0, 2.0, 3.0);
let nv: nalgebra::Vector3<f64> = v.into();
assert_eq!((nv.x, nv.y, nv.z), (1.0, 2.0, 3.0));
assert_eq!(Vec3::from(nv), v);
let e = Ecef::new(10.0, 20.0, 30.0);
let np: nalgebra::Point3<f64> = e.into();
assert_eq!(Ecef::from(np), e);
}
#[allow(dead_code)]
const M: crate::Cov3 = crate::Cov3([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
#[cfg(feature = "mint")]
#[test]
fn mint_matrices_keep_rows_and_columns() {
let r: mint::RowMatrix3<f64> = M.into();
assert_eq!((r.x.y, r.y.x), (2.0, 4.0)); assert_eq!(crate::Cov3::from(r), M);
let c: mint::ColumnMatrix3<f64> = M.into();
assert_eq!((c.y.x, c.x.y), (2.0, 4.0)); assert_eq!(crate::Cov3::from(c), M);
}
#[cfg(feature = "glam")]
#[test]
fn glam_matrix_keeps_rows_and_columns() {
let g: glam::DMat3 = M.into();
assert_eq!(g.row(0), glam::DVec3::new(1.0, 2.0, 3.0));
assert_eq!(crate::Cov3::from(g), M);
}
#[cfg(feature = "nalgebra")]
#[test]
fn nalgebra_matrix_keeps_rows_and_columns() {
let n: nalgebra::Matrix3<f64> = M.into();
assert_eq!(n[(0, 1)], 2.0);
assert_eq!(crate::Cov3::from(n), M);
}
#[allow(dead_code)]
fn same_rotation(a: crate::Quaternion, b: crate::Quaternion) -> bool {
let d = |s: f64| {
(a.w - s * b.w)
.abs()
.max((a.x - s * b.x).abs())
.max((a.y - s * b.y).abs())
.max((a.z - s * b.z).abs())
};
d(1.0).min(d(-1.0)) < 1e-15
}
#[cfg(feature = "mint")]
#[test]
fn mint_quaternion_keeps_the_scalar() {
let q = crate::Quaternion::new(0.5, 0.1, 0.2, 0.3);
let m: mint::Quaternion<f64> = q.into();
assert_eq!((m.s, m.v.x, m.v.y, m.v.z), (0.5, 0.1, 0.2, 0.3));
assert_eq!(crate::Quaternion::from(m), q);
}
#[cfg(feature = "glam")]
#[test]
fn glam_quaternion_agrees() {
let q = crate::Quaternion::new(0.5, 0.1, 0.2, 0.3);
let g: glam::DQuat = q.into();
assert_eq!((g.w, g.x, g.y, g.z), (0.5, 0.1, 0.2, 0.3));
assert_eq!(crate::Quaternion::from(g), q);
for (y, p, r) in [(30.0f64, 10.0f64, -5.0f64), (250.0, -80.0, 170.0), (0.0, 89.9, 45.0)] {
let theirs = glam::DQuat::from_euler(glam::EulerRot::ZYX, y.to_radians(), p.to_radians(), r.to_radians());
assert!(
same_rotation(crate::Attitude::new(y, p, r).to_quaternion(), theirs.into()),
"{y} {p} {r}"
);
}
}
#[cfg(feature = "nalgebra")]
#[test]
fn nalgebra_quaternion_agrees() {
let q = crate::Quaternion::new(0.5, 0.1, 0.2, 0.3);
let n: nalgebra::Quaternion<f64> = q.into();
assert_eq!((n.w, n.i, n.j, n.k), (0.5, 0.1, 0.2, 0.3));
assert_eq!(crate::Quaternion::from(n), q);
for (y, p, r) in [(30.0f64, 10.0f64, -5.0f64), (250.0, -80.0, 170.0), (0.0, 89.9, 45.0)] {
let theirs = nalgebra::UnitQuaternion::from_euler_angles(r.to_radians(), p.to_radians(), y.to_radians());
assert!(
same_rotation(crate::Attitude::new(y, p, r).to_quaternion(), theirs.into()),
"{y} {p} {r}"
);
}
}
#[cfg(feature = "geo-types")]
#[test]
fn geo_types_uses_lon_x_lat_y_and_drops_height() {
let g = Geodetic::new(-33.87, 151.21, 20.0); let p: geo_types::Point<f64> = g.into();
assert_eq!((p.x(), p.y()), (151.21, -33.87)); let back = Geodetic::from(p);
assert_eq!((back.lat_deg, back.lon_deg, back.height_m), (-33.87, 151.21, 0.0));
let c: geo_types::Coord<f64> = g.into();
assert_eq!((c.x, c.y), (151.21, -33.87));
assert_eq!(Geodetic::from(c).lat_deg, -33.87);
}
}