procedural_modelling/mesh/
triangulation.rs1use crate::math::{IndexType, LineSegment2D, Polygon, Scalar, ScalarIteratorExt, Vector2D};
2use std::collections::{HashMap, HashSet};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct IndexedVertex2D<V: IndexType, Vec2: Vector2D> {
7 pub vec: Vec2,
9 pub index: V,
11}
12
13impl<V: IndexType, Vec2: Vector2D> IndexedVertex2D<V, Vec2> {
14 pub fn new(vec: Vec2, index: V) -> Self {
16 IndexedVertex2D { vec, index }
17 }
18
19 pub fn from_vector(vec: Vec<Vec2>) -> Vec<Self> {
21 vec.into_iter()
22 .enumerate()
23 .map(|(i, v)| IndexedVertex2D::new(v, V::new(i)))
24 .collect()
25 }
26}
27
28pub struct Triangulation<'a, V: IndexType> {
33 indices: &'a mut Vec<V>,
35
36 start: usize,
38}
39
40impl<V: IndexType> std::fmt::Debug for Triangulation<'_, V> {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 write!(
43 f,
44 "Triangulation({} triangles; start {})",
45 self.len(),
46 self.start
47 )?;
48 for i in 0..self.len() {
49 let (a, b, c) = self.get_triangle(i);
50 write!(f, "\n{} {} {}", a, b, c)?;
51 }
52 Ok(())
53 }
54}
55
56impl<'a, V: IndexType> Triangulation<'a, V> {
57 pub fn new(indices: &'a mut Vec<V>) -> Self {
59 Triangulation {
60 start: indices.len(),
61 indices,
62 }
63 }
64
65 pub fn insert_triangle(&mut self, a: V, b: V, c: V) {
67 self.indices.extend([a, b, c]);
68 }
69
70 pub fn get_index(&self, i: usize) -> V {
72 self.indices[self.start + i]
73 }
74
75 pub fn get_triangle(&self, i: usize) -> (V, V, V) {
77 (
78 self.indices[self.start + 3 * i],
79 self.indices[self.start + 3 * i + 1],
80 self.indices[self.start + 3 * i + 2],
81 )
82 }
83
84 pub fn get_triangle_area<Vec2: Vector2D>(
86 &self,
87 i: usize,
88 vec_hm: &HashMap<V, Vec2>,
89 ) -> Vec2::S {
90 let (i1, i2, i3) = self.get_triangle(i);
91 let v0 = vec_hm[&i1];
92 let v1 = vec_hm[&i2];
93 let v2 = vec_hm[&i3];
94
95 (v1.x() - v0.x()) * (v2.y() - v0.y()) - (v1.y() - v0.y()) * (v2.x() - v0.x())
97 }
98
99 pub fn insert_triangle_local<Vec2: Vector2D>(
101 &mut self,
102 a: usize,
103 b: usize,
104 c: usize,
105 vec2s: &Vec<IndexedVertex2D<V, Vec2>>,
106 ) {
107 self.indices
108 .extend([vec2s[a].index, vec2s[b].index, vec2s[c].index]);
109 }
110
111 pub fn map_indices(&mut self, id_map: &HashMap<V, V>) {
113 for i in self.start..self.indices.len() {
114 self.indices[i] = id_map[&self.indices[i]];
115 }
116 }
117
118 pub fn len(&self) -> usize {
120 let n = self.indices.len() - self.start;
121 assert!(n % 3 == 0, "Invalid number of indices in triangulation");
122 n / 3
123 }
124
125 pub fn next_pos(&self) -> usize {
127 self.indices.len()
128 }
129
130 pub fn flip_edge(
132 &mut self,
133 a: V,
134 b: V,
135 triangle_ab: usize,
136 triangle_ba: usize,
137 ) -> Result<(), ()> {
138 let offset_ab = if self.indices[triangle_ab + 0] == a {
139 0
140 } else if self.indices[triangle_ab + 1] == a {
141 1
142 } else {
143 2
144 };
145 if self.indices[triangle_ab + offset_ab] != a
146 || self.indices[triangle_ab + ((offset_ab + 1) % 3)] != b
147 {
148 return Err(());
149 }
150
151 let offset_ba = if self.indices[triangle_ba + 0] == a {
152 0
153 } else if self.indices[triangle_ba + 1] == a {
154 1
155 } else {
156 2
157 };
158 if self.indices[triangle_ba + offset_ba] != a
159 || self.indices[triangle_ba + ((offset_ba + 2) % 3)] != b
160 {
161 return Err(());
162 }
163
164 let c = self.indices[triangle_ab + ((offset_ab + 2) % 3)];
165 let d = self.indices[triangle_ba + ((offset_ba + 1) % 3)];
166
167 self.indices[triangle_ab + 0] = a;
171 self.indices[triangle_ab + 1] = d;
172 self.indices[triangle_ab + 2] = c;
173 self.indices[triangle_ba + 0] = d;
174 self.indices[triangle_ba + 1] = b;
175 self.indices[triangle_ba + 2] = c;
176
177 Ok(())
178 }
179
180 pub fn verify_non_degenerate_triangle<Vec2: Vector2D>(&self, vec_hm: &HashMap<V, Vec2>) {
182 for i in self.start..self.len() {
183 let area = self.get_triangle_area(i, vec_hm);
184 if !(area >= -Vec2::S::EPS.sqrt()) {
190 println!("Triangle area: {}", area);
191 assert!(area >= -Vec2::S::EPS.sqrt(), "Triangle has negative area");
192 }
193 }
194 }
195
196 pub fn verify_indices<Vec2: Vector2D>(&self, vec_hm: &HashMap<V, Vec2>) {
198 let num_vertices = vec_hm.len();
200 let num_triangles = self.len();
201
202 assert!(
203 num_triangles == num_vertices - 2,
204 "Expected {} triangles but found {}",
205 num_vertices - 2,
206 num_triangles
207 );
208 for i in self.start..self.indices.len() {
209 assert!(
210 vec_hm.get(&self.indices[i]).is_some(),
211 "Index {} out of bounds in triangulation",
212 self.indices[i]
213 );
214 }
215 }
216
217 pub fn verify_no_intersections<Vec2: Vector2D>(&self, vec_hm: &HashMap<V, Vec2>) {
219 let num_vertices = self.indices.len() - self.start;
220 for i in (self.start..num_vertices).step_by(3) {
232 for j in (self.start..num_vertices).step_by(3) {
233 if i == j {
234 continue;
235 }
236 for k in 0..3 {
237 for l in 0..3 {
238 let i0 = self.get_index(i + k);
239 let v0 = vec_hm[&i0];
240 let i1 = self.get_index(i + (k + 1) % 3);
241 let v1 = vec_hm[&i1];
242
243 let i2 = self.get_index(j + l);
244 let v2 = vec_hm[&i2];
245 let i3 = self.get_index(j + (l + 1) % 3);
246 let v3 = vec_hm[&i3];
247
248 if i0 == i2 || i0 == i3 || i1 == i2 || i1 == i3 {
250 continue;
251 }
252
253 let l1 = LineSegment2D::new(v0, v1);
254 let l2 = LineSegment2D::new(v2, v3);
255 let length = l1.length() + l2.length();
256 let inter = l1.intersect_line(
257 &l2,
258 Vec2::S::EPS.sqrt(), -Vec2::S::EPS.sqrt() * length, );
261 assert!(
262 inter.is_none(),
263 "Edges: \n{} {:?} -> {} {:?}\n{} {:?} -> {} {:?}\nintersect in {:?} (shortest distance: {} * sqrt(eps))\nTriangles {:?} and {:?}",
264 i0,
265 v0,
266 i1,
267 v1,
268 i2,
269 v2,
270 i3,
271 v3,
272 inter.unwrap(),
273 [v0,v1,v2,v3].iter().map(|v| inter.unwrap().distance(&v)).min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap() / Vec2::S::EPS.sqrt(),
274 (self.get_index(i), self.get_index(i+1), self.get_index(i+2)),
275 (self.get_index(j), self.get_index(j+1), self.get_index(j+2)),
276 );
277 }
278 }
279 }
280 }
281 }
282
283 pub fn get_area<Vec2: Vector2D>(&self, vec_hm: &HashMap<V, Vec2>) -> Vec2::S {
285 Vec2::S::HALF
286 * (0..self.len())
287 .into_iter()
288 .map(|i| self.get_triangle_area(i, vec_hm).abs())
289 .stable_sum()
290 }
291
292 pub fn total_edge_weight<Vec2: Vector2D>(&self, vec_hm: &HashMap<V, Vec2>) -> Vec2::S {
294 let mut total = Vec2::S::ZERO;
295 for i in self.start..self.len() {
296 let (i1, i2, i3) = self.get_triangle(i);
297 let v0 = vec_hm[&i1];
298 let v1 = vec_hm[&i2];
299 let v2 = vec_hm[&i3];
300 total += v1.distance(&v0) + v2.distance(&v1) + v0.distance(&v2);
301 }
302 total
303 }
304
305 pub fn verify_area<Vec2: Vector2D, Poly: Polygon<Vec2>>(
307 &self,
308 vec2s: &Vec<IndexedVertex2D<V, Vec2>>,
309 vec_hm: &HashMap<V, Vec2>,
310 ) {
311 let area = self.get_area(vec_hm);
312 let reference = Poly::from_iter(vec2s.iter().map(|v| v.vec)).area();
313
314 assert!(
316 (Vec2::S::ONE - area / reference).abs()
317 <= (Vec2::S::ONE + Vec2::S::from_usize(5) * Vec2::S::EPS),
318 "Area of the polygon is not equal to the sum of the areas of the triangles ({} != {})",
319 area,
320 reference
321 );
322 }
323
324 pub fn verify_all_indices_used<Vec2: Vector2D>(&self, vec2s: &Vec<IndexedVertex2D<V, Vec2>>) {
326 let mut seen = HashSet::new();
327 for i in self.start..self.indices.len() {
328 seen.insert(self.indices[i]);
329 }
330
331 for vertex in vec2s.iter() {
332 assert!(
333 seen.remove(&vertex.index),
334 "Vertex not used in triangulation {}",
335 vertex.index.index()
336 );
337 }
338
339 assert!(
340 seen.is_empty(),
341 "Foreign indices used in triangulation: {:?}",
342 seen.iter().map(|i| i.index()).collect::<Vec<_>>()
343 );
344 }
345
346 pub fn verify_full<Vec2: Vector2D, Poly: Polygon<Vec2>>(
348 &self,
349 vec2s: &Vec<IndexedVertex2D<V, Vec2>>,
350 ) {
351 let vec_hm: HashMap<V, Vec2> = vec2s.iter().map(|v| (v.index, v.vec)).collect();
352
353 self.verify_indices(&vec_hm);
354 self.verify_all_indices_used(&vec2s);
355 self.verify_no_intersections(&vec_hm);
356 self.verify_non_degenerate_triangle(&vec_hm);
357 self.verify_area::<Vec2, Poly>(&vec2s, &vec_hm);
358 }
359}