#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MirrorAxis {
X,
Y,
Z,
}
impl MirrorAxis {
#[allow(dead_code)]
pub fn index(self) -> usize {
match self {
Self::X => 0,
Self::Y => 1,
Self::Z => 2,
}
}
#[allow(dead_code)]
pub fn name(self) -> &'static str {
match self {
Self::X => "X",
Self::Y => "Y",
Self::Z => "Z",
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct MirrorCutResult {
pub positions: Vec<[f32; 3]>,
pub indices: Vec<u32>,
pub kept_count: usize,
pub discarded_count: usize,
}
#[allow(dead_code)]
pub fn mirror_cut(
positions: &[[f32; 3]],
indices: &[u32],
axis: MirrorAxis,
threshold: f32,
) -> MirrorCutResult {
let ax = axis.index();
let mut new_pos: Vec<[f32; 3]> = Vec::new();
let mut new_idx: Vec<u32> = Vec::new();
let mut kept = 0usize;
let mut discarded = 0usize;
let tri_count = indices.len() / 3;
for t in 0..tri_count {
let ia = indices[t * 3] as usize;
let ib = indices[t * 3 + 1] as usize;
let ic = indices[t * 3 + 2] as usize;
if ia >= positions.len() || ib >= positions.len() || ic >= positions.len() {
continue;
}
let pa = positions[ia];
let pb = positions[ib];
let pc = positions[ic];
if pa[ax] >= threshold && pb[ax] >= threshold && pc[ax] >= threshold {
let base = new_pos.len() as u32;
new_pos.push(pa);
new_pos.push(pb);
new_pos.push(pc);
new_idx.extend_from_slice(&[base, base + 1, base + 2]);
kept += 3;
} else {
discarded += 3;
}
}
let original_count = new_pos.len();
for i in 0..original_count {
let mut mirrored = new_pos[i];
mirrored[ax] = -mirrored[ax];
new_pos.push(mirrored);
}
let orig_tri_count = new_idx.len() / 3;
for t in 0..orig_tri_count {
let a = new_idx[t * 3] + original_count as u32;
let b = new_idx[t * 3 + 1] + original_count as u32;
let c = new_idx[t * 3 + 2] + original_count as u32;
new_idx.extend_from_slice(&[a, c, b]);
}
MirrorCutResult {
positions: new_pos,
indices: new_idx,
kept_count: kept,
discarded_count: discarded,
}
}
#[allow(dead_code)]
pub fn cut_positive_side(
positions: &[[f32; 3]],
axis: MirrorAxis,
threshold: f32,
) -> Vec<[f32; 3]> {
let ax = axis.index();
positions
.iter()
.copied()
.filter(|p| p[ax] >= threshold)
.collect()
}
#[allow(dead_code)]
pub fn cut_negative_side(
positions: &[[f32; 3]],
axis: MirrorAxis,
threshold: f32,
) -> Vec<[f32; 3]> {
let ax = axis.index();
positions
.iter()
.copied()
.filter(|p| p[ax] < threshold)
.collect()
}
#[allow(dead_code)]
pub fn count_positive_vertices(positions: &[[f32; 3]], axis: MirrorAxis, threshold: f32) -> usize {
let ax = axis.index();
positions.iter().filter(|p| p[ax] >= threshold).count()
}
#[allow(dead_code)]
pub fn axis_extent(positions: &[[f32; 3]], axis: MirrorAxis) -> (f32, f32) {
let ax = axis.index();
if positions.is_empty() {
return (0.0, 0.0);
}
let min = positions.iter().map(|p| p[ax]).fold(f32::MAX, f32::min);
let max = positions.iter().map(|p| p[ax]).fold(f32::MIN, f32::max);
(min, max)
}
#[allow(dead_code)]
pub fn mirror_cut_to_json(result: &MirrorCutResult) -> String {
format!(
"{{\"vertices\":{},\"indices\":{},\"kept\":{},\"discarded\":{}}}",
result.positions.len(),
result.indices.len(),
result.kept_count,
result.discarded_count
)
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_positions() -> Vec<[f32; 3]> {
vec![
[1.0, 0.0, 0.0],
[1.0, 1.0, 0.0],
[1.0, 0.0, 1.0],
[-1.0, 0.0, 0.0],
[-1.0, 1.0, 0.0],
[-1.0, 0.0, 1.0],
]
}
fn sample_indices() -> Vec<u32> {
vec![0, 1, 2, 3, 4, 5]
}
#[test]
fn test_mirror_axis_index() {
assert_eq!(MirrorAxis::X.index(), 0);
assert_eq!(MirrorAxis::Y.index(), 1);
assert_eq!(MirrorAxis::Z.index(), 2);
}
#[test]
fn test_mirror_axis_name() {
assert_eq!(MirrorAxis::X.name(), "X");
}
#[test]
fn test_cut_keeps_positive() {
let pos = sample_positions();
let idx = sample_indices();
let result = mirror_cut(&pos, &idx, MirrorAxis::X, 0.0);
assert!(result.kept_count > 0);
}
#[test]
fn test_cut_discards_negative() {
let pos = sample_positions();
let idx = sample_indices();
let result = mirror_cut(&pos, &idx, MirrorAxis::X, 0.0);
assert!(result.discarded_count > 0);
}
#[test]
fn test_mirror_doubles_triangles() {
let pos = sample_positions();
let idx = sample_indices();
let result = mirror_cut(&pos, &idx, MirrorAxis::X, 0.0);
assert_eq!(result.indices.len(), 6);
}
#[test]
fn test_cut_positive_side() {
let pos = sample_positions();
let kept = cut_positive_side(&pos, MirrorAxis::X, 0.0);
assert_eq!(kept.len(), 3);
}
#[test]
fn test_cut_negative_side() {
let pos = sample_positions();
let neg = cut_negative_side(&pos, MirrorAxis::X, 0.0);
assert_eq!(neg.len(), 3);
}
#[test]
fn test_count_positive() {
let pos = sample_positions();
assert_eq!(count_positive_vertices(&pos, MirrorAxis::X, 0.0), 3);
}
#[test]
fn test_axis_extent() {
let pos = sample_positions();
let (mn, mx) = axis_extent(&pos, MirrorAxis::X);
assert!((mn - (-1.0)).abs() < 1e-5);
assert!((mx - 1.0).abs() < 1e-5);
}
#[test]
fn test_to_json() {
let pos = sample_positions();
let idx = sample_indices();
let result = mirror_cut(&pos, &idx, MirrorAxis::X, 0.0);
let json = mirror_cut_to_json(&result);
assert!(json.contains("vertices"));
}
}