1extern crate nalgebra as na;
2extern crate parry2d_f64 as parry;
3
4use crate::geometry::{WeightedEdge, WeightedTriangle, WeightedVertex, Triangulation, TriangleLogic};
5use na::Point2;
6use rand::{distributions::Uniform, rngs::ThreadRng, thread_rng, Rng};
7use std::collections::HashMap;
8
9#[allow(dead_code)]
11pub fn generate_vertices(n: u32) -> Vec<WeightedVertex> {
12 let mut rng: ThreadRng = thread_rng();
13 let distribution: Uniform<f64> = Uniform::new(0.0, 10.0);
14
15 (0..n)
16 .map(|_| WeightedVertex::new(rng.sample(distribution), rng.sample(distribution), 0.0))
17 .collect()
18}
19
20#[allow(dead_code)]
22pub fn generate_weighted_vertices(n: u32) -> Vec<WeightedVertex> {
23 let mut rng: ThreadRng = thread_rng();
24 let distribution: Uniform<f64> = Uniform::new(0.0, 10.0);
25
26 (0..n)
27 .map(|_| {
28 WeightedVertex::new(
29 rng.sample(distribution),
30 rng.sample(distribution),
31 rng.sample(distribution),
32 )
33 })
34 .collect()
35}
36
37#[allow(dead_code)]
39pub fn get_fourth_point(triangle: &WeightedTriangle, edge: &WeightedEdge) -> WeightedVertex {
40 if triangle.a != edge.a && triangle.a != edge.b {
41 WeightedVertex::new(triangle.a.x(), triangle.a.y(), triangle.a.w())
42 } else if triangle.b != edge.a && triangle.b != edge.b {
43 WeightedVertex::new(triangle.b.x(), triangle.b.y(), triangle.b.w())
44 } else {
45 WeightedVertex::new(triangle.c.x(), triangle.c.y(), triangle.c.w())
46 }
47}
48
49pub fn barycentric_coords(vertex: &WeightedVertex, triangle: &WeightedTriangle) -> (f64, f64, f64) {
51 let v0 = triangle.b.as_vec2() - triangle.a.as_vec2();
53 let v1 = triangle.c.as_vec2() - triangle.a.as_vec2();
54 let v2 = vertex.as_vec2() - triangle.a.as_vec2();
55
56 let d00 = v0.dot(&v0);
57 let d01 = v0.dot(&v1);
58 let d11 = v1.dot(&v1);
59 let d20 = v2.dot(&v0);
60 let d21 = v2.dot(&v1);
61
62 let inv_denom = 1. / (d00 * d11 - d01 * d01); let v = (d11 * d20 - d01 * d21) * inv_denom;
65 let w = (d00 * d21 - d01 * d20) * inv_denom;
66 let u = 1. - v - w;
67
68 assert!(v + w + u - 1. < 0.0001); (v, w, u)
71}
72
73pub fn point_in_tri(vertex: &WeightedVertex, triangle: &WeightedTriangle) -> bool {
75 let (v, w, u) = barycentric_coords(vertex, triangle);
76 v >= 0.0 && w >= 0.0 && u >= 0.0
77}
78
79pub fn c_hull(points: &Vec<WeightedVertex>) -> Vec<WeightedVertex> {
81 let points2d: Vec<Point2<f64>> = points.iter().map(|v| Point2::new(v.x(), v.y())).collect();
82 let c_hull = parry::transformation::convex_hull(&points2d);
83
84 c_hull
85 .iter()
86 .map(|p| {
87 for v in points {
89 if v.x() == p.x && v.y() == p.y {
90 return WeightedVertex::new(p.x, p.y, v.w());
91 }
92 }
93 panic!("One of the vertices should match the convex hull vertices.")
94 })
95 .collect()
96}
97
98pub fn induced_subcomplex(vertices: &[WeightedVertex], triangulation: &Triangulation) -> Vec<WeightedTriangle> {
131 let subcomplex: Vec<WeightedTriangle> = triangulation.triangles().iter().filter(|tri| {
132 tri.has_vertex(&vertices[0]) && tri.has_vertex(&vertices[1]) && tri.has_vertex(&vertices[2])
133 || tri.has_vertex(&vertices[0]) && tri.has_vertex(&vertices[1]) && tri.has_vertex(&vertices[3])
134 || tri.has_vertex(&vertices[1]) && tri.has_vertex(&vertices[2]) && tri.has_vertex(&vertices[3])
135 || tri.has_vertex(&vertices[0]) && tri.has_vertex(&vertices[2]) && tri.has_vertex(&vertices[3])
136 }).copied().collect();
137 subcomplex
138}
139
140
141pub fn is_flippable_strict(induced_subcomplex: &[WeightedTriangle]) -> bool {
165 let points = induced_subcomplex
166 .iter()
167 .flat_map(|t| [Point2::new(t.a.x(), t.a.y()), Point2::new(t.b.x(), t.b.y()), Point2::new(t.c.x(), t.c.y())] )
168 .collect::<Vec<Point2<f64>>>();
169
170 let c_hull = parry::transformation::convex_hull(&points);
171 points.iter().all(|item| c_hull.contains(item))
173}
174
175pub fn is_flippable(point: &WeightedVertex, edge: &WeightedEdge, pk: &WeightedVertex) -> bool {
178 let points: Vec<Point2<f64>> = vec![
181 Point2::new(point.x(), point.y()),
182 Point2::new(edge.a.x(), edge.a.y()),
183 Point2::new(edge.b.x(), edge.b.y()),
184 Point2::new(pk.x(), pk.y()),
185 ];
186
187 let c_hull = parry::transformation::convex_hull(&points);
189
190 points.iter().all(|item| c_hull.contains(item))
195}
196
197pub fn keep_unique_elements<T: Eq + std::hash::Hash>(elements: Vec<T>) -> Vec<T> {
199 elements
200 .into_iter()
201 .fold(HashMap::new(), |mut map, element| {
202 *map.entry(element).or_insert(0) += 1;
203 map
204 })
205 .into_iter()
206 .filter_map(|(element, count)| (count == 1).then_some(element))
207 .collect()
208}
209
210#[allow(dead_code)]
213pub fn keep_unique_elements2<T: Eq + std::hash::Hash + Clone>(elements: &mut Vec<T>) {
214 let mut freq_map = HashMap::new();
215
216 for element in elements.iter() {
218 *freq_map.entry(element.clone()).or_insert(0) += 1;
219 }
220
221 elements.retain(|element| freq_map.get(element) == Some(&1));
222}
223
224#[allow(warnings)]
229pub fn remove_duplicates<T: std::cmp::PartialEq<T>>(elements: &mut Vec<T>) {
230 let mut indices_to_remove: Vec<usize> = vec![];
231
232 for i in 0..elements.len() {
233 let element: &T = &elements[i];
234 for j in 0..elements.len() {
235 let comp_element: &T = &elements[j];
236 if element == comp_element && i != j {
237 indices_to_remove.push(i);
238 break;
239 }
240 }
241 }
242 for idx in indices_to_remove.iter().rev() {
245 elements.remove(*idx);
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252 use na::{Point2, Vector2};
253
254 #[test]
255 fn test_barycentric_coords() {
256 let tri = WeightedTriangle::new(
257 WeightedVertex::new(0.0, 0.0, 0.0),
258 WeightedVertex::new(1.0, 0.0, 0.0),
259 WeightedVertex::new(0.0, 1.0, 0.0),
260 );
261
262 let point = WeightedVertex::new(0.5, 0.5, 0.0);
263 let (v, w, u) = barycentric_coords(&point, &tri);
264 assert_eq!(v, 0.5);
265 assert_eq!(w, 0.5);
266 assert_eq!(u, 0.0);
267
268 let point = WeightedVertex::new(0.0, 0.0, 0.0);
269 let (v, w, u) = barycentric_coords(&point, &tri);
270 assert_eq!(v, 0.0);
271 assert_eq!(w, 0.0);
272 assert_eq!(u, 1.0);
273
274 let point = WeightedVertex::new(1.0, 0.0, 0.0);
275 let (v, w, u) = barycentric_coords(&point, &tri);
276 assert_eq!(v, 0.0);
277 assert_eq!(w, 1.0);
278 assert_eq!(u, 0.0);
279
280 let point = WeightedVertex::new(0.0, 1.0, 0.0);
281 let (v, w, u) = barycentric_coords(&point, &tri);
282 assert_eq!(v, 1.0);
283 assert_eq!(w, 0.0);
284 assert_eq!(u, 0.0);
285 }
286
287 #[test]
288 fn test_convex_hull() {
289 let points = vec![
290 Vector2::new(0.5, 0.5),
291 Vector2::new(2.5, 2.0),
292 Vector2::new(1.0, 2.5),
293 Vector2::new(2.0, 3.0),
294 Vector2::new(1.0, 1.0),
295 ];
296
297 let true_hull = vec![
298 Point2::new(0.5, 0.5),
299 Point2::new(2.5, 2.0),
300 Point2::new(2.0, 3.0),
301 Point2::new(1.0, 2.5),
302 ];
303
304 let points2d: Vec<Point2<f64>> = points.iter().map(|v| Point2::new(v.x, v.y)).collect();
305 let c_hull = parry::transformation::convex_hull(&points2d);
306 assert!(c_hull != points2d);
310 assert!(true_hull.iter().all(|item| true_hull.contains(item)));
311 }
312
313 #[test]
314 fn test_edge_flippable() {
315 pub const LARGE_NUM: f64 = 10000.0; let p_2_lw: WeightedVertex = WeightedVertex::new(0.0, LARGE_NUM, 0.0);
318
319 let p_1_lw: WeightedVertex = WeightedVertex::new(LARGE_NUM, -LARGE_NUM, 0.0);
320
321 assert!(is_flippable(
324 &WeightedVertex::new(0.5, 0.5, 0.0),
325 &WeightedEdge::new(
326 WeightedVertex::new(2.5, 2.0, 0.0),
327 WeightedVertex::new(1.0, 2.5, 0.0)
328 ),
329 &WeightedVertex::new(2.0, 3.0, 0.0)
330 ));
331
332 assert!(is_flippable(
334 &WeightedVertex::new(2.5, 2.0, 0.0),
335 &WeightedEdge::new(WeightedVertex::new(2.0, 3.0, 0.0), p_2_lw),
336 &p_1_lw
337 ));
338 }
339
340 #[test]
341 fn test_edge_not_flippable() {
342 pub const LARGE_NUM: f64 = 10000.0; let p_2_lw: WeightedVertex = WeightedVertex::new(0.0, LARGE_NUM, 0.0);
345
346 assert!(!is_flippable(
347 &WeightedVertex::new(-5., 1., 0.0),
348 &WeightedEdge::new(
349 WeightedVertex::new(-4., 2., 0.0),
350 WeightedVertex::new(-2.5, 2., 0.0),
351 ),
352 &WeightedVertex::new(-5., 3., 0.0)
353 ));
354
355 assert!(!is_flippable(
356 &WeightedVertex::new(-1.5, 0., 0.0),
357 &WeightedEdge::new(WeightedVertex::new(0., 1.5, 0.0), p_2_lw),
358 &WeightedVertex::new(2., -0.5, 0.0)
359 ));
360 }
361}