use float_cmp::ApproxEq;
use std::collections::HashMap;
use std::io::Result;
#[derive(Default, Clone, Copy, Debug, PartialEq, PartialOrd)]
pub struct Vector<F>(pub [F; 3]);
impl<F> Vector<F> {
pub const fn new(v: [F; 3]) -> Self {
Self(v)
}
}
impl<F> From<Vector<F>> for [F; 3] {
fn from(v: Vector<F>) -> Self {
v.0
}
}
impl<F> std::ops::Index<usize> for Vector<F> {
type Output = F;
fn index(&self, i: usize) -> &Self::Output {
assert!(i < 3);
&self.0[i]
}
}
impl<'a, M: Copy + Default, F: Copy + ApproxEq<Margin = M>> ApproxEq for &'a Vector<F> {
type Margin = M;
fn approx_eq<T: Into<Self::Margin>>(self, other: Self, margin: T) -> bool {
let margin = margin.into();
self[0].approx_eq(other[0], margin)
&& self[1].approx_eq(other[1], margin)
&& self[2].approx_eq(other[2], margin)
}
}
pub type Vertex = Vector<f32>;
pub type Normal = Vector<f32>;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Triangle {
pub normal: Normal,
pub vertices: [Vertex; 3],
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct IndexedTriangle {
pub normal: Normal,
pub vertices: [usize; 3],
}
#[derive(Clone, Debug, PartialEq)]
pub struct IndexedMesh {
pub vertices: Vec<Vertex>,
pub faces: Vec<IndexedTriangle>,
}
impl IndexedMesh {
pub fn validate(&self) -> Result<()> {
let mut unconnected_edges: HashMap<(usize, usize), (usize, usize, usize)> = HashMap::new();
for (fi, face) in self.faces.iter().enumerate() {
{
let a = self.vertices[face.vertices[0]];
let b = self.vertices[face.vertices[1]];
let c = self.vertices[face.vertices[2]];
let area = super::utils::tri_area(a, b, c);
if area < f32::EPSILON {
return Err(::std::io::Error::new(
::std::io::ErrorKind::InvalidData,
format!("face #{} has a zero-area face", fi),
));
}
}
for i in 0..3 {
let u = face.vertices[i];
let v = face.vertices[(i + 1) % 3];
if unconnected_edges.contains_key(&(v, u)) {
unconnected_edges.remove(&(v, u));
} else {
unconnected_edges.insert((u, v), (fi, i, (i + 1) % 3));
}
}
}
if let Option::Some((fi, i1, i2)) = unconnected_edges.values().into_iter().next() {
Err(::std::io::Error::new(
::std::io::ErrorKind::InvalidData,
format!(
"did not find facing edge for face #{}, edge #v{} -> #v{}",
fi, i1, i2
),
))
} else {
Ok(())
}
}
pub fn into_triangle_vec(self) -> Vec<Triangle> {
self.faces
.iter()
.map(|a| Triangle {
normal: a.normal,
vertices: [
self.vertices[a.vertices[0]],
self.vertices[a.vertices[1]],
self.vertices[a.vertices[2]],
],
})
.collect()
}
}