s2json_core/geometry/
bbox.rs

1use crate::*;
2use alloc::fmt;
3use core::{
4    cmp::Ordering,
5    ops::{Mul, MulAssign, Sub},
6};
7use serde::{
8    Deserialize, Deserializer, Serialize, Serializer,
9    de::{self, SeqAccess, Visitor},
10    ser::SerializeTuple,
11};
12
13trait Bounded {
14    fn min_value() -> Self;
15    fn max_value() -> Self;
16}
17macro_rules! impl_bounded {
18    ($($t:ty),*) => {
19        $(
20            impl Bounded for $t {
21                fn min_value() -> Self {
22                    <$t>::MIN
23                }
24                fn max_value() -> Self {
25                    <$t>::MAX
26                }
27            }
28        )*
29    };
30}
31
32// Implement for common numeric types
33impl_bounded!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, isize, usize, f32, f64);
34
35/// A BBOX is defined in lon-lat space and helps with zooming motion to
36/// see the entire line or polygon
37/// The order is (left, bottom, right, top)
38/// If WM, then the projection is lon-lat
39/// If S2, then the projection is s-t
40#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
41pub struct BBox<T = f64> {
42    /// left most longitude (WM) or S (S2)
43    pub left: T,
44    /// bottom most latitude (WM) or T (S2)
45    pub bottom: T,
46    /// right most longitude (WM) or T (S2)
47    pub right: T,
48    /// top most latitude (WM) or S (S2)
49    pub top: T,
50}
51impl<T> From<BBox<T>> for MValue
52where
53    T: Into<ValueType>,
54{
55    fn from(bbox: BBox<T>) -> MValue {
56        MValue::from([
57            ("left".into(), bbox.left.into()),
58            ("bottom".into(), bbox.bottom.into()),
59            ("right".into(), bbox.right.into()),
60            ("top".into(), bbox.top.into()),
61        ])
62    }
63}
64impl<T> From<&BBox<T>> for MValue
65where
66    T: Copy + Into<ValueType>,
67{
68    fn from(bbox: &BBox<T>) -> MValue {
69        MValue::from([
70            ("left".into(), bbox.left.into()),
71            ("bottom".into(), bbox.bottom.into()),
72            ("right".into(), bbox.right.into()),
73            ("top".into(), bbox.top.into()),
74        ])
75    }
76}
77impl<T> From<MValue> for BBox<T>
78where
79    T: From<ValueType>,
80{
81    fn from(mvalue: MValue) -> Self {
82        BBox {
83            left: mvalue.get("left").unwrap().clone().into(),
84            bottom: mvalue.get("bottom").unwrap().clone().into(),
85            right: mvalue.get("right").unwrap().clone().into(),
86            top: mvalue.get("top").unwrap().clone().into(),
87        }
88    }
89}
90impl<T> From<&MValue> for BBox<T>
91where
92    T: From<ValueType>,
93{
94    fn from(mvalue: &MValue) -> Self {
95        BBox {
96            left: mvalue.get("left").unwrap().clone().into(),
97            bottom: mvalue.get("bottom").unwrap().clone().into(),
98            right: mvalue.get("right").unwrap().clone().into(),
99            top: mvalue.get("top").unwrap().clone().into(),
100        }
101    }
102}
103impl<T> MValueCompatible for BBox<T>
104where
105    ValueType: From<T>,
106    T: From<ValueType> + Default + Bounded + Copy + Interpolate,
107{
108}
109impl<T> Default for BBox<T>
110where
111    T: Default + Bounded + Copy,
112{
113    fn default() -> Self {
114        BBox::new(T::max_value(), T::max_value(), T::min_value(), T::min_value())
115    }
116}
117impl<T> BBox<T> {
118    /// Creates a new BBox
119    pub fn new(left: T, bottom: T, right: T, top: T) -> Self
120    where
121        T: Copy,
122    {
123        BBox { left, bottom, right, top }
124    }
125
126    /// Checks if a point is within the BBox
127    pub fn point_overlap<P: GetXY>(&self, point: &P) -> bool
128    where
129        T: Into<f64> + Copy, // Ensures that comparison operators work for type T
130    {
131        point.x() >= self.left.into()
132            && point.x() <= self.right.into()
133            && point.y() >= self.bottom.into()
134            && point.y() <= self.top.into()
135    }
136
137    /// Merges another bounding box with this one
138    pub fn merge(&self, other: &Self) -> Self
139    where
140        T: PartialOrd + Copy,
141    {
142        let mut new_bbox = *self;
143        new_bbox.left = if new_bbox.left < other.left { new_bbox.left } else { other.left };
144        new_bbox.bottom =
145            if new_bbox.bottom < other.bottom { new_bbox.bottom } else { other.bottom };
146        new_bbox.right = if new_bbox.right > other.right { new_bbox.right } else { other.right };
147        new_bbox.top = if new_bbox.top > other.top { new_bbox.top } else { other.top };
148
149        new_bbox
150    }
151
152    /// Merges in place another bounding box with this one
153    pub fn merge_in_place(&mut self, other: &Self)
154    where
155        T: PartialOrd + Copy,
156    {
157        self.left = if self.left < other.left { self.left } else { other.left };
158        self.bottom = if self.bottom < other.bottom { self.bottom } else { other.bottom };
159        self.right = if self.right > other.right { self.right } else { other.right };
160        self.top = if self.top > other.top { self.top } else { other.top };
161    }
162
163    /// Checks if another bounding box overlaps with this one and returns the overlap
164    pub fn overlap(&self, other: &BBox<T>) -> Option<BBox<T>>
165    where
166        T: PartialOrd + Copy,
167    {
168        if self.left > other.right
169            || self.right < other.left
170            || self.bottom > other.top
171            || self.top < other.bottom
172        {
173            None
174        } else {
175            let left = if self.left > other.left { self.left } else { other.left };
176            let bottom = if self.bottom > other.bottom { self.bottom } else { other.bottom };
177            let right = if self.right < other.right { self.right } else { other.right };
178            let top = if self.top < other.top { self.top } else { other.top };
179
180            Some(BBox { left, bottom, right, top })
181        }
182    }
183
184    /// Clips the bounding box along an axis
185    pub fn clip(&self, axis: Axis, k1: T, k2: T) -> BBox<T>
186    where
187        T: PartialOrd + Copy,
188    {
189        let mut new_bbox = *self;
190        if axis == Axis::X {
191            new_bbox.left = if new_bbox.left > k1 { new_bbox.left } else { k1 };
192            new_bbox.right = if new_bbox.right < k2 { new_bbox.right } else { k2 };
193        } else {
194            new_bbox.bottom = if new_bbox.bottom > k1 { new_bbox.bottom } else { k1 };
195            new_bbox.top = if new_bbox.top < k2 { new_bbox.top } else { k2 };
196        }
197
198        new_bbox
199    }
200
201    /// Checks if this bounding box is inside another
202    pub fn inside(&self, other: &BBox<T>) -> bool
203    where
204        T: PartialOrd + Copy,
205    {
206        self.left >= other.left
207            && self.right <= other.right
208            && self.bottom >= other.bottom
209            && self.top <= other.top
210    }
211
212    /// Returns the area of the bounding box
213    pub fn area(&self) -> T
214    where
215        T: Sub<Output = T> + Mul<Output = T> + Copy,
216    {
217        (self.right - self.left) * (self.top - self.bottom)
218    }
219}
220impl BBox<f64> {
221    /// Creates a new BBox from a point
222    pub fn from_point<P: GetXY>(point: &P) -> Self {
223        BBox::new(point.x(), point.y(), point.x(), point.y())
224    }
225
226    /// Creates a new BBox from a linestring
227    pub fn from_linestring<P: GetXY>(line: &[P]) -> Self {
228        let mut bbox = BBox::default();
229        for point in line {
230            bbox.extend_from_point(point);
231        }
232        bbox
233    }
234
235    /// Creates a new BBox from a multi-linestring
236    pub fn from_multi_linestring<P: GetXY>(lines: &[Vec<P>]) -> Self {
237        let mut bbox = BBox::default();
238        for line in lines {
239            for point in line {
240                bbox.extend_from_point(point);
241            }
242        }
243        bbox
244    }
245
246    /// Creates a new BBox from a polygon
247    pub fn from_polygon<P: GetXY>(polygon: &[Vec<P>]) -> Self {
248        Self::from_multi_linestring(polygon)
249    }
250
251    /// Creates a new BBox from a multi-polygon
252    pub fn from_multi_polygon<P: GetXY>(polygons: &[Vec<Vec<P>>]) -> Self {
253        let mut bbox = BBox::default();
254        for polygon in polygons {
255            for line in polygon {
256                for point in line {
257                    bbox.extend_from_point(point);
258                }
259            }
260        }
261        bbox
262    }
263
264    /// Extends the bounding box with a point
265    pub fn extend_from_point<P: GetXY>(&mut self, point: &P) {
266        self.merge_in_place(&BBox::from_point(point));
267    }
268
269    /// Creates a new BBox from zoom-uv coordinates
270    pub fn from_uv_zoom(u: f64, v: f64, zoom: u8) -> Self {
271        let division_factor = 2. / (1 << zoom) as f64;
272
273        BBox {
274            left: division_factor * u - 1.0,
275            bottom: division_factor * v - 1.0,
276            right: division_factor * (u + 1.0) - 1.0,
277            top: division_factor * (v + 1.0) - 1.0,
278        }
279    }
280
281    /// Creates a new BBox from zoom-st coordinates
282    pub fn from_st_zoom(s: f64, t: f64, zoom: u8) -> Self {
283        let division_factor = (2. / (1 << zoom) as f64) * 0.5;
284
285        BBox {
286            left: division_factor * s,
287            bottom: division_factor * t,
288            right: division_factor * (s + 1.),
289            top: division_factor * (t + 1.),
290        }
291    }
292}
293impl<T> Serialize for BBox<T>
294where
295    T: Serialize + Copy,
296{
297    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
298    where
299        S: Serializer,
300    {
301        let mut seq = serializer.serialize_tuple(4)?;
302        seq.serialize_element(&self.left)?;
303        seq.serialize_element(&self.bottom)?;
304        seq.serialize_element(&self.right)?;
305        seq.serialize_element(&self.top)?;
306        seq.end()
307    }
308}
309impl<T: Copy> From<BBox3D<T>> for BBox<T> {
310    fn from(bbox: BBox3D<T>) -> Self {
311        BBox::new(bbox.left, bbox.bottom, bbox.right, bbox.top)
312    }
313}
314impl<'de, T> Deserialize<'de> for BBox<T>
315where
316    T: Deserialize<'de> + Copy,
317{
318    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
319    where
320        D: Deserializer<'de>,
321    {
322        struct BBoxVisitor<T> {
323            marker: core::marker::PhantomData<T>,
324        }
325
326        impl<'de, T> Visitor<'de> for BBoxVisitor<T>
327        where
328            T: Deserialize<'de> + Copy,
329        {
330            type Value = BBox<T>;
331
332            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
333                formatter.write_str("a sequence of four numbers")
334            }
335
336            fn visit_seq<V>(self, mut seq: V) -> Result<BBox<T>, V::Error>
337            where
338                V: SeqAccess<'de>,
339            {
340                let left =
341                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(0, &self))?;
342                let bottom =
343                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?;
344                let right =
345                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?;
346                let top = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(3, &self))?;
347                Ok(BBox { left, bottom, right, top })
348            }
349        }
350
351        deserializer.deserialize_tuple(4, BBoxVisitor { marker: core::marker::PhantomData })
352    }
353}
354
355/// A BBOX is defined in lon-lat space and helps with zooming motion to
356/// see the entire 3D line or polygon
357#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
358pub struct BBox3D<T = f64> {
359    /// left most longitude (WM) or S (S2)
360    pub left: T,
361    /// bottom most latitude (WM) or T (S2)
362    pub bottom: T,
363    /// right most longitude (WM) or T (S2)
364    pub right: T,
365    /// top most latitude (WM) or S (S2)
366    pub top: T,
367    /// near most height (WM) or T (S2)
368    /// generic height is relative to the surface of the earth in meters
369    pub near: T,
370    /// far most height (WM) or T (S2)
371    /// generic height is relative to the surface of the earth in meters
372    pub far: T,
373}
374impl<T> From<BBox3D<T>> for MValue
375where
376    T: Into<ValueType>,
377{
378    fn from(bbox: BBox3D<T>) -> MValue {
379        MValue::from([
380            ("left".into(), bbox.left.into()),
381            ("bottom".into(), bbox.bottom.into()),
382            ("right".into(), bbox.right.into()),
383            ("top".into(), bbox.top.into()),
384            ("near".into(), bbox.near.into()),
385            ("far".into(), bbox.far.into()),
386        ])
387    }
388}
389impl<T> From<&BBox3D<T>> for MValue
390where
391    T: Copy + Into<ValueType>,
392{
393    fn from(bbox: &BBox3D<T>) -> MValue {
394        MValue::from([
395            ("left".into(), bbox.left.into()),
396            ("bottom".into(), bbox.bottom.into()),
397            ("right".into(), bbox.right.into()),
398            ("top".into(), bbox.top.into()),
399            ("near".into(), bbox.near.into()),
400            ("far".into(), bbox.far.into()),
401        ])
402    }
403}
404impl<T> From<MValue> for BBox3D<T>
405where
406    T: From<ValueType>,
407{
408    fn from(mvalue: MValue) -> Self {
409        BBox3D {
410            left: mvalue.get("left").unwrap().clone().into(),
411            bottom: mvalue.get("bottom").unwrap().clone().into(),
412            right: mvalue.get("right").unwrap().clone().into(),
413            top: mvalue.get("top").unwrap().clone().into(),
414            near: mvalue.get("near").unwrap().clone().into(),
415            far: mvalue.get("far").unwrap().clone().into(),
416        }
417    }
418}
419impl<T> From<&MValue> for BBox3D<T>
420where
421    T: From<ValueType>,
422{
423    fn from(mvalue: &MValue) -> Self {
424        BBox3D {
425            left: mvalue.get("left").unwrap().clone().into(),
426            bottom: mvalue.get("bottom").unwrap().clone().into(),
427            right: mvalue.get("right").unwrap().clone().into(),
428            top: mvalue.get("top").unwrap().clone().into(),
429            near: mvalue.get("near").unwrap().clone().into(),
430            far: mvalue.get("far").unwrap().clone().into(),
431        }
432    }
433}
434impl<T> MValueCompatible for BBox3D<T>
435where
436    ValueType: From<T>,
437    T: From<ValueType> + Default + Bounded + Copy + Interpolate,
438{
439}
440impl<T> BBox3D<T> {
441    /// Creates a new BBox3D
442    pub fn new(left: T, bottom: T, right: T, top: T, near: T, far: T) -> Self
443    where
444        T: Copy,
445    {
446        BBox3D { left, bottom, right, top, near, far }
447    }
448
449    /// Checks if a point is within the BBox
450    pub fn point_overlap<P: GetXY + GetZ>(&self, point: &P) -> bool
451    where
452        T: Into<f64> + Copy, // Ensures that comparison operators work for type T
453    {
454        let z = point.z().unwrap_or_default();
455        point.x() >= self.left.into()
456            && point.x() <= self.right.into()
457            && point.y() >= self.bottom.into()
458            && point.y() <= self.top.into()
459            && z >= self.near.into()
460            && z <= self.far.into()
461    }
462
463    /// Merges another bounding box with this one
464    pub fn merge(&self, other: &BBox3D<T>) -> BBox3D<T>
465    where
466        T: PartialOrd + Copy,
467    {
468        let mut new_bbox = *self;
469        new_bbox.left = if new_bbox.left < other.left { new_bbox.left } else { other.left };
470        new_bbox.bottom =
471            if new_bbox.bottom < other.bottom { new_bbox.bottom } else { other.bottom };
472        new_bbox.right = if new_bbox.right > other.right { new_bbox.right } else { other.right };
473        new_bbox.top = if new_bbox.top > other.top { new_bbox.top } else { other.top };
474        new_bbox.near = if new_bbox.near < other.near { new_bbox.near } else { other.near };
475        new_bbox.far = if new_bbox.far > other.far { new_bbox.far } else { other.far };
476
477        new_bbox
478    }
479
480    /// Merges in place another bounding box with this one
481    pub fn merge_in_place(&mut self, other: &Self)
482    where
483        T: PartialOrd + Copy,
484    {
485        self.left = if self.left < other.left { self.left } else { other.left };
486        self.bottom = if self.bottom < other.bottom { self.bottom } else { other.bottom };
487        self.right = if self.right > other.right { self.right } else { other.right };
488        self.top = if self.top > other.top { self.top } else { other.top };
489        self.near = if self.near < other.near { self.near } else { other.near };
490        self.far = if self.far > other.far { self.far } else { other.far };
491    }
492
493    /// Checks if another bounding box overlaps with this one and returns the overlap
494    pub fn overlap(&self, other: &BBox3D<T>) -> Option<BBox3D<T>>
495    where
496        T: PartialOrd + Copy,
497    {
498        if self.left > other.right
499            || self.right < other.left
500            || self.bottom > other.top
501            || self.top < other.bottom
502            || self.near > other.far
503            || self.far < other.near
504        {
505            None
506        } else {
507            let left = if self.left > other.left { self.left } else { other.left };
508            let bottom = if self.bottom > other.bottom { self.bottom } else { other.bottom };
509            let right = if self.right < other.right { self.right } else { other.right };
510            let top = if self.top < other.top { self.top } else { other.top };
511
512            let near = if self.near > other.near { self.near } else { other.near };
513            let far = if self.far < other.far { self.far } else { other.far };
514
515            Some(BBox3D { left, bottom, right, top, near, far })
516        }
517    }
518
519    /// Clips the bounding box along an axis
520    pub fn clip(&self, axis: Axis, k1: T, k2: T) -> BBox3D<T>
521    where
522        T: PartialOrd + Copy,
523    {
524        let mut new_bbox = *self;
525        if axis == Axis::X {
526            new_bbox.left = if new_bbox.left > k1 { new_bbox.left } else { k1 };
527            new_bbox.right = if new_bbox.right < k2 { new_bbox.right } else { k2 };
528        } else {
529            new_bbox.bottom = if new_bbox.bottom > k1 { new_bbox.bottom } else { k1 };
530            new_bbox.top = if new_bbox.top < k2 { new_bbox.top } else { k2 };
531        }
532
533        new_bbox
534    }
535
536    /// Creates a new BBox3D from a BBox
537    pub fn from_bbox(bbox: &BBox<T>) -> Self
538    where
539        T: Copy + Default,
540    {
541        BBox3D::new(bbox.left, bbox.bottom, bbox.right, bbox.top, T::default(), T::default())
542    }
543
544    /// Checks if this bounding box is inside another
545    pub fn inside(&self, other: &BBox3D<T>) -> bool
546    where
547        T: PartialOrd + Copy,
548    {
549        self.left >= other.left
550            && self.right <= other.right
551            && self.bottom >= other.bottom
552            && self.top <= other.top
553            && self.near >= other.near
554            && self.far <= other.far
555    }
556
557    /// Returns the area of the bounding box
558    pub fn area(&self) -> T
559    where
560        T: Sub<Output = T> + Mul<Output = T> + MulAssign + Into<f64> + Copy,
561    {
562        let mut res = (self.right - self.left) * (self.top - self.bottom);
563        if self.far.into() != 0. || self.near.into() != 0. {
564            res *= self.far - self.near
565        }
566
567        res
568    }
569}
570impl<T> Serialize for BBox3D<T>
571where
572    T: Serialize + Copy,
573{
574    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
575    where
576        S: Serializer,
577    {
578        let mut seq = serializer.serialize_tuple(6)?;
579        seq.serialize_element(&self.left)?;
580        seq.serialize_element(&self.bottom)?;
581        seq.serialize_element(&self.right)?;
582        seq.serialize_element(&self.top)?;
583        seq.serialize_element(&self.near)?;
584        seq.serialize_element(&self.far)?;
585        seq.end()
586    }
587}
588impl<T> Default for BBox3D<T>
589where
590    T: Default + Bounded + Copy,
591{
592    fn default() -> Self {
593        BBox3D::new(
594            T::max_value(),
595            T::max_value(),
596            T::min_value(),
597            T::min_value(),
598            T::max_value(),
599            T::min_value(),
600        )
601    }
602}
603impl BBox3D<f64> {
604    /// Creates a new BBox3D from a point
605    pub fn from_point<P: GetXY + GetZ>(point: &P) -> Self {
606        BBox3D::new(
607            point.x(),
608            point.y(),
609            point.x(),
610            point.y(),
611            point.z().unwrap_or(f64::MAX),
612            point.z().unwrap_or(f64::MIN),
613        )
614    }
615
616    /// Creates a new BBox from a linestring
617    pub fn from_linestring<P: GetXY + GetZ>(line: &[P]) -> Self {
618        let mut bbox = BBox3D::default();
619        for point in line {
620            bbox.extend_from_point(point);
621        }
622        bbox
623    }
624
625    /// Creates a new BBox from a multi-linestring
626    pub fn from_multi_linestring<P: GetXY + GetZ>(lines: &[Vec<P>]) -> Self {
627        let mut bbox = BBox3D::default();
628        for line in lines {
629            for point in line {
630                bbox.extend_from_point(point);
631            }
632        }
633        bbox
634    }
635
636    /// Creates a new BBox from a polygon
637    pub fn from_polygon<P: GetXY + GetZ>(polygon: &[Vec<P>]) -> Self {
638        Self::from_multi_linestring(polygon)
639    }
640
641    /// Creates a new BBox from a multi-polygon
642    pub fn from_multi_polygon<P: GetXY + GetZ>(polygons: &[Vec<Vec<P>>]) -> Self {
643        let mut bbox = BBox3D::default();
644        for polygon in polygons {
645            for line in polygon {
646                for point in line {
647                    bbox.extend_from_point(point);
648                }
649            }
650        }
651        bbox
652    }
653
654    /// Extends the bounding box with a point
655    pub fn extend_from_point<P: GetXY + GetZ>(&mut self, point: &P) {
656        self.merge_in_place(&BBox3D::from_point(point));
657    }
658
659    /// Creates a new BBox3D from zoom-uv coordinates
660    pub fn from_uv_zoom(u: f64, v: f64, zoom: u8) -> Self {
661        let division_factor = 2. / (1 << zoom) as f64;
662
663        BBox3D {
664            left: division_factor * u - 1.0,
665            bottom: division_factor * v - 1.0,
666            right: division_factor * (u + 1.0) - 1.0,
667            top: division_factor * (v + 1.0) - 1.0,
668            near: f64::MAX,
669            far: f64::MIN,
670        }
671    }
672
673    /// Creates a new BBox from zoom-st coordinates
674    pub fn from_st_zoom(s: f64, t: f64, zoom: u8) -> Self {
675        let division_factor = (2. / (1 << zoom) as f64) * 0.5;
676
677        BBox3D {
678            left: division_factor * s,
679            bottom: division_factor * t,
680            right: division_factor * (s + 1.),
681            top: division_factor * (t + 1.),
682            near: f64::MAX,
683            far: f64::MIN,
684        }
685    }
686}
687impl<T: Default + Copy> From<BBox<T>> for BBox3D<T> {
688    fn from(bbox: BBox<T>) -> Self {
689        BBox3D::from_bbox(&bbox)
690    }
691}
692impl<'de, T> Deserialize<'de> for BBox3D<T>
693where
694    T: Deserialize<'de> + Copy,
695{
696    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
697    where
698        D: Deserializer<'de>,
699    {
700        struct BBox3DVisitor<T> {
701            marker: core::marker::PhantomData<T>,
702        }
703
704        impl<'de, T> Visitor<'de> for BBox3DVisitor<T>
705        where
706            T: Deserialize<'de> + Copy,
707        {
708            type Value = BBox3D<T>;
709
710            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
711                formatter.write_str("a sequence of six numbers")
712            }
713
714            fn visit_seq<V>(self, mut seq: V) -> Result<BBox3D<T>, V::Error>
715            where
716                V: SeqAccess<'de>,
717            {
718                let left =
719                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(0, &self))?;
720                let bottom =
721                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?;
722                let right =
723                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?;
724                let top = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(3, &self))?;
725                let near =
726                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(4, &self))?;
727                let far = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(5, &self))?;
728                Ok(BBox3D { left, bottom, right, top, near, far })
729            }
730        }
731
732        deserializer.deserialize_tuple(6, BBox3DVisitor { marker: core::marker::PhantomData })
733    }
734}
735
736/// BBox or BBox3D
737#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)]
738pub enum BBOX {
739    /// 2D bounding box
740    BBox(BBox),
741    /// 3D bounding box
742    BBox3D(BBox3D),
743}
744impl Default for BBOX {
745    fn default() -> Self {
746        BBOX::BBox(BBox::default())
747    }
748}
749impl From<BBox> for BBOX {
750    fn from(bbox: BBox) -> Self {
751        BBOX::BBox(bbox)
752    }
753}
754impl From<BBox3D> for BBOX {
755    fn from(bbox: BBox3D) -> Self {
756        BBOX::BBox3D(bbox)
757    }
758}
759impl Eq for BBOX {}
760impl PartialOrd for BBOX {
761    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
762        Some(self.cmp(other))
763    }
764}
765impl Ord for BBOX {
766    fn cmp(&self, other: &Self) -> Ordering {
767        match (self, other) {
768            (BBOX::BBox(a), BBOX::BBox(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
769            (BBOX::BBox3D(a), BBOX::BBox3D(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
770            // Ensure that BBox and BBox3D are ordered correctly
771            (BBOX::BBox(_), BBOX::BBox3D(_)) => Ordering::Less,
772            (BBOX::BBox3D(_), BBOX::BBox(_)) => Ordering::Greater,
773        }
774    }
775}