use crate::polypartition::{Polygon, PolygonInterface};
use crate::polypartition::util::{Diagonal, distance, DPState, is_in_cone, intersects};
use std::collections::VecDeque;
pub fn triangulate_opt_vec(polys: Vec<Polygon>) -> Result<Vec<Polygon>, String> {
let mut triangles = vec![];
for poly in polys.iter() {
if poly.is_hole() {
return Err("Input polygon cannot be a hole in optimal dp!".into());
}
triangles.extend(triangulate_opt(poly)?);
}
Ok(triangles)
}
pub fn triangulate_opt(poly: &Polygon) -> Result<Vec<Polygon>, &str> {
if !poly.is_valid() {
return Err("Input polygon is invalid.");
}
let mut triangles = vec![];
let num_vertices = poly.num_points();
if num_vertices < 3 {
return Err("Input polygon has less than 3 vertices");
}
if num_vertices == 3 {
return Ok(vec![poly.clone()]);
}
let mut dp_states: Vec<Vec<DPState>> = Vec::with_capacity(num_vertices);
dp_states.push(vec![]); for i in 1..num_vertices {
dp_states.push(vec![DPState::default(); i]);
}
for i in 0..(num_vertices-1) {
let p1 = poly.get_point(i);
#[allow(clippy::needless_range_loop)]
for j in (i+1)..num_vertices {
dp_states[j][i].visible = true;
if j != i+1 {
let p2 = poly.get_point(j);
let p3 = poly.get_point(if i == 0 {num_vertices - 1} else {i-1});
let p4 = poly.get_point((i+1) % num_vertices);
if !is_in_cone(&p3, &p1, &p4, &p2) {
dp_states[j][i].visible = false;
continue;
}
let p3 = poly.get_point(if j == 0 {num_vertices - 1} else {j-1});
let p4 = poly.get_point((j+1) % num_vertices);
if !is_in_cone(&p3, &p2, &p4, &p1) {
dp_states[j][i].visible = false;
continue;
}
for k in 0..num_vertices {
let p3 = poly.get_point(k);
let p4 = poly.get_point((k+1) % num_vertices);
if intersects(&p1, &p2, &p3, &p4) {
dp_states[j][i].visible = false;
break;
}
}
}
}
}
dp_states[num_vertices - 1][0].visible = true;
for gap in 2..num_vertices {
for i in 0..(num_vertices - gap) {
let j = i + gap;
if !dp_states[j][i].visible {
continue;
}
let mut best_vertex = None;
let mut min_weight = f64::MAX;
for k in (i+1)..j {
if !dp_states[k][i].visible {
continue;
}
if !dp_states[j][k].visible {
continue;
}
let d1;
let d2;
if k <= i+1 {
d1 = 0.0;
} else {
d1 = distance(&poly.get_point(i), &poly.get_point(k));
}
if j <= k+1 {
d2 = 0.0;
} else {
d2 = distance(&poly.get_point(k), &poly.get_point(j));
}
let weight = dp_states[k][i].weight + dp_states[j][k].weight + d1 + d2;
if best_vertex.is_none() || weight < min_weight {
best_vertex = Some(k);
min_weight = weight;
}
}
if best_vertex.is_none() {
return Err("No best vertex found");
}
dp_states[j][i].best_vertex = best_vertex;
dp_states[j][i].weight = min_weight;
}
}
let mut diagonals = VecDeque::<Diagonal>::new();
diagonals.push_back(Diagonal::new(0, num_vertices - 1));
while !diagonals.is_empty() {
let diagonal = diagonals.pop_front().unwrap();
let best_vertex = dp_states[diagonal.index_2][diagonal.index_1].best_vertex;
if best_vertex.is_none() {
return Err("No best vertex found in diagonal stage");
}
let best_vertex = best_vertex.unwrap();
triangles.push(Polygon::triangle(
poly.get_point(diagonal.index_1),
poly.get_point(best_vertex),
poly.get_point(diagonal.index_2),
));
if best_vertex > diagonal.index_1 + 1 {
diagonals.push_back(Diagonal::new(diagonal.index_1, best_vertex));
}
if diagonal.index_2 > best_vertex + 1 {
diagonals.push_back(Diagonal::new(best_vertex, diagonal.index_2));
}
}
Ok(triangles)
}