use crate::linalg::Vec3;
use crate::types::{Box as BBox, Halfedge};
use crate::impl_mesh::ManifoldImpl;
const K_NO_CODE: u32 = 0xFFFF_FFFFu32;
#[inline]
fn spread_bits3(mut v: u32) -> u32 {
v = 0xFF0000FFu32 & v.wrapping_mul(0x00010001u32);
v = 0x0F00F00Fu32 & v.wrapping_mul(0x00000101u32);
v = 0xC30C30C3u32 & v.wrapping_mul(0x00000011u32);
v = 0x49249249u32 & v.wrapping_mul(0x00000005u32);
v
}
pub fn morton_code(position: Vec3, bbox: &BBox) -> u32 {
if position.x.is_nan() {
return K_NO_CODE;
}
morton_code_impl(position, bbox)
}
fn morton_code_impl(position: Vec3, bbox: &BBox) -> u32 {
let range = bbox.max - bbox.min;
let xyz = (position - bbox.min) / range;
let x_f = (1024.0 * xyz.x).min(1023.0).max(0.0);
let y_f = (1024.0 * xyz.y).min(1023.0).max(0.0);
let z_f = (1024.0 * xyz.z).min(1023.0).max(0.0);
let x = spread_bits3(x_f as u32);
let y = spread_bits3(y_f as u32);
let z = spread_bits3(z_f as u32);
x * 4 + y * 2 + z
}
pub fn sort_verts(mesh: &mut ManifoldImpl) {
let num_vert = mesh.vert_pos.len();
let bbox = mesh.bbox;
let vert_morton: Vec<u32> = mesh.vert_pos.iter()
.map(|&p| morton_code(p, &bbox))
.collect();
let mut vert_new2old: Vec<i32> = (0..num_vert as i32).collect();
vert_new2old.sort_by(|&a, &b| vert_morton[a as usize].cmp(&vert_morton[b as usize]));
let new_num_vert = vert_new2old.partition_point(|&v| vert_morton[v as usize] < K_NO_CODE);
let vert_new2old_trimmed = &vert_new2old[..new_num_vert];
reindex_verts(mesh, vert_new2old_trimmed, num_vert);
let old_pos = mesh.vert_pos.clone();
mesh.vert_pos.resize(new_num_vert, Vec3::new(0.0, 0.0, 0.0));
for (new_idx, &old_idx) in vert_new2old_trimmed.iter().enumerate() {
mesh.vert_pos[new_idx] = old_pos[old_idx as usize];
}
if mesh.vert_normal.len() == num_vert {
let old_n = mesh.vert_normal.clone();
mesh.vert_normal.resize(new_num_vert, Vec3::new(0.0, 0.0, 0.0));
for (new_idx, &old_idx) in vert_new2old_trimmed.iter().enumerate() {
mesh.vert_normal[new_idx] = old_n[old_idx as usize];
}
}
}
pub fn reindex_verts(mesh: &mut ManifoldImpl, vert_new2old: &[i32], old_num_vert: usize) {
let mut vert_old2new = vec![-1i32; old_num_vert];
for (new_idx, &old_idx) in vert_new2old.iter().enumerate() {
vert_old2new[old_idx as usize] = new_idx as i32;
}
let has_prop = mesh.num_prop > 0;
for edge in mesh.halfedge.iter_mut() {
if edge.start_vert < 0 {
continue;
}
edge.start_vert = vert_old2new[edge.start_vert as usize];
edge.end_vert = vert_old2new[edge.end_vert as usize];
if !has_prop {
edge.prop_vert = edge.start_vert;
}
}
}
pub fn get_face_box_morton(mesh: &ManifoldImpl) -> (Vec<BBox>, Vec<u32>) {
let num_tri = mesh.num_tri();
let bbox = mesh.bbox;
let mut face_box = vec![BBox::default(); num_tri];
let mut face_morton = vec![0u32; num_tri];
for face in 0..num_tri {
if mesh.halfedge[3 * face].paired_halfedge < 0 {
face_morton[face] = K_NO_CODE;
continue;
}
let mut center = Vec3::new(0.0, 0.0, 0.0);
for i in 0..3 {
let pos = mesh.vert_pos[mesh.halfedge[3 * face + i].start_vert as usize];
center = center + pos;
face_box[face].union_point(pos);
}
center = center / 3.0;
face_morton[face] = morton_code_impl(center, &bbox);
}
(face_box, face_morton)
}
pub fn sort_faces(mesh: &mut ManifoldImpl, face_box: &mut Vec<BBox>, face_morton: &mut Vec<u32>) {
let num_tri = face_box.len();
let mut face_new2old: Vec<usize> = (0..num_tri).collect();
face_new2old.sort_by(|&a, &b| face_morton[a].cmp(&face_morton[b]));
let new_num_tri = face_new2old.partition_point(|&f| face_morton[f] < K_NO_CODE);
face_new2old.truncate(new_num_tri);
let old_morton = face_morton.clone();
let old_box = face_box.clone();
face_morton.resize(new_num_tri, 0);
face_box.resize(new_num_tri, BBox::default());
for (new_f, &old_f) in face_new2old.iter().enumerate() {
face_morton[new_f] = old_morton[old_f];
face_box[new_f] = old_box[old_f];
}
gather_faces(mesh, &face_new2old);
}
pub fn gather_faces(mesh: &mut ManifoldImpl, face_new2old: &[usize]) {
let num_tri = face_new2old.len();
let old_num_tri = mesh.num_tri();
if mesh.mesh_relation.tri_ref.len() == old_num_tri {
let old_tri_ref = mesh.mesh_relation.tri_ref.clone();
mesh.mesh_relation.tri_ref.resize(num_tri, Default::default());
for (new_f, &old_f) in face_new2old.iter().enumerate() {
mesh.mesh_relation.tri_ref[new_f] = old_tri_ref[old_f];
}
}
if mesh.face_normal.len() == old_num_tri {
let old_normals = mesh.face_normal.clone();
mesh.face_normal.resize(num_tri, Vec3::new(0.0, 0.0, 0.0));
for (new_f, &old_f) in face_new2old.iter().enumerate() {
mesh.face_normal[new_f] = old_normals[old_f];
}
}
let mut face_old2new = vec![-1i32; old_num_tri];
for (new_f, &old_f) in face_new2old.iter().enumerate() {
face_old2new[old_f] = new_f as i32;
}
let old_halfedge = mesh.halfedge.clone();
let old_tangent = mesh.halfedge_tangent.clone();
let has_tangent = !old_tangent.is_empty();
mesh.halfedge.resize(3 * num_tri, Halfedge::default());
if has_tangent {
mesh.halfedge_tangent.resize(3 * num_tri, Default::default());
}
for new_face in 0..num_tri {
let old_face = face_new2old[new_face];
for i in 0..3 {
let old_edge_idx = 3 * old_face + i;
let new_edge_idx = 3 * new_face + i;
let mut edge = old_halfedge[old_edge_idx];
if edge.paired_halfedge >= 0 {
let paired_old_face = (edge.paired_halfedge / 3) as usize;
let offset = edge.paired_halfedge % 3;
edge.paired_halfedge = 3 * face_old2new[paired_old_face] + offset;
}
mesh.halfedge[new_edge_idx] = edge;
if has_tangent {
mesh.halfedge_tangent[new_edge_idx] = old_tangent[old_edge_idx];
}
}
}
}
pub fn compact_props(mesh: &mut ManifoldImpl) {
if mesh.num_prop == 0 {
return;
}
let num_prop = mesh.num_prop;
let num_prop_verts = mesh.properties.len() / num_prop;
let mut keep = vec![false; num_prop_verts];
for edge in &mesh.halfedge {
if edge.prop_vert >= 0 && (edge.prop_vert as usize) < num_prop_verts {
keep[edge.prop_vert as usize] = true;
}
}
let mut prop_old2new = vec![0i32; num_prop_verts + 1];
for i in 0..num_prop_verts {
prop_old2new[i + 1] = prop_old2new[i] + if keep[i] { 1 } else { 0 };
}
let new_num_prop_verts = prop_old2new[num_prop_verts] as usize;
let old_prop = mesh.properties.clone();
mesh.properties.resize(num_prop * new_num_prop_verts, 0.0);
for old_idx in 0..num_prop_verts {
if !keep[old_idx] {
continue;
}
let new_idx = prop_old2new[old_idx] as usize;
for p in 0..num_prop {
mesh.properties[new_idx * num_prop + p] = old_prop[old_idx * num_prop + p];
}
}
for edge in mesh.halfedge.iter_mut() {
if edge.prop_vert >= 0 {
edge.prop_vert = prop_old2new[edge.prop_vert as usize];
}
}
}
pub fn sort_geometry(mesh: &mut ManifoldImpl) {
if mesh.halfedge.is_empty() {
return;
}
sort_verts(mesh);
let (mut face_box, mut face_morton) = get_face_box_morton(mesh);
sort_faces(mesh, &mut face_box, &mut face_morton);
if mesh.halfedge.is_empty() {
return;
}
compact_props(mesh);
debug_assert!(
mesh.halfedge.len() % 6 == 0,
"Not an even number of halfedges after sorting (expected multiple of 6, got {})",
mesh.halfedge.len()
);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::linalg::Mat3x4;
use crate::impl_mesh::ManifoldImpl;
#[test]
fn test_spread_bits3() {
assert_eq!(spread_bits3(0), 0);
assert_eq!(spread_bits3(1), 1);
assert_eq!(spread_bits3(0b10), 0b1000);
assert_eq!(spread_bits3(0b11), 0b1001);
assert_eq!(spread_bits3(0b100), 0b1000000);
}
#[test]
fn test_morton_code_basic() {
let bbox = BBox {
min: Vec3::new(0.0, 0.0, 0.0),
max: Vec3::new(1.0, 1.0, 1.0),
};
let code_origin = morton_code(Vec3::new(0.0, 0.0, 0.0), &bbox);
assert_eq!(code_origin, 0);
let code_nan = morton_code(Vec3::new(f64::NAN, 0.0, 0.0), &bbox);
assert_eq!(code_nan, K_NO_CODE);
let code_center = morton_code(Vec3::new(0.5, 0.5, 0.5), &bbox);
assert!(code_center > 0 && code_center < K_NO_CODE);
}
#[test]
fn test_morton_code_ordering() {
let bbox = BBox {
min: Vec3::new(0.0, 0.0, 0.0),
max: Vec3::new(8.0, 8.0, 8.0),
};
let p0 = morton_code(Vec3::new(0.0, 0.0, 0.0), &bbox);
let p1 = morton_code(Vec3::new(1.0, 0.0, 0.0), &bbox);
let p2 = morton_code(Vec3::new(2.0, 0.0, 0.0), &bbox);
assert!(p0 < p1);
assert!(p1 < p2);
}
#[test]
fn test_sort_geometry_tetrahedron() {
let mut m = ManifoldImpl::tetrahedron(&Mat3x4::identity());
assert_eq!(m.vert_pos.len(), 4);
assert_eq!(m.halfedge.len(), 12);
sort_geometry(&mut m);
assert_eq!(m.vert_pos.len(), 4);
assert_eq!(m.halfedge.len(), 12);
}
#[test]
fn test_sort_geometry_cube() {
let mut m = ManifoldImpl::cube(&Mat3x4::identity());
let vert_count = m.vert_pos.len();
let halfedge_count = m.halfedge.len();
sort_geometry(&mut m);
assert_eq!(m.vert_pos.len(), vert_count);
assert_eq!(m.halfedge.len(), halfedge_count);
for (i, edge) in m.halfedge.iter().enumerate() {
assert!(edge.paired_halfedge >= 0,
"halfedge {} has invalid paired_halfedge {}", i, edge.paired_halfedge);
let paired = &m.halfedge[edge.paired_halfedge as usize];
assert_eq!(paired.paired_halfedge, i as i32,
"halfedge {} paired -> {} but paired doesn't point back", i, edge.paired_halfedge);
}
}
#[test]
fn test_reindex_verts_identity() {
let mut m = ManifoldImpl::tetrahedron(&Mat3x4::identity());
let n = m.vert_pos.len();
let identity: Vec<i32> = (0..n as i32).collect();
let before: Vec<_> = m.halfedge.iter().map(|e| (e.start_vert, e.end_vert)).collect();
reindex_verts(&mut m, &identity, n);
let after: Vec<_> = m.halfedge.iter().map(|e| (e.start_vert, e.end_vert)).collect();
assert_eq!(before, after);
}
#[test]
fn test_sort_faces_manifold_preserved() {
let mut m = ManifoldImpl::cube(&Mat3x4::identity());
sort_geometry(&mut m);
assert!(m.is_2_manifold());
}
}