use core::marker::PhantomData;
use geometry_cs::CoordinateSystem;
use geometry_tag::PointTag;
use geometry_trait::{Geometry, Point, PointMut};
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct WithCs<T, Cs: CoordinateSystem> {
pub inner: T,
_cs: PhantomData<Cs>,
}
impl<T, Cs: CoordinateSystem> WithCs<T, Cs> {
#[inline]
#[must_use]
pub const fn new(inner: T) -> Self {
Self {
inner,
_cs: PhantomData,
}
}
#[inline]
#[must_use]
pub fn into_inner(self) -> T {
self.inner
}
}
impl<P: Point, Cs: CoordinateSystem> Geometry for WithCs<P, Cs> {
type Kind = PointTag;
type Point = Self;
}
impl<P: Point, Cs: CoordinateSystem> Point for WithCs<P, Cs> {
type Scalar = P::Scalar;
type Cs = Cs;
const DIM: usize = P::DIM;
#[inline]
fn get<const D: usize>(&self) -> P::Scalar {
self.inner.get::<D>()
}
}
impl<P: PointMut, Cs: CoordinateSystem> PointMut for WithCs<P, Cs> {
#[inline]
fn set<const D: usize>(&mut self, value: P::Scalar) {
self.inner.set::<D>(value);
}
}
#[cfg(test)]
#[allow(
clippy::float_cmp,
reason = "Every assertion here reads back a value just written, \
with no arithmetic in between, so strict equality is exact."
)]
mod tests {
use super::*;
use crate::Adapt;
use geometry_cs::{Cartesian, CoordinateSystem, Degree, Geographic, Spherical};
use geometry_trait::{Point, PointMut};
#[test]
fn re_tag_array_as_geographic_preserves_values() {
let p: WithCs<Adapt<[f64; 2]>, Geographic<Degree>> = WithCs::new(Adapt([4.9, 52.4]));
assert_eq!(p.get::<0>(), 4.9);
assert_eq!(p.get::<1>(), 52.4);
assert_eq!(
<WithCs<Adapt<[f64; 2]>, Geographic<Degree>> as Point>::DIM,
2
);
}
#[test]
fn re_tag_changes_family() {
fn family<P: Point>() -> &'static str
where
<P::Cs as CoordinateSystem>::Family: 'static,
{
core::any::type_name::<<P::Cs as CoordinateSystem>::Family>()
}
assert!(family::<Adapt<[f64; 2]>>().contains("CartesianFamily"));
assert!(
family::<WithCs<Adapt<[f64; 2]>, Geographic<Degree>>>().contains("GeographicFamily")
);
assert!(family::<WithCs<Adapt<[f64; 2]>, Spherical<Degree>>>().contains("SphericalFamily"));
}
struct MyXy(f64, f64);
impl Geometry for MyXy {
type Kind = PointTag;
type Point = Self;
}
impl Point for MyXy {
type Scalar = f64;
type Cs = Cartesian;
const DIM: usize = 2;
fn get<const D: usize>(&self) -> f64 {
if D == 0 { self.0 } else { self.1 }
}
}
impl PointMut for MyXy {
fn set<const D: usize>(&mut self, v: f64) {
if D == 0 {
self.0 = v;
} else {
self.1 = v;
}
}
}
#[test]
fn re_tag_user_point() {
let p: WithCs<MyXy, Spherical<Degree>> = WithCs::new(MyXy(1.0, 2.0));
assert_eq!(p.get::<0>(), 1.0);
assert_eq!(p.get::<1>(), 2.0);
}
#[test]
fn set_writes_through_to_inner() {
let mut p: WithCs<MyXy, Spherical<Degree>> = WithCs::new(MyXy(0.0, 0.0));
p.set::<0>(7.0);
p.set::<1>(9.0);
assert_eq!(p.get::<0>(), 7.0);
assert_eq!(p.get::<1>(), 9.0);
let inner = p.into_inner();
assert_eq!(inner.0, 7.0);
assert_eq!(inner.1, 9.0);
}
#[test]
fn size_is_inner_size() {
assert_eq!(
core::mem::size_of::<WithCs<Adapt<[f64; 2]>, Geographic<Degree>>>(),
core::mem::size_of::<[f64; 2]>(),
);
}
}