use crate::render_wgpu::mesh::Vertex;
pub fn generate_clip_cap(
points: &[[f32; 3]],
triangles: &[[u32; 3]],
plane_normal: [f32; 3],
plane_distance: f32,
cap_color: [f32; 3],
) -> (Vec<Vertex>, Vec<u32>) {
let mut edge_points: Vec<[f32; 3]> = Vec::new();
for tri in triangles {
let p0 = points[tri[0] as usize];
let p1 = points[tri[1] as usize];
let p2 = points[tri[2] as usize];
let d0 = dot3(plane_normal, p0) + plane_distance;
let d1 = dot3(plane_normal, p1) + plane_distance;
let d2 = dot3(plane_normal, p2) + plane_distance;
let mut isects = Vec::new();
check_edge(p0, p1, d0, d1, &mut isects);
check_edge(p1, p2, d1, d2, &mut isects);
check_edge(p2, p0, d2, d0, &mut isects);
if isects.len() == 2 {
edge_points.push(isects[0]);
edge_points.push(isects[1]);
}
}
if edge_points.is_empty() {
return (Vec::new(), Vec::new());
}
let mut cx = 0.0f32;
let mut cy = 0.0f32;
let mut cz = 0.0f32;
let n = edge_points.len() as f32;
for p in &edge_points {
cx += p[0];
cy += p[1];
cz += p[2];
}
let centroid = [cx / n, cy / n, cz / n];
let mut vertices = Vec::new();
let mut indices = Vec::new();
vertices.push(Vertex {
position: centroid,
normal: plane_normal,
color: cap_color,
});
for pair in edge_points.chunks_exact(2) {
let base = vertices.len() as u32;
vertices.push(Vertex {
position: pair[0],
normal: plane_normal,
color: cap_color,
});
vertices.push(Vertex {
position: pair[1],
normal: plane_normal,
color: cap_color,
});
indices.push(0); indices.push(base);
indices.push(base + 1);
}
(vertices, indices)
}
pub fn extract_triangles(poly_data: &crate::data::PolyData) -> (Vec<[f32; 3]>, Vec<[u32; 3]>) {
let mut points = Vec::with_capacity(poly_data.points.len());
for i in 0..poly_data.points.len() {
let p = poly_data.points.get(i);
points.push([p[0] as f32, p[1] as f32, p[2] as f32]);
}
let mut tris = Vec::new();
for cell in poly_data.polys.iter() {
if cell.len() >= 3 {
for i in 1..cell.len() - 1 {
tris.push([cell[0] as u32, cell[i] as u32, cell[i + 1] as u32]);
}
}
}
(points, tris)
}
fn dot3(a: [f32; 3], b: [f32; 3]) -> f32 {
a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
}
fn check_edge(p0: [f32; 3], p1: [f32; 3], d0: f32, d1: f32, out: &mut Vec<[f32; 3]>) {
if (d0 > 0.0) != (d1 > 0.0) {
let t = d0 / (d0 - d1);
out.push([
p0[0] + t * (p1[0] - p0[0]),
p0[1] + t * (p1[1] - p0[1]),
p0[2] + t * (p1[2] - p0[2]),
]);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cap_single_triangle() {
let points = vec![
[0.0, 0.0, -1.0],
[1.0, 0.0, 1.0],
[0.0, 1.0, 1.0],
];
let tris = vec![[0, 1, 2]];
let (verts, idxs) = generate_clip_cap(
&points, &tris,
[0.0, 0.0, 1.0], 0.0, [1.0, 1.0, 1.0],
);
assert!(!verts.is_empty());
assert!(!idxs.is_empty());
assert_eq!(verts.len(), 3);
assert_eq!(idxs.len(), 3);
}
#[test]
fn cap_no_intersection() {
let points = vec![
[0.0, 0.0, 1.0],
[1.0, 0.0, 2.0],
[0.0, 1.0, 3.0],
];
let tris = vec![[0, 1, 2]];
let (verts, idxs) = generate_clip_cap(
&points, &tris,
[0.0, 0.0, 1.0], 0.0,
[1.0, 1.0, 1.0],
);
assert!(verts.is_empty());
assert!(idxs.is_empty());
}
}