egml_core/model/geometry/
envelope.rs1use 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 pub fn contains_envelope(&self, envelope: &Envelope) -> bool {
50 self.contains(&envelope.lower_corner) && self.contains(&envelope.upper_corner)
51 }
52
53 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}