Skip to main content

egml_core/model/geometry/primitives/
surface.rs

1use crate::Error;
2use crate::impl_has_geometry_type;
3use crate::model::common::{
4    ApplyTransform, ComputeEnvelope, IterGeometries, Triangulate, Triangulation,
5};
6use crate::model::geometry::primitives::{
7    AbstractSurface, AbstractSurfacePatchKind, AsAbstractSurface, AsAbstractSurfaceMut,
8    TriangulatedSurface,
9};
10use crate::model::geometry::refs::AbstractGeometryKindRef;
11use crate::model::geometry::{DirectPosition, Envelope};
12use nalgebra::{Isometry3, Rotation3, Scale3, Transform3, Vector3};
13
14/// A 2-D geometry composed of one or more surface patches.
15///
16/// Corresponds to `gml:Surface` in [OGC 07-036 ยง10.5.10](https://docs.ogc.org/is/07-036/07-036.pdf).  Patches are stored as
17/// a list of [`AbstractSurfacePatchKind`] and may be of mixed kinds (polygons, triangles).
18#[derive(Debug, Clone, PartialEq)]
19pub struct Surface {
20    pub abstract_surface: AbstractSurface,
21    patches: Vec<AbstractSurfacePatchKind>,
22}
23
24impl Surface {
25    /// Creates a new `Surface` from a patch array.
26    pub fn new(patches: Vec<AbstractSurfacePatchKind>) -> Self {
27        Surface {
28            abstract_surface: AbstractSurface::default(),
29            patches,
30        }
31    }
32
33    pub fn from_abstract_surface(
34        abstract_surface: AbstractSurface,
35        patches: Vec<AbstractSurfacePatchKind>,
36    ) -> Self {
37        Self {
38            abstract_surface,
39            patches,
40        }
41    }
42
43    pub fn patches(&self) -> &[AbstractSurfacePatchKind] {
44        &self.patches
45    }
46}
47
48/// Object-safe read accessor for [`Surface`] data.
49pub trait AsSurface: AsAbstractSurface {
50    /// Returns a reference to the underlying [`Surface`].
51    fn surface(&self) -> &Surface;
52
53    /// Returns the patch array of this surface.
54    fn patches(&self) -> &[AbstractSurfacePatchKind] {
55        &self.surface().patches
56    }
57
58    fn patches_len(&self) -> usize {
59        self.patches().len()
60    }
61}
62
63/// Mutable companion to [`AsSurface`].
64pub trait AsSurfaceMut: AsSurface + AsAbstractSurfaceMut {
65    /// Returns a mutable reference to the underlying [`Surface`].
66    fn surface_mut(&mut self) -> &mut Surface;
67
68    fn patches_mut(&mut self) -> &mut Vec<AbstractSurfacePatchKind> {
69        &mut self.surface_mut().patches
70    }
71}
72
73impl AsSurface for Surface {
74    fn surface(&self) -> &Surface {
75        self
76    }
77}
78
79impl AsSurfaceMut for Surface {
80    fn surface_mut(&mut self) -> &mut Surface {
81        self
82    }
83}
84
85#[macro_export]
86macro_rules! impl_surface_traits {
87    ($type:ty) => {
88        $crate::impl_abstract_surface_traits!($type);
89
90        impl $crate::model::geometry::primitives::AsAbstractSurface for $type {
91            fn abstract_surface(&self) -> &$crate::model::geometry::primitives::AbstractSurface {
92                &<$type as $crate::model::geometry::primitives::AsSurface>::surface(self)
93                    .abstract_surface
94            }
95        }
96    };
97}
98
99#[macro_export]
100macro_rules! impl_surface_mut_traits {
101    ($type:ty) => {
102        $crate::impl_abstract_surface_mut_traits!($type);
103
104        impl $crate::model::geometry::primitives::AsAbstractSurfaceMut for $type {
105            fn abstract_surface_mut(
106                &mut self,
107            ) -> &mut $crate::model::geometry::primitives::AbstractSurface {
108                &mut <$type as $crate::model::geometry::primitives::AsSurfaceMut>::surface_mut(self)
109                    .abstract_surface
110            }
111        }
112    };
113}
114
115impl_surface_traits!(Surface);
116impl_surface_mut_traits!(Surface);
117impl_has_geometry_type!(Surface, Surface);
118
119impl Surface {
120    pub(crate) fn into_patches(self) -> Vec<AbstractSurfacePatchKind> {
121        self.patches
122    }
123
124    pub fn area_3d(&self) -> Result<f64, Error> {
125        self.patches
126            .iter()
127            .map(|p| p.area_3d())
128            .collect::<Result<Vec<f64>, Error>>()
129            .map(|area_3ds| area_3ds.into_iter().sum())
130    }
131
132    /// Returns the positions of all patches, in patch order.
133    pub fn points(&self) -> Vec<&DirectPosition> {
134        self.patches.iter().flat_map(|p| p.points()).collect()
135    }
136}
137
138impl IterGeometries for Surface {
139    fn iter_geometries(&self) -> Box<dyn Iterator<Item = AbstractGeometryKindRef<'_>> + '_> {
140        Box::new(std::iter::once(self.into()))
141    }
142}
143
144impl ApplyTransform for Surface {
145    fn apply_transform(&mut self, transform: Transform3<f64>) {
146        self.patches
147            .iter_mut()
148            .for_each(|x| x.apply_transform(transform));
149    }
150
151    fn apply_isometry(&mut self, isometry: Isometry3<f64>) {
152        self.patches
153            .iter_mut()
154            .for_each(|x| x.apply_isometry(isometry));
155    }
156
157    fn apply_translation(&mut self, vector: Vector3<f64>) {
158        self.patches
159            .iter_mut()
160            .for_each(|x| x.apply_translation(vector));
161    }
162
163    fn apply_rotation(&mut self, rotation: Rotation3<f64>) {
164        self.patches
165            .iter_mut()
166            .for_each(|x| x.apply_rotation(rotation));
167    }
168
169    fn apply_scale(&mut self, scale: Scale3<f64>) {
170        self.patches.iter_mut().for_each(|x| x.apply_scale(scale));
171    }
172}
173
174impl ComputeEnvelope for Surface {
175    /// Returns the union of the bounding boxes of all patches.
176    fn compute_envelope(&self) -> Option<Envelope> {
177        let envelopes: Vec<Envelope> = self
178            .patches
179            .iter()
180            .flat_map(|x| x.compute_envelope())
181            .collect();
182
183        Envelope::from_envelopes(&envelopes)
184    }
185}
186
187impl Triangulate for Surface {
188    /// Patches that fail to triangulate individually (e.g. a degenerate ring) are
189    /// skipped rather than failing the whole surface; see their errors via
190    /// [`Triangulation::skipped`].
191    ///
192    /// # Errors
193    ///
194    /// Returns [`Error::TooFewElements`] if no patch could be triangulated.
195    fn triangulate(&self) -> Result<Triangulation, Error> {
196        let mut surfaces = Vec::new();
197        let mut skipped = Vec::new();
198
199        for patch in &self.patches {
200            match patch.triangulate() {
201                Ok(triangulation) => {
202                    let (surface, nested_skipped) = triangulation.into_parts();
203                    surfaces.push(surface);
204                    skipped.extend(nested_skipped);
205                }
206                Err(error) => {
207                    skipped.push(error);
208                }
209            }
210        }
211
212        let combined = TriangulatedSurface::from_triangulated_surfaces(surfaces)?;
213        Ok(Triangulation::new(combined, skipped))
214    }
215}
216
217#[cfg(test)]
218mod tests {
219    use super::*;
220    use crate::model::geometry::primitives::{
221        AbstractRingKind, AbstractSurfacePatchKind, LinearRing, PolygonPatch, Triangle,
222    };
223
224    fn pos(x: f64, y: f64, z: f64) -> DirectPosition {
225        DirectPosition::new(x, y, z).unwrap()
226    }
227
228    #[test]
229    fn points_collects_all_patches() {
230        let square = LinearRing::new([
231            pos(0., 0., 0.),
232            pos(1., 0., 0.),
233            pos(1., 1., 0.),
234            pos(0., 1., 0.),
235        ])
236        .unwrap();
237        let patch = PolygonPatch::new(Some(AbstractRingKind::LinearRing(square)), []);
238        let triangle =
239            Triangle::from_points(pos(0., 0., 1.), pos(1., 0., 1.), pos(0., 1., 1.)).unwrap();
240        let surface = Surface::new(vec![
241            AbstractSurfacePatchKind::PolygonPatch(patch),
242            AbstractSurfacePatchKind::Triangle(triangle),
243        ]);
244
245        let points = surface.points();
246        assert_eq!(points.len(), 7);
247        assert_eq!(points[0], &pos(0., 0., 0.));
248        assert_eq!(points[4], &pos(0., 0., 1.));
249    }
250}