Skip to main content

egml_core/model/geometry/primitives/
triangulated_surface.rs

1use crate::error::Error;
2use crate::model::common::{ApplyTransform, ComputeEnvelope, IterGeometries};
3use crate::model::geometry::primitives::abstract_surface_patch_kind::AbstractSurfacePatchKind;
4use crate::model::geometry::primitives::{
5    AbstractSurfacePatchArrayProperty, AsSurface, AsSurfaceMut, Surface, Triangle,
6};
7use crate::model::geometry::refs::AbstractGeometryKindRef;
8use crate::model::geometry::{DirectPosition, Envelope};
9use crate::{impl_has_geometry_type, impl_surface_mut_traits, impl_surface_traits};
10use nalgebra::{Isometry3, Rotation3, Scale3, Transform3, Vector3};
11
12/// A 2-D surface composed exclusively of [`Triangle`] patches.
13///
14/// Corresponds to `gml:TriangulatedSurface` in [OGC 07-036 §10.5.11.4](https://docs.ogc.org/is/07-036/07-036.pdf).
15/// This type is the primary output of triangulation operations.
16#[derive(Debug, Clone, PartialEq)]
17pub struct TriangulatedSurface {
18    surface: Surface,
19}
20
21impl TriangulatedSurface {
22    /// Creates a new `TriangulatedSurface` from an existing [`Surface`].
23    pub fn new(surface: Surface) -> Result<Self, Error> {
24        Ok(TriangulatedSurface { surface })
25    }
26
27    pub fn surface(&self) -> &Surface {
28        &self.surface
29    }
30}
31
32impl TriangulatedSurface {
33    /// Creates a `TriangulatedSurface` from a flat list of triangles.
34    ///
35    /// # Errors
36    ///
37    /// Returns [`Error::TooFewElements`] if `triangles` is empty.
38    pub fn from_triangles(triangles: Vec<Triangle>) -> Result<Self, Error> {
39        Self::validate_triangles(&triangles)?;
40
41        let patches: Vec<AbstractSurfacePatchKind> = triangles
42            .into_iter()
43            .map(AbstractSurfacePatchKind::Triangle)
44            .collect();
45        let surface_patch_array_property: AbstractSurfacePatchArrayProperty =
46            AbstractSurfacePatchArrayProperty::from_objects(patches);
47
48        Self::new(Surface::new(surface_patch_array_property))
49    }
50
51    /// Merges multiple triangulated surfaces into one by combining all their patches.
52    ///
53    /// # Errors
54    ///
55    /// Returns [`Error::TooFewElements`] if `surfaces` is empty.
56    pub fn from_triangulated_surfaces(surfaces: Vec<TriangulatedSurface>) -> Result<Self, Error> {
57        Self::validate_surfaces(&surfaces)?;
58
59        let patches: Vec<AbstractSurfacePatchKind> = surfaces
60            .into_iter()
61            .flat_map(|surface| {
62                let mut patch_array = surface.surface.into_patches();
63                std::mem::take(patch_array.objects_mut())
64            })
65            .collect();
66
67        let surface_patch_array_property: AbstractSurfacePatchArrayProperty =
68            AbstractSurfacePatchArrayProperty::from_objects(patches);
69
70        let surface = Surface::new(surface_patch_array_property);
71        Ok(TriangulatedSurface { surface })
72    }
73
74    fn validate_triangles(triangles: &[Triangle]) -> Result<(), Error> {
75        if triangles.is_empty() {
76            return Err(Error::TooFewElements {
77                geometry: "gml:TriangulatedSurface",
78                minimum: 1,
79                spec: Some("OGC 07-036 §10.5.11.4"),
80                id: None,
81                detail: None,
82            });
83        }
84        Ok(())
85    }
86
87    fn validate_surfaces(surfaces: &[TriangulatedSurface]) -> Result<(), Error> {
88        if surfaces.is_empty() {
89            return Err(Error::TooFewElements {
90                geometry: "TriangulatedSurface::from_triangulated_surfaces",
91                minimum: 1,
92                spec: None,
93                id: None,
94                detail: None,
95            });
96        }
97        Ok(())
98    }
99
100    /// Returns references to all [`Triangle`] patches in this surface.
101    pub fn triangles(&self) -> Vec<&Triangle> {
102        self.surface
103            .patches()
104            .objects()
105            .iter()
106            .filter_map(|patch| match patch {
107                AbstractSurfacePatchKind::Triangle(triangle) => Some(triangle),
108                _ => None,
109            })
110            .collect()
111    }
112
113    pub fn points(&self) -> Vec<&DirectPosition> {
114        self.surface.points()
115    }
116
117    pub fn area_3d(&self) -> Result<f64, Error> {
118        self.surface.area_3d()
119    }
120}
121
122impl AsSurface for TriangulatedSurface {
123    fn surface(&self) -> &Surface {
124        &self.surface
125    }
126}
127
128impl AsSurfaceMut for TriangulatedSurface {
129    fn surface_mut(&mut self) -> &mut Surface {
130        &mut self.surface
131    }
132}
133
134impl_surface_traits!(TriangulatedSurface);
135impl_surface_mut_traits!(TriangulatedSurface);
136impl_has_geometry_type!(TriangulatedSurface, TriangulatedSurface);
137
138impl IterGeometries for TriangulatedSurface {
139    fn iter_geometries(&self) -> Box<dyn Iterator<Item = AbstractGeometryKindRef<'_>> + '_> {
140        Box::new(std::iter::once(self.into()))
141    }
142}
143
144impl ApplyTransform for TriangulatedSurface {
145    fn apply_transform(&mut self, transform: Transform3<f64>) {
146        self.surface.apply_transform(transform);
147    }
148
149    fn apply_isometry(&mut self, isometry: Isometry3<f64>) {
150        self.surface.apply_isometry(isometry);
151    }
152
153    fn apply_translation(&mut self, vector: Vector3<f64>) {
154        self.surface.apply_translation(vector);
155    }
156
157    fn apply_rotation(&mut self, rotation: Rotation3<f64>) {
158        self.surface.apply_rotation(rotation);
159    }
160
161    fn apply_scale(&mut self, scale: Scale3<f64>) {
162        self.surface.apply_scale(scale);
163    }
164}
165
166impl ComputeEnvelope for TriangulatedSurface {
167    /// Returns the axis-aligned bounding box of all triangles.
168    fn compute_envelope(&self) -> Option<Envelope> {
169        self.surface.compute_envelope()
170    }
171}