#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct IsolineItem {
pub positions: Vec<[f32; 3]>,
pub indices: Vec<u32>,
pub scalars: Vec<f32>,
pub isovalues: Vec<f32>,
pub color: [f32; 4],
pub line_width: f32,
pub model_matrix: glam::Mat4,
pub depth_bias: f32,
}
impl Default for IsolineItem {
fn default() -> Self {
Self {
positions: Vec::new(),
indices: Vec::new(),
scalars: Vec::new(),
isovalues: Vec::new(),
color: [1.0, 1.0, 1.0, 1.0],
line_width: 1.0,
model_matrix: glam::Mat4::IDENTITY,
depth_bias: 0.001,
}
}
}
pub fn extract_isolines(item: &IsolineItem) -> (Vec<[f32; 3]>, Vec<u32>) {
if item.positions.is_empty()
|| item.indices.is_empty()
|| item.scalars.is_empty()
|| item.isovalues.is_empty()
{
return (Vec::new(), Vec::new());
}
if item.scalars.len() != item.positions.len() {
return (Vec::new(), Vec::new());
}
let tri_count = item.indices.len() / 3;
let iso_count = item.isovalues.len();
let mut out_positions: Vec<[f32; 3]> = Vec::with_capacity(tri_count * iso_count * 2);
let mut strip_lengths: Vec<u32> = Vec::with_capacity(tri_count * iso_count);
for &iso in &item.isovalues {
extract_for_isovalue(item, iso, &mut out_positions, &mut strip_lengths);
}
(out_positions, strip_lengths)
}
fn extract_for_isovalue(
item: &IsolineItem,
iso: f32,
out_positions: &mut Vec<[f32; 3]>,
strip_lengths: &mut Vec<u32>,
) {
let positions = &item.positions;
let scalars = &item.scalars;
let model = item.model_matrix;
let bias = item.depth_bias;
let tri_count = item.indices.len() / 3;
for tri_idx in 0..tri_count {
let i0 = item.indices[tri_idx * 3] as usize;
let i1 = item.indices[tri_idx * 3 + 1] as usize;
let i2 = item.indices[tri_idx * 3 + 2] as usize;
if i0 >= positions.len() || i1 >= positions.len() || i2 >= positions.len() {
continue;
}
let p0 = glam::Vec3::from(positions[i0]);
let p1 = glam::Vec3::from(positions[i1]);
let p2 = glam::Vec3::from(positions[i2]);
let s0 = scalars[i0];
let s1 = scalars[i1];
let s2 = scalars[i2];
let edge_a = p1 - p0;
let edge_b = p2 - p0;
let cross = edge_a.cross(edge_b);
let len_sq = cross.length_squared();
if len_sq < f32::EPSILON {
continue; }
let face_normal = cross / len_sq.sqrt();
let edges = [(p0, p1, s0, s1), (p1, p2, s1, s2), (p2, p0, s2, s0)];
let mut crossings: [Option<glam::Vec3>; 3] = [None; 3];
for (edge_slot, &(ea, eb, sa, sb)) in edges.iter().enumerate() {
if let Some(p) = edge_crossing(ea, eb, sa, sb, iso) {
crossings[edge_slot] = Some(p);
}
}
let pts: Vec<glam::Vec3> = crossings.iter().filter_map(|&c| c).collect();
if pts.len() < 2 {
continue;
}
let a = pts[0];
let b = pts[1];
let bias_vec = face_normal * bias;
let wa = transform_point(model, a + bias_vec);
let wb = transform_point(model, b + bias_vec);
out_positions.push(wa);
out_positions.push(wb);
strip_lengths.push(2);
}
}
#[inline]
fn edge_crossing(pa: glam::Vec3, pb: glam::Vec3, sa: f32, sb: f32, iso: f32) -> Option<glam::Vec3> {
if (sb - sa).abs() < f32::EPSILON {
return None;
}
let crosses = (sa < iso && sb >= iso) || (sb < iso && sa >= iso);
if !crosses {
return None;
}
let t = (iso - sa) / (sb - sa);
Some(pa + t * (pb - pa))
}
#[inline]
fn transform_point(m: glam::Mat4, p: glam::Vec3) -> [f32; 3] {
m.transform_point3(p).to_array()
}