geometry_adapt/
with_cs.rs1use core::marker::PhantomData;
17
18use geometry_cs::CoordinateSystem;
19use geometry_tag::PointTag;
20use geometry_trait::{Geometry, Point, PointMut};
21
22#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
50#[repr(transparent)]
51pub struct WithCs<T, Cs: CoordinateSystem> {
52 pub inner: T,
55 _cs: PhantomData<Cs>,
56}
57
58impl<T, Cs: CoordinateSystem> WithCs<T, Cs> {
59 #[inline]
61 #[must_use]
62 pub const fn new(inner: T) -> Self {
63 Self {
64 inner,
65 _cs: PhantomData,
66 }
67 }
68
69 #[inline]
71 #[must_use]
72 pub fn into_inner(self) -> T {
73 self.inner
74 }
75}
76
77impl<P: Point, Cs: CoordinateSystem> Geometry for WithCs<P, Cs> {
78 type Kind = PointTag;
79 type Point = Self;
80}
81
82impl<P: Point, Cs: CoordinateSystem> Point for WithCs<P, Cs> {
83 type Scalar = P::Scalar;
84 type Cs = Cs;
85 const DIM: usize = P::DIM;
86
87 #[inline]
88 fn get<const D: usize>(&self) -> P::Scalar {
89 self.inner.get::<D>()
90 }
91}
92
93impl<P: PointMut, Cs: CoordinateSystem> PointMut for WithCs<P, Cs> {
94 #[inline]
95 fn set<const D: usize>(&mut self, value: P::Scalar) {
96 self.inner.set::<D>(value);
97 }
98}
99
100#[cfg(test)]
101#[allow(
102 clippy::float_cmp,
103 reason = "Every assertion here reads back a value just written, \
104 with no arithmetic in between, so strict equality is exact."
105)]
106mod tests {
107 use super::*;
108 use crate::Adapt;
109 use geometry_cs::{Cartesian, CoordinateSystem, Degree, Geographic, Spherical};
110 use geometry_trait::{Point, PointMut};
111
112 #[test]
114 fn re_tag_array_as_geographic_preserves_values() {
115 let p: WithCs<Adapt<[f64; 2]>, Geographic<Degree>> = WithCs::new(Adapt([4.9, 52.4]));
116 assert_eq!(p.get::<0>(), 4.9);
117 assert_eq!(p.get::<1>(), 52.4);
118 assert_eq!(
119 <WithCs<Adapt<[f64; 2]>, Geographic<Degree>> as Point>::DIM,
120 2
121 );
122 }
123
124 #[test]
126 fn re_tag_changes_family() {
127 fn family<P: Point>() -> &'static str
128 where
129 <P::Cs as CoordinateSystem>::Family: 'static,
130 {
131 core::any::type_name::<<P::Cs as CoordinateSystem>::Family>()
132 }
133 assert!(family::<Adapt<[f64; 2]>>().contains("CartesianFamily"));
135 assert!(
137 family::<WithCs<Adapt<[f64; 2]>, Geographic<Degree>>>().contains("GeographicFamily")
138 );
139 assert!(family::<WithCs<Adapt<[f64; 2]>, Spherical<Degree>>>().contains("SphericalFamily"));
141 }
142
143 struct MyXy(f64, f64);
145
146 impl Geometry for MyXy {
147 type Kind = PointTag;
148 type Point = Self;
149 }
150
151 impl Point for MyXy {
152 type Scalar = f64;
153 type Cs = Cartesian;
154 const DIM: usize = 2;
155
156 fn get<const D: usize>(&self) -> f64 {
157 if D == 0 { self.0 } else { self.1 }
158 }
159 }
160
161 impl PointMut for MyXy {
162 fn set<const D: usize>(&mut self, v: f64) {
163 if D == 0 {
164 self.0 = v;
165 } else {
166 self.1 = v;
167 }
168 }
169 }
170
171 #[test]
172 fn re_tag_user_point() {
173 let p: WithCs<MyXy, Spherical<Degree>> = WithCs::new(MyXy(1.0, 2.0));
174 assert_eq!(p.get::<0>(), 1.0);
175 assert_eq!(p.get::<1>(), 2.0);
176 }
177
178 #[test]
181 fn set_writes_through_to_inner() {
182 let mut p: WithCs<MyXy, Spherical<Degree>> = WithCs::new(MyXy(0.0, 0.0));
183 p.set::<0>(7.0);
184 p.set::<1>(9.0);
185 assert_eq!(p.get::<0>(), 7.0);
186 assert_eq!(p.get::<1>(), 9.0);
187 let inner = p.into_inner();
189 assert_eq!(inner.0, 7.0);
190 assert_eq!(inner.1, 9.0);
191 }
192
193 #[test]
197 fn size_is_inner_size() {
198 assert_eq!(
199 core::mem::size_of::<WithCs<Adapt<[f64; 2]>, Geographic<Degree>>>(),
200 core::mem::size_of::<[f64; 2]>(),
201 );
202 }
203}