use crate::geometry::{WeightedEdge, WeightedTriangle, WeightedVertex};
fn setup() -> WeightedTriangle {
pub const LARGE_NUM: f64 = 100.0;
let p_2_lw: WeightedVertex = WeightedVertex::new(0.0, LARGE_NUM, 0.0);
let p_1_lw: WeightedVertex = WeightedVertex::new(LARGE_NUM, -LARGE_NUM, 0.0);
let p_0_lw: WeightedVertex = WeightedVertex::new(-LARGE_NUM, -LARGE_NUM, 0.0);
let _e_21_lw: WeightedEdge = WeightedEdge::new(p_2_lw, p_1_lw);
let _e_10_lw: WeightedEdge = WeightedEdge::new(p_1_lw, p_0_lw);
let _e_02_lw: WeightedEdge = WeightedEdge::new(p_0_lw, p_2_lw);
WeightedTriangle::new(p_2_lw, p_1_lw, p_0_lw)
}
pub mod ric {
use crate::{
algos::setup,
geometry::{TriangleLogic, WeightedEdge, WeightedTriangle, WeightedVertex, Triangulation},
utils::{point_in_tri, c_hull, is_flippable_strict, induced_subcomplex},
};
use rand::{seq::SliceRandom, thread_rng};
fn find_containing_triangle(
triangulation: &mut [WeightedTriangle],
vertex: &WeightedVertex,
) -> Option<WeightedTriangle> {
triangulation
.iter()
.find(|triangle| point_in_tri(vertex, triangle))
.copied()
}
fn find_neighboring_triangle(
edge: &WeightedEdge,
vertex: &WeightedVertex,
triangulation: &[WeightedTriangle],
) -> Option<(WeightedTriangle, WeightedVertex)> {
triangulation.iter().find_map(|triangle| {
if triangle.has_edge(edge) && !triangle.has_vertex(vertex) {
Some((*triangle, triangle.get_third_point(edge)))
} else {
None
}
})
}
fn one_to_three_split(triangle: &WeightedTriangle, vertex: &WeightedVertex) -> Vec<WeightedTriangle> {
triangle
.edges()
.iter()
.map(|edge| WeightedTriangle::new(*vertex, edge.a, edge.b))
.collect()
}
fn three_to_one_split(points: &[WeightedVertex; 4]) -> WeightedTriangle {
let c_hull = c_hull(&points.to_vec());
WeightedTriangle::new(c_hull[0], c_hull[1], c_hull[2])
}
fn two_flip(
edge: &WeightedEdge,
vertex: &WeightedVertex,
neighbor_triangle: &WeightedTriangle,
) -> ([WeightedTriangle; 2], Vec<WeightedTriangle>) {
let pk = neighbor_triangle.get_third_point(edge);
(
[
*neighbor_triangle,
WeightedTriangle::new(edge.a, edge.b, *vertex),
],
vec![
WeightedTriangle::new(edge.a, *vertex, pk),
WeightedTriangle::new(edge.b, *vertex, pk),
],
)
}
pub fn compute2d(vertices: &mut Vec<WeightedVertex>, epsilon: f64) -> Triangulation {
let super_tri = setup();
let mut rng = thread_rng();
let mut triangulation = Triangulation::new(super_tri);
triangulation.cache.push(vec![super_tri]);
let mut edge_stack: Vec<WeightedEdge> = Vec::new();
vertices.shuffle(&mut rng);
for vertex in vertices.iter() {
if let Some(invalid_triangle) = find_containing_triangle(triangulation.triangles_mut(), vertex) {
if !invalid_triangle.is_eps_regular(vertex, epsilon) {
triangulation.used_vertices_mut().push(*vertex);
triangulation.triangles_mut().retain(|tri| *tri != invalid_triangle);
triangulation.triangles_mut().append(&mut one_to_three_split(&invalid_triangle, vertex));
triangulation.cache.push(triangulation.triangles().clone());
edge_stack.append(&mut invalid_triangle.edges());
while !edge_stack.is_empty() {
if let Some(edge) = edge_stack.pop() {
if let Some((neighbor_triangle, pk)) =
find_neighboring_triangle(&edge, vertex, triangulation.triangles())
{
if !neighbor_triangle.is_regular(vertex) {
let subcomplex: Vec<WeightedTriangle> = induced_subcomplex(&[edge.a, edge.b, *vertex, pk], &triangulation);
if subcomplex.len() == 2 && is_flippable_strict(&subcomplex) { let (triangles_to_remove, mut triangles_to_add) =
two_flip(&edge, vertex, &neighbor_triangle);
triangulation.triangles_mut().retain(|tri| !triangles_to_remove.contains(tri));
triangulation.triangles_mut().append(&mut triangles_to_add);
triangulation.cache.push(triangulation.triangles().clone());
edge_stack.append(&mut vec![
WeightedEdge::new(edge.a, pk),
WeightedEdge::new(edge.b, pk),
])
} else if subcomplex.len() == 3 {
let triangle = three_to_one_split(&[edge.a, edge.b, *vertex, pk]);
triangulation.triangles_mut().retain(|tri| !subcomplex.contains(tri));
triangulation.triangles_mut().push(triangle);
edge_stack.append(&mut triangle.edges());
}
}
}
}
}
} else {
triangulation.ignored_vertices_mut().push(*vertex);
}
} else {
panic!("The vertex should lie in one of the triangles!")
}
triangulation.cache.push(triangulation.triangles().clone());
}
triangulation.triangles_mut().retain(|triangle| {
!triangle.has_vertex(&super_tri.a)
&& !triangle.has_vertex(&super_tri.b)
&& !triangle.has_vertex(&super_tri.c)
});
triangulation.cache.push(triangulation.triangles().clone());
triangulation
}
}
pub mod bw {
extern crate nalgebra as na;
use rand::seq::SliceRandom;
use rand::thread_rng;
use crate::geometry::{TriangleLogic, Triangulation};
use crate::geometry::{WeightedEdge, WeightedTriangle, WeightedVertex};
use crate::utils::{keep_unique_elements, point_in_tri};
use super::setup;
pub fn compute2d(vertices: &mut Vec<WeightedVertex>, epsilon: f64) -> Triangulation {
let super_tri = setup();
let mut triangulation = Triangulation::new(super_tri);
triangulation.cache.push(vec![super_tri]);
let mut rng = thread_rng();
vertices.shuffle(&mut rng);
for vertex in vertices {
add_vertex(&mut triangulation, vertex, epsilon);
}
triangulation.triangles_mut().retain(|triangle| {
!triangle.has_vertex(&super_tri.a)
&& !triangle.has_vertex(&super_tri.b)
&& !triangle.has_vertex(&super_tri.c)
});
triangulation.cache.push(triangulation.triangles().clone());
triangulation
}
fn add_vertex(
triangulation: &mut Triangulation,
vertex: &WeightedVertex,
epsilon: f64,
) {
let mut edges_to_retriangulate: Vec<WeightedEdge> = Vec::new();
for triangle in triangulation.triangles().iter() {
if point_in_tri(vertex, triangle) {
if triangle.is_eps_regular(vertex, epsilon) {
triangulation.ignored_vertices_mut().push(*vertex);
return;
}
break;
}
}
triangulation.used_vertices_mut().push(*vertex);
triangulation.triangles_mut().retain(|triangle| {
let regular = triangle.is_regular(vertex);
if !regular {
edges_to_retriangulate.append(&mut triangle.edges());
}
regular
});
edges_to_retriangulate = keep_unique_elements(edges_to_retriangulate);
for edge in edges_to_retriangulate {
triangulation.triangles_mut().push(WeightedTriangle::new(edge.a, edge.b, *vertex));
}
triangulation.cache.push(triangulation.triangles().clone());
}
}
#[cfg(test)]
mod tests {
use crate::{
algos::{bw, ric},
geometry::{WeightedTriangle, WeightedVertex, WeightedTriangulation}, validation::all_globally_regular,
};
#[test]
fn test_algos() {
let vertices = vec![
WeightedVertex::new(2.0, 3.0, 0.0),
WeightedVertex::new(2.5, 2.0, 0.0),
WeightedVertex::new(1.0, 2.5, 0.0),
WeightedVertex::new(0.5, 0.5, 0.0),
];
let mut true_triangulation = vec![
WeightedTriangle::new(
WeightedVertex::new(2.5, 2.0, 0.0),
WeightedVertex::new(1.0, 2.5, 0.0),
WeightedVertex::new(2.0, 3.0, 0.0),
),
WeightedTriangle::new(
WeightedVertex::new(2.5, 2.0, 0.0),
WeightedVertex::new(1.0, 2.5, 0.0),
WeightedVertex::new(0.5, 0.5, 0.0),
),
];
let eps = 0.0;
let mut triangulation_bw = bw::compute2d(&mut vertices.clone(), eps);
let mut triangulation_ric = ric::compute2d(&mut vertices.clone(), eps);
assert_eq!(true_triangulation.len(), triangulation_bw.triangles().len());
assert_eq!(true_triangulation.len(), triangulation_ric.triangles().len());
true_triangulation.sort();
triangulation_bw.triangles_mut().sort();
triangulation_ric.triangles_mut().sort();
assert_eq!(true_triangulation, *triangulation_bw.triangles());
assert_eq!(true_triangulation, *triangulation_ric.triangles());
}
#[test]
fn test_debug_ric() {
let vertices = vec![
WeightedVertex::new(5.27, 1.20, 2.95),
WeightedVertex::new(3.35, 7.15, 4.69),
WeightedVertex::new(6.13, 6.62, 1.76),
WeightedVertex::new(4.56, 8.19, 1.13),
WeightedVertex::new(7.44, 5.76, 8.45),
WeightedVertex::new(3.56, 7.33, 7.93),
];
let triangulation = ric::compute2d(&mut vertices.clone(), 0.0);
let regularity = all_globally_regular(&vertices, &triangulation.triangles());
println!("\n\nFinal triangulation:\n{}", WeightedTriangulation(triangulation.triangles()));
println!("Regularity: {}", regularity);
println!();
for tris in triangulation.cache {
println!("\n\nTriangulation:\n{}", WeightedTriangulation(&tris));
}
}
}