use crate::GlyphItem;
pub fn edge_one_form_to_glyphs(
positions: &[[f32; 3]],
indices: &[u32],
edge_values: &[f32],
scale: f32,
) -> GlyphItem {
let num_tris = indices.len() / 3;
let n = num_tris.min(edge_values.len() / 3);
let mut glyph_positions = Vec::with_capacity(n);
let mut glyph_vectors = Vec::with_capacity(n);
for tri in 0..n {
let i0 = indices[3 * tri] as usize;
let i1 = indices[3 * tri + 1] as usize;
let i2 = indices[3 * tri + 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 e01 = p1 - p0;
let e12 = p2 - p1;
let e20 = p0 - p2;
let n_raw = e01.cross(-e20); let area2 = n_raw.length();
if area2 < 1e-12 {
continue; }
let face_normal = n_raw / area2;
let w01 = edge_values[3 * tri];
let w12 = edge_values[3 * tri + 1];
let w20 = edge_values[3 * tri + 2];
let f = (w01 * face_normal.cross(e01)
+ w12 * face_normal.cross(e12)
+ w20 * face_normal.cross(e20))
/ area2;
let centroid = (p0 + p1 + p2) / 3.0;
glyph_positions.push(centroid.to_array());
glyph_vectors.push(f.to_array());
}
let mut item = GlyphItem::default();
item.positions = glyph_positions;
item.vectors = glyph_vectors;
item.scale = scale;
item
}
#[cfg(test)]
mod tests {
use super::*;
fn xy_triangle() -> (Vec<[f32; 3]>, Vec<u32>) {
(
vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
vec![0, 1, 2],
)
}
#[test]
fn zero_edge_values_produce_zero_vector() {
let (pos, idx) = xy_triangle();
let edge_values = vec![0.0, 0.0, 0.0];
let item = edge_one_form_to_glyphs(&pos, &idx, &edge_values, 1.0);
assert_eq!(item.vectors.len(), 1);
for c in &item.vectors[0] {
assert!(c.abs() < 1e-6);
}
}
#[test]
fn linearity_scaling_edge_values_scales_output() {
let (pos, idx) = xy_triangle();
let ev1 = vec![1.0, 0.5, -0.5];
let ev2: Vec<f32> = ev1.iter().map(|v| v * 3.0).collect();
let item1 = edge_one_form_to_glyphs(&pos, &idx, &ev1, 1.0);
let item2 = edge_one_form_to_glyphs(&pos, &idx, &ev2, 1.0);
for i in 0..3 {
assert!(
(item2.vectors[0][i] - item1.vectors[0][i] * 3.0).abs() < 1e-4,
"linearity failed on component {i}"
);
}
}
#[test]
fn output_position_is_centroid() {
let (pos, idx) = xy_triangle();
let edge_values = vec![1.0, 0.0, 0.0];
let item = edge_one_form_to_glyphs(&pos, &idx, &edge_values, 1.0);
let c = item.positions[0];
let expected = [1.0 / 3.0, 1.0 / 3.0, 0.0];
for i in 0..3 {
assert!((c[i] - expected[i]).abs() < 1e-4);
}
}
#[test]
fn degenerate_triangle_skipped() {
let pos = vec![[0.0; 3]; 3];
let idx = vec![0u32, 1, 2];
let edge_values = vec![1.0, 1.0, 1.0];
let item = edge_one_form_to_glyphs(&pos, &idx, &edge_values, 1.0);
assert!(item.positions.is_empty());
}
#[test]
fn out_of_bounds_indices_skipped() {
let pos = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]];
let idx = vec![0u32, 1, 99];
let edge_values = vec![1.0, 1.0, 1.0];
let item = edge_one_form_to_glyphs(&pos, &idx, &edge_values, 1.0);
assert!(item.positions.is_empty());
}
#[test]
fn scale_forwarded() {
let (pos, idx) = xy_triangle();
let item = edge_one_form_to_glyphs(&pos, &idx, &[1.0, 0.0, 0.0], 5.0);
assert!((item.scale - 5.0).abs() < 1e-6);
}
#[test]
fn short_edge_values_truncates() {
let (pos, idx) = xy_triangle();
let item = edge_one_form_to_glyphs(&pos, &idx, &[1.0, 0.0], 1.0);
assert!(item.positions.is_empty());
}
}