use crate::impl_mesh::ManifoldImpl;
use crate::linalg::{length2, Vec3};
use crate::polygon::{ccw, triangulate_idx_halfedges, HalfedgeTriangulation};
use crate::types::{Halfedge, TriRef};
use super::{assemble_halfedges, get_axis_aligned_projection, project_polygons};
fn write_local_triangles(
output: &mut [Halfedge],
contour2tri: &mut [i32],
face_halfedge: &[Halfedge],
first_tri: usize,
triangles: &[[i32; 3]],
) {
debug_assert!(triangles.len() <= 2, "local face path only handles tris/quads");
let first_out = 3 * first_tri as i32;
let mut local_edges: [[i32; 3]; 6] = [[0; 3]; 6];
let mut num_edge = 0usize;
for tri in triangles {
for i in 0..3 {
let out = first_out + num_edge as i32;
let start = tri[i];
let end = tri[(i + 1) % 3];
local_edges[num_edge] = [start, end, out];
output[out as usize].start_vert = face_halfedge[start as usize].start_vert;
output[out as usize].end_vert = face_halfedge[end as usize].start_vert;
output[out as usize].prop_vert = face_halfedge[start as usize].prop_vert;
output[out as usize].paired_halfedge = -1;
num_edge += 1;
}
}
for i in 0..num_edge {
let edge = local_edges[i];
let mut pair = -1;
for j in 0..num_edge {
if local_edges[j][0] == edge[1] && local_edges[j][1] == edge[0] {
pair = local_edges[j][2];
break;
}
}
if pair >= 0 {
output[edge[2] as usize].paired_halfedge = pair;
} else {
contour2tri[edge[0] as usize] = edge[2];
}
}
}
fn write_general_triangulation(
output: &mut [Halfedge],
contour2tri: &mut [i32],
face_halfedge: &[Halfedge],
first_tri: usize,
triangulation: &HalfedgeTriangulation,
) {
let first_out = 3 * first_tri as i32;
let contour_end = triangulation.contour_end;
let num_tri_halfedge = 3 * triangulation.num_tri();
for local in 0..num_tri_halfedge {
let out = (first_out as usize) + local;
let edge = &triangulation.halfedges[contour_end + local];
output[out].start_vert = face_halfedge[edge.start_vert as usize].start_vert;
output[out].end_vert = face_halfedge[edge.end_vert as usize].start_vert;
output[out].prop_vert = face_halfedge[edge.start_vert as usize].prop_vert;
if edge.paired_halfedge >= contour_end as i32 {
output[out].paired_halfedge = first_out + edge.paired_halfedge - contour_end as i32;
} else {
output[out].paired_halfedge = -1;
}
}
for contour in 0..contour_end {
let edge = &triangulation.halfedges[contour];
if edge.paired_halfedge < 0 {
continue;
}
debug_assert!(
edge.paired_halfedge >= contour_end as i32,
"contour paired to another contour"
);
let boundary = edge.end_vert;
debug_assert!(
boundary >= 0 && (boundary as usize) < contour2tri.len(),
"contour edge index out of bounds"
);
contour2tri[boundary as usize] = first_out + edge.paired_halfedge - contour_end as i32;
}
}
pub fn face2tri(
mesh: &mut ManifoldImpl,
face_edge: &[i32],
halfedge_ref: &[TriRef],
allow_convex: bool,
) {
face2tri_ct(mesh, face_edge, halfedge_ref, allow_convex, None);
}
pub fn face2tri_ct(
mesh: &mut ManifoldImpl,
face_edge: &[i32],
halfedge_ref: &[TriRef],
allow_convex: bool,
token: Option<&crate::cancel::CancelToken>,
) -> bool {
if crate::cancel::is_cancelled(token) {
return false;
}
let face_halfedge = std::mem::take(&mut mesh.halfedge);
let num_faces = face_edge.len() - 1;
let mut contour2tri = vec![-1i32; face_halfedge.len()];
let face_normal_ref = &mesh.face_normal;
let vert_pos_ref = &mesh.vert_pos;
let epsilon = mesh.epsilon;
let general: Vec<Option<HalfedgeTriangulation>> =
match crate::par::maybe_par_map_ct(num_faces, 512, token, |face| {
let num_edge = (face_edge[face + 1] - face_edge[face]) as usize;
if num_edge <= 4 {
return None;
}
let first_edge = face_edge[face] as usize;
let last_edge = face_edge[face + 1] as usize;
let projection = get_axis_aligned_projection(face_normal_ref[face]);
let polys_loops =
assemble_halfedges(&face_halfedge[first_edge..last_edge], first_edge as i32);
let polys = project_polygons(&polys_loops, &face_halfedge, vert_pos_ref, &projection);
Some(triangulate_idx_halfedges(&polys, epsilon, allow_convex))
}) {
Some(general) => general,
None => return false,
};
let mut tri_offset = vec![0usize; face_edge.len()];
let mut results: std::collections::HashMap<usize, HalfedgeTriangulation> =
std::collections::HashMap::new();
for (face, triangulation) in general.into_iter().enumerate() {
let num_edge = (face_edge[face + 1] - face_edge[face]) as usize;
if num_edge == 0 {
continue;
}
debug_assert!(num_edge >= 3, "face has less than three edges");
tri_offset[face] = num_edge - 2;
if let Some(t) = triangulation {
tri_offset[face] = t.num_tri();
results.insert(face, t);
}
}
let mut acc = 0usize;
for entry in tri_offset.iter_mut() {
let count = *entry;
*entry = acc;
acc += count;
}
let num_tri = acc;
let mut new_halfedge = vec![
Halfedge {
start_vert: -1,
end_vert: -1,
paired_halfedge: -1,
prop_vert: -1,
};
3 * num_tri
];
let mut tri_normal = vec![Vec3::new(0.0, 0.0, 0.0); num_tri];
let mut tri_ref = vec![TriRef::default(); num_tri];
for face in 0..num_faces {
let first_edge = face_edge[face] as usize;
let last_edge = face_edge[face + 1] as usize;
let num_edge = last_edge - first_edge;
if num_edge == 0 {
continue;
}
let normal = mesh.face_normal[face];
let first_tri = tri_offset[face];
let face_num_tri;
if num_edge == 3 {
let mut tri_edge = [first_edge as i32, first_edge as i32 + 1, first_edge as i32 + 2];
let mut tri = [
face_halfedge[first_edge].start_vert,
face_halfedge[first_edge + 1].start_vert,
face_halfedge[first_edge + 2].start_vert,
];
let mut ends = [
face_halfedge[first_edge].end_vert,
face_halfedge[first_edge + 1].end_vert,
face_halfedge[first_edge + 2].end_vert,
];
if ends[0] == tri[2] {
tri_edge.swap(1, 2);
tri.swap(1, 2);
ends.swap(1, 2);
}
debug_assert!(
ends[0] == tri[1] && ends[1] == tri[2] && ends[2] == tri[0],
"these 3 edges do not form a triangle!"
);
write_local_triangles(
&mut new_halfedge,
&mut contour2tri,
&face_halfedge,
first_tri,
&[tri_edge],
);
face_num_tri = 1;
} else if num_edge == 4 {
let projection = get_axis_aligned_projection(normal);
let tri_ccw = |t: [i32; 3]| -> bool {
ccw(
projection.apply(mesh.vert_pos[face_halfedge[t[0] as usize].start_vert as usize]),
projection.apply(mesh.vert_pos[face_halfedge[t[1] as usize].start_vert as usize]),
projection.apply(mesh.vert_pos[face_halfedge[t[2] as usize].start_vert as usize]),
mesh.epsilon,
) >= 0
};
let quad_loops =
assemble_halfedges(&face_halfedge[first_edge..last_edge], first_edge as i32);
let quad = &quad_loops[0];
let tris0 = [
[quad[0], quad[1], quad[2]],
[quad[0], quad[2], quad[3]],
];
let tris1 = [
[quad[1], quad[2], quad[3]],
[quad[0], quad[1], quad[3]],
];
let choice = if !(tri_ccw(tris0[0]) && tri_ccw(tris0[1])) {
1
} else if tri_ccw(tris1[0]) && tri_ccw(tris1[1]) {
let diag0 = mesh.vert_pos[face_halfedge[quad[0] as usize].start_vert as usize]
- mesh.vert_pos[face_halfedge[quad[2] as usize].start_vert as usize];
let diag1 = mesh.vert_pos[face_halfedge[quad[1] as usize].start_vert as usize]
- mesh.vert_pos[face_halfedge[quad[3] as usize].start_vert as usize];
if length2(diag0) > length2(diag1) { 1 } else { 0 }
} else {
0
};
let chosen = if choice == 0 { &tris0 } else { &tris1 };
write_local_triangles(
&mut new_halfedge,
&mut contour2tri,
&face_halfedge,
first_tri,
chosen,
);
face_num_tri = 2;
} else {
let triangulation = results
.get(&face)
.expect("general face missing triangulation result");
write_general_triangulation(
&mut new_halfedge,
&mut contour2tri,
&face_halfedge,
first_tri,
triangulation,
);
face_num_tri = triangulation.num_tri();
}
let ref_tri = halfedge_ref[first_edge];
for t in 0..face_num_tri {
tri_normal[first_tri + t] = normal;
tri_ref[first_tri + t] = ref_tri;
}
}
for edge in 0..face_halfedge.len() {
let tri_edge = contour2tri[edge];
if tri_edge < 0 {
continue;
}
let pair = face_halfedge[edge].paired_halfedge;
if pair < 0 {
continue;
}
let pair_tri = contour2tri[pair as usize];
debug_assert!(pair_tri >= 0, "boundary edge did not triangulate with its pair");
new_halfedge[tri_edge as usize].paired_halfedge = pair_tri;
}
mesh.halfedge = new_halfedge;
mesh.face_normal = tri_normal;
mesh.mesh_relation.tri_ref = tri_ref;
true
}