1use std::{
2 error::Error,
3 fmt::Display,
4 marker::PhantomData,
5 num::ParseFloatError,
6 ops::{Bound, Range, RangeBounds, RangeTo},
7 str::FromStr,
8};
9
10use super::{CoordinateLike, HasProximity};
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
16#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
17pub struct CoordinateRange<C> {
18 pub start: Option<f64>,
19 pub end: Option<f64>,
20 #[cfg_attr(feature = "serde", serde(skip))]
21 coord: PhantomData<C>,
22}
23
24impl<C> CoordinateRange<C> {
25 pub fn new(start: Option<f64>, end: Option<f64>) -> Self {
26 Self {
27 start,
28 end,
29 coord: PhantomData,
30 }
31 }
32
33 pub fn contains<T: CoordinateLike<C>>(&self, point: &T) -> bool {
34 let x = CoordinateLike::<C>::coordinate(point);
35 RangeBounds::<f64>::contains(&self, &x)
36 }
37
38 pub fn contains_raw(&self, x: &f64) -> bool {
39 RangeBounds::<f64>::contains(&self, x)
40 }
41
42 pub fn overlaps<T: RangeBounds<f64>>(&self, interval: &T) -> bool {
43 let interval_start = match interval.start_bound() {
44 Bound::Included(x) => *x,
45 Bound::Excluded(x) => *x,
46 Bound::Unbounded => 0.0,
47 };
48
49 let interval_end = match interval.end_bound() {
50 Bound::Included(y) => *y,
51 Bound::Excluded(y) => *y,
52 Bound::Unbounded => f64::INFINITY,
53 };
54 (self.end.unwrap_or(f64::INFINITY) >= interval_start
55 && interval_end >= self.start.unwrap_or(0.0))
56 || (self.end.is_close(&Some(interval_end))
57 && self.start.is_close(&Some(interval_start)))
58 }
59}
60
61impl<C> Default for CoordinateRange<C> {
62 fn default() -> Self {
63 Self {
64 start: None,
65 end: None,
66 coord: PhantomData,
67 }
68 }
69}
70
71pub trait Span1D {
74 type DimType: HasProximity;
75
76 fn start(&self) -> Self::DimType;
77 fn end(&self) -> Self::DimType;
78
79 fn contains(&self, i: &Self::DimType) -> bool {
80 (self.start() <= *i && *i <= self.end()) || (self.start().is_close(i) || self.end().is_close(i))
81 }
82
83 fn is_close<T: Span1D<DimType = Self::DimType>>(&self, interval: &T) -> bool {
84 self.start().is_close(&interval.start()) && self.end().is_close(&interval.end())
85 }
86
87 fn overlaps<T: Span1D<DimType = Self::DimType>>(&self, interval: &T) -> bool {
88 (self.end() >= interval.start() && interval.end() >= self.start())
89 || self.is_close(&interval)
90 }
91
92 fn is_contained_in_interval<T: Span1D<DimType = Self::DimType>>(&self, interval: &T) -> bool {
93 (self.start() >= interval.start() && self.end() <= interval.end())
94 || self.is_close(&interval)
95 }
96
97 fn contains_interval<T: Span1D<DimType = Self::DimType>>(&self, interval: &T) -> bool {
98 (self.start() <= interval.start() && self.end() >= interval.end())
99 || self.is_close(&interval)
100 }
101}
102
103impl<T: Span1D> Span1D for &T {
104 type DimType = T::DimType;
105
106 fn start(&self) -> Self::DimType {
107 (*self).start()
108 }
109
110 fn end(&self) -> Self::DimType {
111 (*self).end()
112 }
113}
114
115impl<C> Span1D for CoordinateRange<C> {
116 type DimType = Option<f64>;
117
118 fn start(&self) -> Self::DimType {
119 self.start
120 }
121
122 fn end(&self) -> Self::DimType {
123 self.end
124 }
125}
126
127impl<T: HasProximity> Span1D for Range<T> {
128 type DimType = T;
129
130 fn start(&self) -> Self::DimType {
131 self.start
132 }
133
134 fn end(&self) -> Self::DimType {
135 self.end
136 }
137}
138
139#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
141#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
142pub struct SimpleInterval<V: PartialOrd> {
143 pub start: V,
144 pub end: V,
145}
146
147impl<V: PartialOrd> SimpleInterval<V> {
148 pub fn new(start: V, end: V) -> SimpleInterval<V> {
149 SimpleInterval { start, end }
150 }
151}
152
153impl<V: HasProximity> Span1D for SimpleInterval<V> {
154 type DimType = V;
155
156 fn start(&self) -> Self::DimType {
157 self.start
158 }
159
160 fn end(&self) -> Self::DimType {
161 self.end
162 }
163}
164
165impl<V: PartialOrd> From<(V, V)> for SimpleInterval<V> {
166 fn from(value: (V, V)) -> Self {
167 Self::new(value.0, value.1)
168 }
169}
170
171impl<V: PartialOrd> From<Range<V>> for SimpleInterval<V> {
172 fn from(value: Range<V>) -> Self {
173 Self::new(value.start, value.end)
174 }
175}
176
177#[derive(Debug)]
178pub enum CoordinateRangeParseError {
179 MalformedStart(ParseFloatError),
180 MalformedEnd(ParseFloatError),
181}
182
183impl Display for CoordinateRangeParseError {
184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185 match self {
186 CoordinateRangeParseError::MalformedStart(e) => {
187 write!(f, "Failed to parse range start {e}")
188 }
189 CoordinateRangeParseError::MalformedEnd(e) => {
190 write!(f, "Failed to parse range end {e}")
191 }
192 }
193 }
194}
195
196impl Error for CoordinateRangeParseError {}
197
198impl<C> FromStr for CoordinateRange<C> {
199 type Err = CoordinateRangeParseError;
200
201 fn from_str(s: &str) -> Result<Self, Self::Err> {
202 let mut tokens = if s.contains(' ') {
203 s.split(' ')
204 } else if s.contains(':') {
205 s.split(':')
206 } else if s.contains('-') {
207 s.split('-')
208 } else {
209 s.split(' ')
210 };
211 let start_s = tokens.next().unwrap();
212 let start_t = if start_s.is_empty() {
213 None
214 } else {
215 match start_s.parse() {
216 Ok(val) => Some(val),
217 Err(e) => return Err(CoordinateRangeParseError::MalformedStart(e)),
218 }
219 };
220 let end_s = tokens.next().unwrap();
221 let end_t = if end_s.is_empty() {
222 None
223 } else {
224 match end_s.parse() {
225 Ok(val) => Some(val),
226 Err(e) => return Err(CoordinateRangeParseError::MalformedEnd(e)),
227 }
228 };
229 Ok(CoordinateRange {
230 start: start_t,
231 end: end_t,
232 coord: PhantomData,
233 })
234 }
235}
236
237impl<C> From<RangeTo<f64>> for CoordinateRange<C> {
238 fn from(value: RangeTo<f64>) -> Self {
239 Self::new(None, Some(value.end))
240 }
241}
242
243impl<C> From<Range<f64>> for CoordinateRange<C> {
244 fn from(value: Range<f64>) -> Self {
245 Self::new(Some(value.start), Some(value.end))
246 }
247}
248
249impl<C> RangeBounds<f64> for CoordinateRange<C> {
250 fn start_bound(&self) -> Bound<&f64> {
251 if let Some(start) = self.start.as_ref() {
252 Bound::Included(start)
253 } else {
254 Bound::Unbounded
255 }
256 }
257
258 fn end_bound(&self) -> Bound<&f64> {
259 if let Some(end) = self.end.as_ref() {
260 Bound::Included(end)
261 } else {
262 Bound::Unbounded
263 }
264 }
265}
266
267impl<C> RangeBounds<f64> for &CoordinateRange<C> {
268 fn start_bound(&self) -> Bound<&f64> {
269 (*self).start_bound()
270 }
271
272 fn end_bound(&self) -> Bound<&f64> {
273 (*self).end_bound()
274 }
275}
276
277impl<C> From<(f64, f64)> for CoordinateRange<C> {
278 fn from(value: (f64, f64)) -> Self {
279 Self::new(Some(value.0), Some(value.1))
280 }
281}
282
283impl<C> From<CoordinateRange<C>> for Range<f64> {
284 fn from(value: CoordinateRange<C>) -> Self {
285 let start = value.start.unwrap_or(0.0);
286 let end = value.end.unwrap_or(f64::INFINITY);
287
288 start..end
289 }
290}
291
292#[cfg(test)]
293mod test {
294 use crate::Time;
295
296 use super::*;
297
298 #[test]
299 fn test_conversion() {
300 let time_range: CoordinateRange<Time> = (..5.0).into();
301 assert_eq!(time_range.start(), None);
302 assert_eq!(time_range.end(), Some(5.0));
303
304 let time_range: CoordinateRange<Time> = (5.0..10.0).into();
305 assert_eq!(time_range.start(), Some(5.0));
306 assert_eq!(time_range.end(), Some(10.0));
307
308 let time_range: CoordinateRange<Time> = ":5.0".parse().unwrap();
309 assert_eq!(time_range.start(), None);
310 assert_eq!(time_range.end(), Some(5.0));
311
312 let time_range: CoordinateRange<Time> = "-5.0".parse().unwrap();
313 assert_eq!(time_range.start(), None);
314 assert_eq!(time_range.end(), Some(5.0));
315
316 let time_range: CoordinateRange<Time> = " 5.0".parse().unwrap();
317 assert_eq!(time_range.start(), None);
318 assert_eq!(time_range.end(), Some(5.0));
319
320 let time_range: CoordinateRange<Time> = (0.0, 5.0).into();
325 assert_eq!(time_range.start(), Some(0.0));
326 assert_eq!(time_range.end(), Some(5.0));
327
328 let time_range: SimpleInterval<f64> = (0.0, 5.0).into();
329 assert_eq!(time_range.start(), 0.0);
330 assert_eq!(time_range.end(), 5.0);
331
332 let time_range: SimpleInterval<f64> = (5.0..10.0).into();
333 assert_eq!(time_range.start(), 5.0);
334 assert_eq!(time_range.end(), 10.0);
335
336 let empty = CoordinateRange::<Time>::default();
337 assert_eq!(empty.start(), None);
338 assert_eq!(empty.end(), None);
339
340 assert_eq!(empty.start_bound(), Bound::Unbounded);
341 assert_eq!(empty.end_bound(), Bound::Unbounded);
342 let t: Range<f64> = empty.into();
343
344 assert_eq!(t.start, 0.0);
345 assert_eq!(t.end, f64::INFINITY);
346 }
347
348 #[test]
349 fn test_interval() {
350 let time_range: CoordinateRange<Time> = (..5.0).into();
351 assert!(time_range.contains_raw(&2.5));
352 assert!(!time_range.contains_raw(&7.5));
353
354 let time_range2: CoordinateRange<Time> = (3.0..10.0).into();
355 assert!(time_range.overlaps(&time_range2));
356
357 assert!((5.0..7.0).is_contained_in_interval(&(3.0..10.0)));
358 assert!((3.0..10.0).contains_interval(&(5.0..7.0)));
359 }
360}