use failure::Error;
use num::{Integer, NumCast, Unsigned};
use std::hash::Hash;
use std::iter::FromIterator;
use geometry::convert::IntoGeometry;
use primitive::{
Arity, FromIndexer, HashIndexer, IndexVertices, Indexer, IntoVertices, MapVerticesInto,
};
#[derive(Debug, Fail)]
pub enum BufferError {
#[fail(display = "index into vertex data out of bounds")]
IndexOutOfBounds,
}
pub struct MeshBuffer<N, V>
where
N: Integer + Unsigned,
{
indeces: Vec<N>,
vertices: Vec<V>,
}
impl<N, V> MeshBuffer<N, V>
where
N: Copy + Integer + NumCast + Unsigned,
{
pub fn new() -> Self {
Self::default()
}
pub fn from_raw_buffers<I, J>(indeces: I, vertices: J) -> Result<Self, Error>
where
I: IntoIterator<Item = N>,
J: IntoIterator<Item = V>,
{
let indeces = indeces.into_iter().collect::<Vec<_>>();
let vertices = vertices.into_iter().collect::<Vec<_>>();
let len = N::from(vertices.len()).unwrap();
if indeces.iter().any(|index| *index >= len) {
Err(BufferError::IndexOutOfBounds.into())
}
else {
Ok(MeshBuffer { indeces, vertices })
}
}
pub fn append<M, U>(&mut self, other: &mut MeshBuffer<M, U>)
where
M: Copy + Integer + Into<N> + Unsigned,
U: IntoGeometry<V>,
{
let offset = N::from(self.vertices.len()).unwrap();
self.vertices.extend(
other
.vertices
.drain(..)
.map(|vertex| vertex.into_geometry()),
);
self.indeces
.extend(other.indeces.drain(..).map(|index| index.into() + offset))
}
pub fn as_index_slice(&self) -> &[N] {
self.indeces.as_slice()
}
pub fn as_vertex_slice(&self) -> &[V] {
self.vertices.as_slice()
}
}
impl<N, V> Default for MeshBuffer<N, V>
where
N: Integer + Unsigned,
{
fn default() -> Self {
MeshBuffer {
indeces: vec![],
vertices: vec![],
}
}
}
impl<N, V, P> FromIndexer<P, P> for MeshBuffer<N, V>
where
P: Arity + MapVerticesInto<usize>,
P::Output: IntoVertices,
P::Vertex: IntoGeometry<V>,
N: Copy + Integer + NumCast + Unsigned,
{
fn from_indexer<I, M>(input: I, indexer: M) -> Self
where
I: IntoIterator<Item = P>,
M: Indexer<P, P::Vertex>,
{
let (indeces, vertices) = input.into_iter().index_vertices(indexer);
MeshBuffer::from_raw_buffers(
indeces
.into_iter()
.flat_map(|topology| topology.into_vertices())
.map(|index| N::from(index).unwrap()),
vertices.into_iter().map(|vertex| vertex.into_geometry()),
).unwrap()
}
}
impl<N, V, P> FromIterator<P> for MeshBuffer<N, V>
where
P: Arity + MapVerticesInto<usize>,
P::Output: IntoVertices,
P::Vertex: Eq + Hash + IntoGeometry<V>,
N: Copy + Integer + NumCast + Unsigned,
{
fn from_iter<I>(input: I) -> Self
where
I: IntoIterator<Item = P>,
{
Self::from_indexer(input, HashIndexer::default())
}
}
#[cfg(test)]
mod tests {
use nalgebra::Point3;
use buffer::*;
use graph::*;
use primitive::*;
#[test]
fn collect_topology_into_buffer() {
let buffer = sphere::UvSphere::new(3, 2)
.polygons_with_position() .triangulate()
.collect::<MeshBuffer<u32, Point3<f32>>>();
assert_eq!(18, buffer.as_index_slice().len());
assert_eq!(5, buffer.as_vertex_slice().len());
}
#[test]
fn convert_mesh_to_buffer_by_vertex() {
let mesh = sphere::UvSphere::new(3, 2)
.polygons_with_position() .collect::<Mesh<Point3<f32>>>();
let buffer = mesh.to_mesh_buffer_by_vertex::<u32, Point3<f32>>().unwrap();
assert_eq!(18, buffer.as_index_slice().len());
assert_eq!(5, buffer.as_vertex_slice().len());
}
#[test]
fn convert_mesh_to_buffer_by_face() {
let mesh = sphere::UvSphere::new(3, 2)
.polygons_with_position() .collect::<Mesh<Point3<f32>>>();
let buffer = mesh.to_mesh_buffer_by_face::<u32, Point3<f32>>().unwrap();
assert_eq!(18, buffer.as_index_slice().len());
assert_eq!(18, buffer.as_vertex_slice().len());
}
}