Skip to main content

egml_core/model/geometry/primitives/
triangle.rs

1use crate::Error;
2use crate::model::common::{ApplyTransform, ComputeEnvelope, Triangulate, Triangulation};
3use crate::model::geometry::primitives::{
4    AbstractRingProperty, AbstractSurfacePatch, AsAbstractSurfacePatch, AsAbstractSurfacePatchMut,
5    LinearRing, TriangulatedSurface,
6};
7use crate::model::geometry::{DirectPosition, Envelope};
8use nalgebra::{Isometry3, Rotation3, Scale3, Transform3, Vector3};
9use parry3d_f64::query::PointQuery;
10
11#[derive(Debug, Clone, PartialEq)]
12pub struct Triangle {
13    pub abstract_surface_patch: AbstractSurfacePatch,
14    exterior: AbstractRingProperty,
15}
16
17impl Triangle {
18    pub fn new(exterior: AbstractRingProperty) -> Result<Self, Error> {
19        Self::validate(&exterior)?;
20
21        Ok(Self {
22            abstract_surface_patch: AbstractSurfacePatch::default(),
23            exterior,
24        })
25    }
26
27    pub(crate) fn new_unchecked(exterior: AbstractRingProperty) -> Self {
28        Self {
29            abstract_surface_patch: AbstractSurfacePatch::default(),
30            exterior,
31        }
32    }
33
34    pub fn from_abstract_surface_patch(
35        abstract_surface_patch: AbstractSurfacePatch,
36        exterior: AbstractRingProperty,
37    ) -> Result<Self, Error> {
38        Self::validate(&exterior)?;
39
40        Ok(Self {
41            abstract_surface_patch,
42            exterior,
43        })
44    }
45
46    pub fn from_points(
47        a: DirectPosition,
48        b: DirectPosition,
49        c: DirectPosition,
50    ) -> Result<Self, Error> {
51        let linear_ring = LinearRing::new([a, b, c])?;
52        let exterior = AbstractRingProperty::from_object(linear_ring.into());
53
54        Ok(Self {
55            abstract_surface_patch: AbstractSurfacePatch::default(),
56            exterior,
57        })
58    }
59
60    pub fn from_points_unchecked(a: DirectPosition, b: DirectPosition, c: DirectPosition) -> Self {
61        let linear_ring =
62            LinearRing::new([a, b, c]).expect("from points unchecked: LinearRing::new");
63        let exterior = AbstractRingProperty::from_object(linear_ring.into());
64
65        Self {
66            abstract_surface_patch: AbstractSurfacePatch::default(),
67            exterior,
68        }
69    }
70
71    fn validate(exterior: &AbstractRingProperty) -> Result<(), Error> {
72        if let Some(object) = exterior.object() {
73            let len = object.points().len();
74            if len != 3 {
75                return Err(Error::InvalidElementCount {
76                    geometry: "Triangle",
77                    expected: 3,
78                    actual: len,
79                    spec: Some("OGC 07-036 ยง10.5.12"),
80                });
81            }
82        }
83
84        Ok(())
85    }
86
87    pub fn exterior(&self) -> &AbstractRingProperty {
88        &self.exterior
89    }
90
91    pub fn a(&self) -> &DirectPosition {
92        &self.exterior.object().unwrap().points()[0]
93    }
94
95    pub fn b(&self) -> &DirectPosition {
96        &self.exterior.object().unwrap().points()[1]
97    }
98
99    pub fn c(&self) -> &DirectPosition {
100        &self.exterior.object().unwrap().points()[2]
101    }
102}
103
104impl AsAbstractSurfacePatch for Triangle {
105    fn abstract_surface_patch(&self) -> &AbstractSurfacePatch {
106        &self.abstract_surface_patch
107    }
108}
109
110impl AsAbstractSurfacePatchMut for Triangle {
111    fn abstract_surface_patch_mut(&mut self) -> &mut AbstractSurfacePatch {
112        &mut self.abstract_surface_patch
113    }
114}
115
116impl Triangle {
117    pub fn distance_to_local_point(&self, p: &DirectPosition) -> f64 {
118        let parry_triangle: parry3d_f64::shape::Triangle = self.clone().into();
119        let point: parry3d_f64::math::Vector = (*p).into();
120        parry_triangle.distance_to_local_point(point, false)
121    }
122
123    pub fn points(&self) -> Vec<&DirectPosition> {
124        vec![&self.a(), &self.b(), &self.c()]
125    }
126
127    pub fn area(&self) -> f64 {
128        let parry_triangle: parry3d_f64::shape::Triangle = self.clone().into();
129        // parry_triangle.distance_to_local_point().
130        parry_triangle.area()
131    }
132}
133
134impl ApplyTransform for Triangle {
135    fn apply_transform(&mut self, transform: Transform3<f64>) {
136        if let Some(object) = self.exterior.object_mut() {
137            object.apply_transform(transform);
138        }
139    }
140
141    fn apply_isometry(&mut self, isometry: Isometry3<f64>) {
142        if let Some(object) = self.exterior.object_mut() {
143            object.apply_isometry(isometry);
144        }
145    }
146
147    fn apply_translation(&mut self, vector: Vector3<f64>) {
148        if let Some(object) = self.exterior.object_mut() {
149            object.apply_translation(vector);
150        }
151    }
152
153    fn apply_rotation(&mut self, rotation: Rotation3<f64>) {
154        if let Some(object) = self.exterior.object_mut() {
155            object.apply_rotation(rotation);
156        }
157    }
158
159    fn apply_scale(&mut self, scale: Scale3<f64>) {
160        if let Some(object) = self.exterior.object_mut() {
161            object.apply_scale(scale);
162        }
163    }
164}
165
166impl ComputeEnvelope for Triangle {
167    fn compute_envelope(&self) -> Option<Envelope> {
168        self.exterior.object()?.compute_envelope()
169    }
170}
171
172impl Triangulate for Triangle {
173    fn triangulate(&self) -> Result<Triangulation, Error> {
174        let surface = TriangulatedSurface::from_triangles(vec![self.clone()])?;
175        Ok(Triangulation::new(surface, Vec::new()))
176    }
177}
178
179impl From<Triangle> for parry3d_f64::shape::Triangle {
180    fn from(item: Triangle) -> Self {
181        Self::new((*item.a()).into(), (*item.b()).into(), (*item.c()).into())
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    #[test]
190    fn triangle_construction_test() {
191        let linear_ring = LinearRing::new([
192            DirectPosition::new(0.0, 0.0, 0.0).unwrap(),
193            DirectPosition::new(1.0, 0.0, 0.0).unwrap(),
194            DirectPosition::new(1.0, 1.0, 0.0).unwrap(),
195            DirectPosition::new(1.0, 1.0, 1.0).unwrap(),
196        ])
197        .expect("should work");
198        let triangle_result = Triangle::new(AbstractRingProperty::from_object(linear_ring.into()));
199
200        assert!(matches!(
201            triangle_result,
202            Err(Error::InvalidElementCount { .. })
203        ));
204    }
205
206    #[test]
207    fn triangle_distance_test() {
208        let linear_ring = LinearRing::new(vec![
209            DirectPosition::new(0.0, 0.0, 0.0).unwrap(),
210            DirectPosition::new(1.0, 0.0, 0.0).unwrap(),
211            DirectPosition::new(1.0, 1.0, 0.0).unwrap(),
212        ])
213        .expect("LinearRing::new");
214        let triangle =
215            Triangle::new(AbstractRingProperty::from_object(linear_ring.into())).unwrap();
216
217        let distance =
218            triangle.distance_to_local_point(&DirectPosition::new(0.5, 0.5, 1.0).unwrap());
219
220        assert_eq!(distance, 1.0);
221    }
222}