use std::collections::HashMap;
use crate::data::{CellArray, PolyData};
pub fn to_triangle_strips(input: &PolyData) -> PolyData {
let nc = input.polys.num_cells();
if nc == 0 {
return input.clone();
}
let offsets = input.polys.offsets();
let conn = input.polys.connectivity();
let mut tri_verts: Vec<[i64; 3]> = Vec::with_capacity(nc);
for ci in 0..nc {
let start = offsets[ci] as usize;
let end = offsets[ci + 1] as usize;
if end - start == 3 {
tri_verts.push([conn[start], conn[start + 1], conn[start + 2]]);
}
}
let nt = tri_verts.len();
if nt == 0 {
return input.clone();
}
let mut edge_list: Vec<(u64, u32, u8)> = Vec::with_capacity(nt * 3); for (ti, tri) in tri_verts.iter().enumerate() {
for e in 0u8..3 {
let a = tri[e as usize];
let b = tri[((e + 1) % 3) as usize];
let key = if a < b { (a as u64) << 32 | b as u64 } else { (b as u64) << 32 | a as u64 };
edge_list.push((key, ti as u32, e));
}
}
edge_list.sort_unstable_by_key(|&(k, _, _)| k);
let mut tri_adj = vec![u32::MAX; nt * 3]; let n_edges = edge_list.len();
let mut i = 0;
while i < n_edges {
let key = edge_list[i].0;
if i + 1 < n_edges && edge_list[i + 1].0 == key {
let (_, t0, e0) = edge_list[i];
let (_, t1, e1) = edge_list[i + 1];
tri_adj[t0 as usize * 3 + e0 as usize] = t1;
tri_adj[t1 as usize * 3 + e1 as usize] = t0;
i += 2;
} else {
i += 1;
}
}
let mut edge_tris: HashMap<u64, [u32; 2]> = HashMap::with_capacity(nt * 3 / 2);
i = 0;
while i < n_edges {
let key = edge_list[i].0;
let t0 = edge_list[i].1;
let mut t1 = u32::MAX;
if i + 1 < n_edges && edge_list[i + 1].0 == key {
t1 = edge_list[i + 1].1;
i += 1;
}
edge_tris.insert(key, [t0, t1]);
i += 1;
}
let mut visited = vec![false; nt];
let mut strip_off: Vec<i64> = Vec::with_capacity(nt / 2 + 1);
let mut strip_conn: Vec<i64> = Vec::with_capacity(nt * 2);
strip_off.push(0);
for start in 0..nt {
if visited[start] {
continue;
}
visited[start] = true;
let tri = &tri_verts[start];
let _strip_start = strip_conn.len();
strip_conn.push(tri[0]);
strip_conn.push(tri[1]);
strip_conn.push(tri[2]);
loop {
let len = strip_conn.len();
let a = strip_conn[len - 2];
let b = strip_conn[len - 1];
let key = if a < b { (a as u64) << 32 | b as u64 } else { (b as u64) << 32 | a as u64 };
let next = edge_tris.get(&key).and_then(|pair| {
for &ti in pair {
if ti == u32::MAX { break; }
let ti = ti as usize;
if visited[ti] { continue; }
let t = &tri_verts[ti];
for &v in t {
if v != a && v != b {
return Some((ti, v));
}
}
}
None
});
if let Some((ti, new_pt)) = next {
visited[ti] = true;
strip_conn.push(new_pt);
} else {
break;
}
}
strip_off.push(strip_conn.len() as i64);
}
let mut pd = PolyData::new();
pd.points = input.points.clone();
pd.strips = CellArray::from_raw(strip_off, strip_conn);
pd.lines = input.lines.clone();
pd.verts = input.verts.clone();
pd
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn single_triangle_to_strip() {
let pd = PolyData::from_triangles(
vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.5, 1.0, 0.0]],
vec![[0, 1, 2]],
);
let result = to_triangle_strips(&pd);
assert_eq!(result.polys.num_cells(), 0);
assert_eq!(result.strips.num_cells(), 1);
assert_eq!(result.strips.cell(0).len(), 3);
}
#[test]
fn two_adjacent_triangles() {
let pd = PolyData::from_triangles(
vec![
[0.0, 0.0, 0.0], [1.0, 0.0, 0.0],
[0.5, 1.0, 0.0], [1.5, 1.0, 0.0],
],
vec![[0, 1, 2], [1, 3, 2]],
);
let result = to_triangle_strips(&pd);
let total_strip_verts: usize = result.strips.iter().map(|s| s.len()).sum();
assert!(total_strip_verts <= 5);
}
#[test]
fn preserves_points() {
let pd = PolyData::from_triangles(
vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.5, 1.0, 0.0]],
vec![[0, 1, 2]],
);
let result = to_triangle_strips(&pd);
assert_eq!(result.points.len(), 3);
}
}