egml_core/model/geometry/
envelope.rs

1use crate::error::Error;
2use crate::error::Error::LowerCornerMustBeEqualOrBelowUpperCorner;
3use crate::model::geometry::DirectPosition;
4use nalgebra::{Point3, Vector3};
5
6#[derive(Debug, Clone, Copy, PartialEq, Default)]
7pub struct Envelope {
8    lower_corner: DirectPosition,
9    upper_corner: DirectPosition,
10}
11
12impl Envelope {
13    pub fn new(lower_corner: DirectPosition, upper_corner: DirectPosition) -> Result<Self, Error> {
14        let lower_corner_point: Point3<f64> = lower_corner.into();
15        let upper_corner_point: Point3<f64> = upper_corner.into();
16        if lower_corner_point > upper_corner_point {
17            return Err(LowerCornerMustBeEqualOrBelowUpperCorner(""));
18        }
19
20        Ok(Self {
21            lower_corner,
22            upper_corner,
23        })
24    }
25
26    pub fn lower_corner(&self) -> &DirectPosition {
27        &self.lower_corner
28    }
29
30    pub fn upper_corner(&self) -> &DirectPosition {
31        &self.upper_corner
32    }
33
34    pub fn size(&self) -> Vector3<f64> {
35        let lower_corner_point: Point3<f64> = self.lower_corner.into();
36        let upper_corner_point: Point3<f64> = self.upper_corner.into();
37        upper_corner_point - lower_corner_point
38    }
39
40    pub fn contains(&self, point: &DirectPosition) -> bool {
41        let lower_corner: Point3<f64> = self.lower_corner.into();
42        let upper_corner: Point3<f64> = self.upper_corner.into();
43        let point: Point3<f64> = (*point).into();
44
45        lower_corner <= point && point <= upper_corner
46    }
47
48    /// Returns `true` if envelope is fully contained.
49    pub fn contains_envelope(&self, envelope: &Envelope) -> bool {
50        self.contains(&envelope.lower_corner) && self.contains(&envelope.upper_corner)
51    }
52
53    /// Returns `true` if envelope is fully contained.
54    pub fn contains_envelope_partially(&self, envelope: &Envelope) -> bool {
55        self.contains(&envelope.lower_corner) || self.contains(&envelope.upper_corner)
56    }
57
58    pub fn enlarge(&self, distance: f64) -> Result<Envelope, Error> {
59        let lower_corner = DirectPosition::new(
60            self.lower_corner.x() - distance,
61            self.lower_corner.y() - distance,
62            self.lower_corner.z() - distance,
63        )?;
64        let upper_corner = DirectPosition::new(
65            self.upper_corner.x() + distance,
66            self.upper_corner.y() + distance,
67            self.upper_corner.z() + distance,
68        )?;
69
70        let envelope = Envelope::new(lower_corner, upper_corner)?;
71        Ok(envelope)
72    }
73}
74
75impl Envelope {
76    pub fn from_envelopes(envelopes: &Vec<Self>) -> Result<Self, Error> {
77        if envelopes.is_empty() {
78            return Err(Error::MustNotBeEmpty("envelopes"));
79        }
80
81        let x_min: f64 = envelopes
82            .iter()
83            .map(|e| e.lower_corner)
84            .min_by(|a, b| a.x().partial_cmp(&b.x()).unwrap())
85            .unwrap()
86            .x();
87        let y_min = envelopes
88            .iter()
89            .map(|e| e.lower_corner)
90            .min_by(|a, b| a.y().partial_cmp(&b.y()).unwrap())
91            .unwrap()
92            .y();
93        let z_min = envelopes
94            .iter()
95            .map(|e| e.lower_corner)
96            .min_by(|a, b| a.z().partial_cmp(&b.z()).unwrap())
97            .unwrap()
98            .z();
99        let lower_corner = DirectPosition::new(x_min, y_min, z_min).unwrap();
100
101        let x_max: f64 = envelopes
102            .iter()
103            .map(|e| e.upper_corner)
104            .max_by(|a, b| a.x().partial_cmp(&b.x()).unwrap())
105            .unwrap()
106            .x();
107        let y_max = envelopes
108            .iter()
109            .map(|e| e.upper_corner)
110            .max_by(|a, b| a.y().partial_cmp(&b.y()).unwrap())
111            .unwrap()
112            .y();
113        let z_max = envelopes
114            .iter()
115            .map(|e| e.upper_corner)
116            .max_by(|a, b| a.z().partial_cmp(&b.z()).unwrap())
117            .unwrap()
118            .z();
119        let upper_corner = DirectPosition::new(x_max, y_max, z_max).unwrap();
120
121        let envelope = Envelope::new(lower_corner, upper_corner)?;
122        Ok(envelope)
123    }
124
125    pub fn from_optional_envelopes(
126        envelopes: &Vec<Option<Envelope>>,
127    ) -> Result<Option<Self>, Error> {
128        let flattened_envelopes: Vec<Envelope> = envelopes.iter().cloned().flatten().collect();
129        if flattened_envelopes.is_empty() {
130            return Ok(None);
131        }
132
133        Self::from_envelopes(&flattened_envelopes).map(Some)
134    }
135}