use std::collections::BTreeMap;
use crate::linalg::{Vec2, Vec3, cross, dot, normalize, length2};
use crate::math;
use crate::polygon::{ccw, triangulate_idx_halfedges, HalfedgeTriangulation};
use crate::types::{next_halfedge, Halfedge, PolyVert, PolygonsIdx, TriRef};
use crate::impl_mesh::ManifoldImpl;
#[derive(Clone, Copy, Debug)]
pub struct Proj2x3 {
pub row0: Vec3,
pub row1: Vec3,
}
impl Proj2x3 {
#[inline]
pub fn apply(&self, v: Vec3) -> Vec2 {
Vec2::new(dot(self.row0, v), dot(self.row1, v))
}
}
pub fn get_axis_aligned_projection(normal: Vec3) -> Proj2x3 {
let abs = Vec3::new(normal.x.abs(), normal.y.abs(), normal.z.abs());
let (row0, row1, xyz_max) = if abs.z > abs.x && abs.z > abs.y {
(Vec3::new(1.0, 0.0, 0.0), Vec3::new(0.0, 1.0, 0.0), normal.z)
} else if abs.y > abs.x {
(Vec3::new(0.0, 0.0, 1.0), Vec3::new(1.0, 0.0, 0.0), normal.y)
} else {
(Vec3::new(0.0, 1.0, 0.0), Vec3::new(0.0, 0.0, 1.0), normal.x)
};
if xyz_max < 0.0 {
Proj2x3 { row0: Vec3::new(-row0.x, -row0.y, -row0.z), row1 }
} else {
Proj2x3 { row0, row1 }
}
}
pub fn set_normals_and_coplanar(mesh: &mut ManifoldImpl) {
let num_tri = mesh.num_tri();
mesh.face_normal.resize(num_tri, Vec3::new(0.0, 0.0, 1.0));
struct TriPriority {
area2: f64,
tri: usize,
}
let mut tri_priority: Vec<TriPriority> = (0..num_tri)
.map(|tri| {
if tri < mesh.mesh_relation.tri_ref.len() {
mesh.mesh_relation.tri_ref[tri].coplanar_id = -1;
}
if mesh.halfedge[3 * tri].start_vert < 0 {
return TriPriority { area2: 0.0, tri };
}
let v = mesh.vert_pos[mesh.halfedge[3 * tri].start_vert as usize];
let n = cross(
mesh.vert_pos[mesh.halfedge[3 * tri].end_vert as usize] - v,
mesh.vert_pos[mesh.halfedge[3 * tri + 1].end_vert as usize] - v,
);
let normal = normalize(n);
mesh.face_normal[tri] = if normal.x.is_nan() {
Vec3::new(0.0, 0.0, 1.0)
} else {
normal
};
TriPriority { area2: length2(n), tri }
})
.collect();
tri_priority.sort_by(|a, b| b.area2.partial_cmp(&a.area2).unwrap_or(std::cmp::Ordering::Equal));
let mut interior_halfedges: Vec<usize> = Vec::new();
for tp in &tri_priority {
let tri = tp.tri;
if tri >= mesh.mesh_relation.tri_ref.len() {
continue;
}
if mesh.mesh_relation.tri_ref[tri].coplanar_id >= 0 {
continue;
}
mesh.mesh_relation.tri_ref[tri].coplanar_id = tri as i32;
if mesh.halfedge[3 * tri].start_vert < 0 {
continue;
}
let base = mesh.vert_pos[mesh.halfedge[3 * tri].start_vert as usize];
let normal = mesh.face_normal[tri];
interior_halfedges.clear();
interior_halfedges.push(3 * tri);
interior_halfedges.push(3 * tri + 1);
interior_halfedges.push(3 * tri + 2);
while let Some(h) = interior_halfedges.pop() {
let paired = mesh.halfedge[h].paired_halfedge;
if paired < 0 {
continue;
}
let h2 = next_halfedge(paired) as usize;
let h2_tri = h2 / 3;
if h2_tri >= mesh.mesh_relation.tri_ref.len() {
continue;
}
if mesh.mesh_relation.tri_ref[h2_tri].coplanar_id >= 0 {
continue;
}
let v = mesh.vert_pos[mesh.halfedge[h2].end_vert as usize];
if (dot(v - base, normal)).abs() < mesh.tolerance {
mesh.mesh_relation.tri_ref[h2_tri].coplanar_id = tri as i32;
mesh.face_normal[h2_tri] = normal;
let last = interior_halfedges.last().copied();
if last == Some(mesh.halfedge[h2].paired_halfedge as usize) {
interior_halfedges.pop();
} else {
interior_halfedges.push(h2 as usize);
}
interior_halfedges.push(next_halfedge(h2 as i32) as usize);
}
}
}
calculate_vert_normals(mesh);
}
pub fn calculate_vert_normals(mesh: &mut ManifoldImpl) {
let num_vert = mesh.vert_pos.len();
mesh.vert_normal.resize(num_vert, Vec3::new(0.0, 0.0, 0.0));
let mut vert_first_edge = vec![i32::MAX; num_vert];
for (i, edge) in mesh.halfedge.iter().enumerate() {
let sv = edge.start_vert;
if sv >= 0 && (sv as usize) < num_vert {
let sv = sv as usize;
if (i as i32) < vert_first_edge[sv] {
vert_first_edge[sv] = i as i32;
}
}
}
let halfedge = &mesh.halfedge;
let vert_pos = &mesh.vert_pos;
let face_normal = &mesh.face_normal;
let normals: Vec<Vec3> = crate::par::maybe_par_map(num_vert, 10_000, |vert| {
let first_edge = vert_first_edge[vert];
if first_edge == i32::MAX {
return Vec3::new(0.0, 0.0, 0.0);
}
let mut normal = Vec3::new(0.0, 0.0, 0.0);
let mut current = first_edge as usize;
loop {
let paired = halfedge[current].paired_halfedge;
if paired < 0 {
break;
}
current = next_halfedge(paired) as usize;
let h = &halfedge[current];
let tri_verts = [
h.start_vert as usize,
h.end_vert as usize,
halfedge[next_halfedge(current as i32) as usize].end_vert as usize,
];
if tri_verts[0] < vert_pos.len()
&& tri_verts[1] < vert_pos.len()
&& tri_verts[2] < vert_pos.len()
{
let curr_edge_dir = vert_pos[tri_verts[1]] - vert_pos[tri_verts[0]];
let prev_edge_dir = vert_pos[tri_verts[0]] - vert_pos[tri_verts[2]];
let curr_len = length2(curr_edge_dir).sqrt();
let prev_len = length2(prev_edge_dir).sqrt();
if curr_len > 0.0 && prev_len > 0.0 {
let curr_norm = curr_edge_dir / curr_len;
let prev_norm = prev_edge_dir / prev_len;
if curr_norm.x.is_finite() && prev_norm.x.is_finite() {
let d = dot(prev_norm, curr_norm).clamp(-1.0, 1.0);
let phi = math::acos(-d);
if phi.is_finite() && current / 3 < face_normal.len() {
normal = normal + face_normal[current / 3] * phi;
}
}
}
}
if current == first_edge as usize {
break;
}
}
let len = length2(normal).sqrt();
if len > 0.0 { normal / len } else { Vec3::new(0.0, 0.0, 0.0) }
});
mesh.vert_normal = normals;
}
pub fn get_barycentric(v: Vec3, tri_pos: [Vec3; 3], tolerance: f64) -> Vec3 {
let edges = [
tri_pos[2] - tri_pos[1],
tri_pos[0] - tri_pos[2],
tri_pos[1] - tri_pos[0],
];
let d2 = [
dot(edges[0], edges[0]),
dot(edges[1], edges[1]),
dot(edges[2], edges[2]),
];
let long_side = if d2[0] > d2[1] && d2[0] > d2[2] {
0
} else if d2[1] > d2[2] {
1
} else {
2
};
let cross_p = cross(edges[0], edges[1]);
let area2 = dot(cross_p, cross_p);
let tol2 = tolerance * tolerance;
let mut uvw = Vec3::splat(0.0);
for i in 0..3 {
let dv = v - tri_pos[i];
if dot(dv, dv) < tol2 {
uvw = Vec3::splat(0.0);
match i {
0 => uvw.x = 1.0,
1 => uvw.y = 1.0,
_ => uvw.z = 1.0,
}
return uvw;
}
}
if d2[long_side] < tol2 {
return Vec3::new(1.0, 0.0, 0.0);
} else if area2 > d2[long_side] * tol2 {
for i in 0..3 {
let j = (i + 1) % 3;
let cross_pv = cross(edges[i], v - tri_pos[j]);
let area2v = dot(cross_pv, cross_pv);
let val = if area2v < d2[i] * tol2 {
0.0
} else {
dot(cross_pv, cross_p)
};
match i {
0 => uvw.x = val,
1 => uvw.y = val,
_ => uvw.z = val,
}
}
let sum = uvw.x + uvw.y + uvw.z;
uvw = uvw / sum;
return uvw;
} else {
let next_v = (long_side + 1) % 3;
let alpha = dot(v - tri_pos[next_v], edges[long_side]) / d2[long_side];
uvw = Vec3::splat(0.0);
let last_v = (next_v + 1) % 3;
match next_v {
0 => uvw.x = 1.0 - alpha,
1 => uvw.y = 1.0 - alpha,
_ => uvw.z = 1.0 - alpha,
}
match last_v {
0 => uvw.x = alpha,
1 => uvw.y = alpha,
_ => uvw.z = alpha,
}
return uvw;
}
}
pub fn assemble_halfedges(
halfedges: &[Halfedge],
start_halfedge_idx: i32,
) -> Vec<Vec<i32>> {
let mut vert_edge: BTreeMap<i32, Vec<usize>> = BTreeMap::new();
for (i, he) in halfedges.iter().enumerate() {
vert_edge.entry(he.start_vert).or_default().push(i);
}
let mut polys: Vec<Vec<i32>> = Vec::new();
let mut start_edge: usize = 0;
let mut this_edge: usize = start_edge;
loop {
if this_edge == start_edge {
if vert_edge.is_empty() {
break;
}
let (&_vert, edges) = vert_edge.iter().next().unwrap();
start_edge = edges[0];
this_edge = start_edge;
polys.push(Vec::new());
}
polys.last_mut().unwrap().push(start_halfedge_idx + this_edge as i32);
let end_vert = halfedges[this_edge].end_vert;
let edges = vert_edge.get_mut(&end_vert).expect("non-manifold edge");
let pos = 0; this_edge = edges.remove(pos);
if edges.is_empty() {
vert_edge.remove(&end_vert);
}
}
polys
}
pub fn project_polygons(
polys: &[Vec<i32>],
halfedge: &[Halfedge],
vert_pos: &[Vec3],
projection: &Proj2x3,
) -> PolygonsIdx {
let mut polygons: PolygonsIdx = Vec::new();
for poly in polys {
let mut simple_poly = Vec::new();
for &edge in poly {
let vert = halfedge[edge as usize].start_vert;
simple_poly.push(PolyVert {
pos: projection.apply(vert_pos[vert as usize]),
idx: edge,
});
}
polygons.push(simple_poly);
}
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,
) {
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>> =
crate::par::maybe_par_map(num_faces, 512, |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))
});
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;
}
pub fn reorder_halfedges(mesh: &mut ManifoldImpl) {
let num_tri = mesh.halfedge.len() / 3;
for tri in 0..num_tri {
let base = tri * 3;
let face = [
mesh.halfedge[base],
mesh.halfedge[base + 1],
mesh.halfedge[base + 2],
];
if face[0].start_vert < 0 {
continue;
}
let mut index = 0;
for i in 1..3 {
if face[i].start_vert < face[index].start_vert {
index = i;
}
}
for i in 0..3 {
mesh.halfedge[base + i] = face[(index + i) % 3];
}
}
for tri in 0..num_tri {
for i in 0..3 {
let base = tri * 3 + i;
let curr = mesh.halfedge[base];
if curr.start_vert < 0 {
break; }
if curr.paired_halfedge < 0 {
continue; }
let opp_face = curr.paired_halfedge as usize / 3;
let mut index = -1i32;
for j in 0..3 {
if curr.start_vert == mesh.halfedge[opp_face * 3 + j].end_vert {
index = j as i32;
}
}
mesh.halfedge[base].paired_halfedge = opp_face as i32 * 3 + index;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::linalg::Mat3x4;
use crate::impl_mesh::ManifoldImpl;
#[test]
fn test_get_axis_aligned_projection_z() {
let proj = get_axis_aligned_projection(Vec3::new(0.0, 0.0, 1.0));
let v = Vec3::new(3.0, 4.0, 5.0);
let p = proj.apply(v);
assert!((p.x - 3.0).abs() < 1e-12);
assert!((p.y - 4.0).abs() < 1e-12);
}
#[test]
fn test_get_axis_aligned_projection_y() {
let proj = get_axis_aligned_projection(Vec3::new(0.0, 1.0, 0.0));
let v = Vec3::new(3.0, 4.0, 5.0);
let p = proj.apply(v);
assert!((p.x - 5.0).abs() < 1e-12, "expected z=5, got {}", p.x);
assert!((p.y - 3.0).abs() < 1e-12, "expected x=3, got {}", p.y);
}
#[test]
fn test_get_axis_aligned_projection_x() {
let proj = get_axis_aligned_projection(Vec3::new(1.0, 0.0, 0.0));
let v = Vec3::new(3.0, 4.0, 5.0);
let p = proj.apply(v);
assert!((p.x - 4.0).abs() < 1e-12, "expected y=4, got {}", p.x);
assert!((p.y - 5.0).abs() < 1e-12, "expected z=5, got {}", p.y);
}
#[test]
fn test_get_axis_aligned_projection_negative_z() {
let proj = get_axis_aligned_projection(Vec3::new(0.0, 0.0, -1.0));
let v = Vec3::new(3.0, 4.0, 5.0);
let p = proj.apply(v);
assert!((p.x + 3.0).abs() < 1e-12, "expected -x=-3, got {}", p.x);
assert!((p.y - 4.0).abs() < 1e-12);
}
#[test]
fn test_set_normals_tetrahedron() {
let mut m = ManifoldImpl::tetrahedron(&Mat3x4::identity());
set_normals_and_coplanar(&mut m);
for n in &m.face_normal {
let len = length2(*n).sqrt();
assert!((len - 1.0).abs() < 1e-10, "face normal not unit: len={}", len);
}
for n in &m.vert_normal {
let len = length2(*n).sqrt();
assert!(len > 0.0, "vert normal is zero");
}
}
#[test]
fn test_set_normals_cube() {
let mut m = ManifoldImpl::cube(&Mat3x4::identity());
set_normals_and_coplanar(&mut m);
assert_eq!(m.face_normal.len(), m.num_tri());
let any_coplanar_id_set = m.mesh_relation.tri_ref.iter()
.any(|r| r.coplanar_id >= 0);
assert!(any_coplanar_id_set, "no coplanar IDs were set");
}
}
use crate::collider::Collider;
use crate::sort::get_face_box_morton;
use crate::types::{Box as BBox, Polygons, SimplePolygon};
impl ManifoldImpl {
pub fn slice(&self, height: f64) -> Polygons {
let num_tri = self.num_tri();
if num_tri == 0 {
return vec![];
}
let mut plane = self.bbox;
plane.min.z = height;
plane.max.z = height;
let (face_box, face_morton) = get_face_box_morton(self);
let collider = Collider::new(face_box, face_morton);
let mut tris: std::collections::HashSet<usize> = std::collections::HashSet::new();
let query = vec![BBox::from_points(plane.min, plane.max)];
collider.collisions_with_boxes(&query, false, |_query_idx, tri| {
let mut min_z = f64::INFINITY;
let mut max_z = f64::NEG_INFINITY;
for j in 0..3 {
let z = self.vert_pos[self.halfedge[3 * tri + j].start_vert as usize].z;
min_z = min_z.min(z);
max_z = max_z.max(z);
}
if min_z <= height && max_z > height {
tris.insert(tri);
}
});
let mut polys: Polygons = Vec::new();
while !tris.is_empty() {
let start_tri = *tris.iter().next().unwrap();
let mut poly: SimplePolygon = Vec::new();
let mut k = 0usize;
for j in 0..3usize {
let next_j = (j + 1) % 3;
if self.vert_pos[self.halfedge[3 * start_tri + j].start_vert as usize].z > height
&& self.vert_pos[self.halfedge[3 * start_tri + next_j].start_vert as usize].z <= height
{
k = next_j;
break;
}
}
let mut tri = start_tri;
loop {
tris.remove(&tri);
if self.vert_pos[self.halfedge[3 * tri + k].end_vert as usize].z <= height {
k = (k + 1) % 3;
}
let up = &self.halfedge[3 * tri + k];
let below = self.vert_pos[up.start_vert as usize];
let above = self.vert_pos[up.end_vert as usize];
let a = (height - below.z) / (above.z - below.z);
let pt = Vec2::new(
below.x + a * (above.x - below.x),
below.y + a * (above.y - below.y),
);
poly.push(pt);
let pair = up.paired_halfedge;
tri = pair as usize / 3;
k = ((pair as usize) % 3 + 1) % 3;
if tri == start_tri {
break;
}
}
polys.push(poly);
}
polys
}
pub fn project(&self) -> Polygons {
if self.num_tri() == 0 || self.face_normal.is_empty() {
return vec![];
}
let projection = get_axis_aligned_projection(Vec3::new(0.0, 0.0, 1.0));
let mut cusps: Vec<crate::types::Halfedge> = Vec::new();
for edge in &self.halfedge {
let paired_face = self.halfedge[edge.paired_halfedge as usize].paired_halfedge as usize / 3;
let this_face = edge.paired_halfedge as usize / 3;
if self.face_normal[paired_face].z >= 0.0 && self.face_normal[this_face].z < 0.0 {
cusps.push(*edge);
}
}
if cusps.is_empty() {
return vec![];
}
let loops = assemble_halfedges(&cusps, 0);
let polys_indexed = project_polygons(&loops, &cusps, &self.vert_pos, &projection);
let mut polys: Polygons = Vec::new();
for poly in &polys_indexed {
let simple: SimplePolygon = poly.iter().map(|pv| pv.pos).collect();
polys.push(simple);
}
polys
}
}