#![allow(dead_code)]
#[allow(dead_code)]
pub struct MeshLevel {
pub positions: Vec<[f32; 3]>,
pub indices: Vec<u32>,
pub level: usize,
}
#[allow(dead_code)]
pub struct MultiResMesh {
pub levels: Vec<MeshLevel>,
}
#[allow(dead_code)]
pub struct MultiResConfig {
pub levels: usize,
pub reduction_ratio: f32,
}
#[allow(dead_code)]
pub fn default_multi_res_config() -> MultiResConfig {
MultiResConfig {
levels: 4,
reduction_ratio: 0.5,
}
}
fn midpoint(a: [f32; 3], b: [f32; 3]) -> [f32; 3] {
[
(a[0] + b[0]) * 0.5,
(a[1] + b[1]) * 0.5,
(a[2] + b[2]) * 0.5,
]
}
fn decimate_half(level: &MeshLevel) -> MeshLevel {
let keep = level.indices.len().div_ceil(3 * 2);
let new_indices = level.indices[..(keep * 3).min(level.indices.len())].to_vec();
MeshLevel {
positions: level.positions.clone(),
indices: new_indices,
level: level.level + 1,
}
}
#[allow(dead_code)]
pub fn build_multi_res(
positions: &[[f32; 3]],
indices: &[u32],
cfg: &MultiResConfig,
) -> MultiResMesh {
let base = MeshLevel {
positions: positions.to_vec(),
indices: indices.to_vec(),
level: 0,
};
let mut levels = vec![base];
for _ in 1..cfg.levels {
let last = &levels[levels.len() - 1];
if last.indices.len() < 6 {
break;
}
levels.push(decimate_half(last));
}
MultiResMesh { levels }
}
#[allow(dead_code)]
pub fn level_count(mr: &MultiResMesh) -> usize {
mr.levels.len()
}
#[allow(dead_code)]
pub fn get_level(mr: &MultiResMesh, idx: usize) -> Option<&MeshLevel> {
mr.levels.get(idx)
}
#[allow(dead_code)]
pub fn coarsest_level(mr: &MultiResMesh) -> Option<&MeshLevel> {
mr.levels.last()
}
#[allow(dead_code)]
pub fn finest_level(mr: &MultiResMesh) -> Option<&MeshLevel> {
mr.levels.first()
}
#[allow(dead_code)]
pub fn level_face_counts(mr: &MultiResMesh) -> Vec<usize> {
mr.levels.iter().map(|l| l.indices.len() / 3).collect()
}
#[allow(dead_code)]
pub fn multi_res_to_json(mr: &MultiResMesh) -> String {
let counts: Vec<String> = mr
.levels
.iter()
.map(|l| format!("{}", l.indices.len() / 3))
.collect();
format!(
"{{\"level_count\":{},\"face_counts\":[{}]}}",
mr.levels.len(),
counts.join(",")
)
}
#[allow(dead_code)]
pub fn midpoint_upsample(level: &MeshLevel) -> MeshLevel {
let n = level.positions.len();
let mut new_pos = level.positions.clone();
let mut new_idx: Vec<u32> = Vec::new();
let mut edge_map: std::collections::HashMap<(u32, u32), u32> = std::collections::HashMap::new();
for chunk in level.indices.chunks(3) {
if chunk.len() < 3 {
continue;
}
let (a, b, c) = (chunk[0], chunk[1], chunk[2]);
let get_mid = |p: u32,
q: u32,
positions: &mut Vec<[f32; 3]>,
map: &mut std::collections::HashMap<(u32, u32), u32>|
-> u32 {
let key = if p < q { (p, q) } else { (q, p) };
if let Some(&idx) = map.get(&key) {
return idx;
}
let mp = midpoint(positions[p as usize], positions[q as usize]);
let idx = positions.len() as u32;
positions.push(mp);
map.insert(key, idx);
idx
};
let ab = get_mid(a, b, &mut new_pos, &mut edge_map);
let bc = get_mid(b, c, &mut new_pos, &mut edge_map);
let ca = get_mid(c, a, &mut new_pos, &mut edge_map);
new_idx.extend_from_slice(&[a, ab, ca]);
new_idx.extend_from_slice(&[ab, b, bc]);
new_idx.extend_from_slice(&[ca, bc, c]);
new_idx.extend_from_slice(&[ab, bc, ca]);
}
let _ = n;
MeshLevel {
positions: new_pos,
indices: new_idx,
level: level.level.saturating_sub(1),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn simple_mesh() -> (Vec<[f32; 3]>, Vec<u32>) {
let pos = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.5, 1.0, 0.0]];
let idx = vec![0, 1, 2];
(pos, idx)
}
fn quad_mesh() -> (Vec<[f32; 3]>, Vec<u32>) {
let pos = vec![
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[1.0, 1.0, 0.0],
[0.0, 1.0, 0.0],
];
let idx = vec![0, 1, 2, 0, 2, 3];
(pos, idx)
}
#[test]
fn test_build_multi_res_levels() {
let (pos, idx) = quad_mesh();
let cfg = MultiResConfig {
levels: 3,
reduction_ratio: 0.5,
};
let mr = build_multi_res(&pos, &idx, &cfg);
assert!(level_count(&mr) >= 1);
}
#[test]
fn test_finest_level_face_count() {
let (pos, idx) = quad_mesh();
let cfg = default_multi_res_config();
let mr = build_multi_res(&pos, &idx, &cfg);
let finest = finest_level(&mr).expect("should succeed");
assert_eq!(finest.indices.len() / 3, 2);
}
#[test]
fn test_coarsest_level_fewer_faces() {
let (pos, idx) = quad_mesh();
let cfg = MultiResConfig {
levels: 3,
reduction_ratio: 0.5,
};
let mr = build_multi_res(&pos, &idx, &cfg);
let finest_fc = finest_level(&mr).expect("should succeed").indices.len() / 3;
let coarsest_fc = coarsest_level(&mr).expect("should succeed").indices.len() / 3;
assert!(coarsest_fc <= finest_fc);
}
#[test]
fn test_level_face_counts() {
let (pos, idx) = quad_mesh();
let cfg = MultiResConfig {
levels: 3,
reduction_ratio: 0.5,
};
let mr = build_multi_res(&pos, &idx, &cfg);
let counts = level_face_counts(&mr);
assert_eq!(counts.len(), mr.levels.len());
}
#[test]
fn test_get_level_some() {
let (pos, idx) = quad_mesh();
let cfg = default_multi_res_config();
let mr = build_multi_res(&pos, &idx, &cfg);
assert!(get_level(&mr, 0).is_some());
}
#[test]
fn test_get_level_out_of_bounds() {
let (pos, idx) = quad_mesh();
let cfg = default_multi_res_config();
let mr = build_multi_res(&pos, &idx, &cfg);
assert!(get_level(&mr, 999).is_none());
}
#[test]
fn test_to_json() {
let (pos, idx) = quad_mesh();
let cfg = default_multi_res_config();
let mr = build_multi_res(&pos, &idx, &cfg);
let j = multi_res_to_json(&mr);
assert!(j.contains("level_count"));
}
#[test]
fn test_midpoint_upsample_increases_faces() {
let (pos, idx) = simple_mesh();
let level = MeshLevel {
positions: pos,
indices: idx,
level: 1,
};
let up = midpoint_upsample(&level);
assert_eq!(up.indices.len() / 3, 4);
}
#[test]
fn test_empty_mesh_one_level() {
let cfg = default_multi_res_config();
let mr = build_multi_res(&[], &[], &cfg);
assert_eq!(level_count(&mr), 1);
}
#[test]
fn test_default_config() {
let cfg = default_multi_res_config();
assert_eq!(cfg.levels, 4);
assert!((cfg.reduction_ratio - 0.5).abs() < 1e-6);
}
}