use nalgebra::Vector3;
use openmesh::MeshError;
use crate::base::Float;
use alloc::vec::Vec;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Triangle<T: Float> {
v1: Vector3<T>,
v2: Vector3<T>,
v3: Vector3<T>,
}
impl<T: Float> Triangle<T> {
#[inline]
pub fn new(v1: Vector3<T>, v2: Vector3<T>, v3: Vector3<T>) -> Self {
Self { v1, v2, v3 }
}
#[inline]
pub fn vertices(&self) -> [Vector3<T>; 3] {
[self.v1, self.v2, self.v3]
}
}
#[inline]
pub fn is_ray_hit<T: Float>(
triangle: Triangle<T>,
ray_origin: Vector3<T>,
ray_dir: Vector3<T>,
t_min: T,
t_max: T,
) -> bool {
let eps = T::epsilon() * T::from(16.0).unwrap();
let e1 = triangle.v2 - triangle.v1;
let e2 = triangle.v3 - triangle.v1;
let p = ray_dir.cross(&e2);
let det = e1.dot(&p);
if num_traits::Float::abs(det) < eps {
return false;
}
let inv_det = T::one() / det;
let tvec = ray_origin - triangle.v1;
let u = tvec.dot(&p) * inv_det;
if u < T::zero() || u > T::one() {
return false;
}
let q = tvec.cross(&e1);
let v = ray_dir.dot(&q) * inv_det;
if v < T::zero() || u + v > T::one() {
return false;
}
let t = e2.dot(&q) * inv_det;
if !(t_min..=t_max).contains(&t) {
return false;
}
true
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TriMesh<T: Float> {
triangles: Vec<Triangle<T>>,
}
impl<T: Float + core::iter::Sum> TriMesh<T> {
pub fn new<V, F>(vertices: V, faces: F) -> Result<Self, MeshError>
where
V: IntoIterator<Item = Vector3<T>>,
F: IntoIterator<Item = [usize; 3]>,
{
let vertices: Vec<Vector3<T>> = vertices.into_iter().collect();
let faces: Vec<[usize; 3]> = faces.into_iter().collect();
let v_val: Vec<openmesh::Vertex<T>> = vertices.iter().map(|&v| v.into()).collect();
let f_val: Vec<openmesh::Face> = faces.iter().map(|&f| f.into()).collect();
openmesh::core::validate_mesh(&v_val, &f_val, T::from(1e-4).unwrap())?;
Ok(Self::new_unchecked(vertices, faces))
}
#[cfg(feature = "io-stl")]
pub fn from_stl<R>(reader: &mut R) -> std::io::Result<Self>
where
R: std::io::Read + std::io::Seek,
{
let mesh: openmesh::Mesh<T> = openmesh::Mesh::from_stl(reader)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
mesh.validate()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))?;
let vertices: Vec<Vector3<T>> = mesh
.vertices
.into_iter()
.map(|v| Vector3::new(v.0, v.1, v.2))
.collect();
let faces: Vec<[usize; 3]> = mesh.faces.into_iter().map(|f| [f.0, f.1, f.2]).collect();
Ok(Self::new_unchecked(vertices, faces))
}
}
impl<T: Float> TriMesh<T> {
#[inline]
pub fn new_unchecked<V, F>(vertices: V, faces: F) -> Self
where
V: IntoIterator<Item = Vector3<T>>,
F: IntoIterator<Item = [usize; 3]>,
{
let vertices: Vec<_> = vertices.into_iter().collect();
let triangles = faces
.into_iter()
.map(|face| Triangle::new(vertices[face[0]], vertices[face[1]], vertices[face[2]]))
.collect();
Self { triangles }
}
#[inline]
pub fn triangles(&self) -> &[Triangle<T>] {
&self.triangles
}
#[inline]
pub fn from_triangles(triangles: Vec<Triangle<T>>) -> Self {
Self { triangles }
}
}
impl<T: Float + core::iter::Sum> From<openmesh::Mesh<T>> for TriMesh<T> {
fn from(mesh: openmesh::Mesh<T>) -> Self {
let vertices: Vec<Vector3<T>> = mesh
.vertices
.into_iter()
.map(|v| Vector3::new(v.0, v.1, v.2))
.collect();
let faces: Vec<[usize; 3]> = mesh.faces.into_iter().map(|f| [f.0, f.1, f.2]).collect();
Self::new_unchecked(vertices, faces)
}
}