egml_core/model/geometry/primitives/
triangle.rs1use 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.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}