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