use itertools::Itertools;
use num::{Integer, NumCast, ToPrimitive, Unsigned};
use std::hash::Hash;
use std::iter::FromIterator;
use std::marker::PhantomData;
use typenum::{self, NonZero};
use crate::geometry::convert::IntoGeometry;
use crate::primitive::decompose::IntoVertices;
use crate::primitive::index::{
FlatIndexVertices, FromIndexer, HashIndexer, IndexVertices, Indexer,
};
use crate::primitive::{Arity, Map, Polygon, Polygonal, Quad, Topological, Triangle};
use crate::FromRawBuffers;
pub use typenum::{U3, U4};
#[derive(Debug, Fail)]
pub enum BufferError {
#[fail(display = "index into vertex data out of bounds")]
IndexOutOfBounds,
#[fail(display = "conflicting arity")]
ArityConflict,
}
pub trait IndexBuffer {
type Item;
const ARITY: Option<usize>;
}
#[derive(Debug)]
pub struct Flat<A = U3, N = usize>
where
A: NonZero + typenum::Unsigned,
N: Copy + Integer + NumCast + Unsigned,
{
phantom: PhantomData<(A, N)>,
}
impl<A, N> IndexBuffer for Flat<A, N>
where
A: NonZero + typenum::Unsigned,
N: Copy + Integer + NumCast + Unsigned,
{
type Item = N;
const ARITY: Option<usize> = Some(A::USIZE);
}
pub type Flat3<N = usize> = Flat<U3, N>;
pub type Flat4<N = usize> = Flat<U4, N>;
pub type MeshBuffer3<N, G> = MeshBuffer<Flat3<N>, G>;
pub type MeshBuffer4<N, G> = MeshBuffer<Flat4<N>, G>;
#[derive(Debug)]
pub struct Structured<P = Polygon<usize>>
where
P: Polygonal,
P::Vertex: Copy + Integer + NumCast + Unsigned,
{
phantom: PhantomData<P>,
}
impl<N> IndexBuffer for Structured<Polygon<N>>
where
N: Copy + Integer + NumCast + Unsigned,
{
type Item = Polygon<N>;
const ARITY: Option<usize> = None;
}
impl<P> IndexBuffer for Structured<P>
where
P: Arity + Polygonal,
P::Vertex: Copy + Integer + NumCast + Unsigned,
{
type Item = P;
const ARITY: Option<usize> = Some(<P as Arity>::ARITY);
}
pub type Structured3<N = usize> = Structured<Triangle<N>>;
pub type Structured4<N = usize> = Structured<Quad<N>>;
pub type StructuredN<N = usize> = Structured<Polygon<N>>;
pub type MeshBufferN<N, G> = MeshBuffer<StructuredN<N>, G>;
pub trait IntoFlatIndex<A, G>
where
A: NonZero + typenum::Unsigned,
{
type Item: Copy + Integer + NumCast + Unsigned;
fn into_flat_index(self) -> MeshBuffer<Flat<A, Self::Item>, G>;
}
pub trait IntoStructuredIndex<G>
where
Structured<Self::Item>: IndexBuffer,
<Self::Item as Topological>::Vertex: Copy + Integer + NumCast + Unsigned,
{
type Item: Polygonal;
fn into_structured_index(self) -> MeshBuffer<Structured<Self::Item>, G>;
}
#[derive(Debug)]
pub struct MeshBuffer<I, G>
where
I: IndexBuffer,
{
indices: Vec<I::Item>,
vertices: Vec<G>,
}
impl<I, G> MeshBuffer<I, G>
where
I: IndexBuffer,
{
pub fn new() -> Self {
Self::default()
}
pub fn into_raw_buffers(self) -> (Vec<I::Item>, Vec<G>) {
let MeshBuffer { indices, vertices } = self;
(indices, vertices)
}
pub fn map_vertices_into<H, F>(self, f: F) -> MeshBuffer<I, H>
where
F: FnMut(G) -> H,
{
let (indices, vertices) = self.into_raw_buffers();
MeshBuffer {
indices,
vertices: vertices.into_iter().map(f).collect::<Vec<_>>(),
}
}
pub fn arity(&self) -> Option<usize> {
I::ARITY
}
pub fn as_index_slice(&self) -> &[I::Item] {
self.indices.as_slice()
}
pub fn as_vertex_slice(&self) -> &[G] {
self.vertices.as_slice()
}
}
impl<I, G> Default for MeshBuffer<I, G>
where
I: IndexBuffer,
{
fn default() -> Self {
MeshBuffer {
indices: Default::default(),
vertices: Default::default(),
}
}
}
impl<A, N, G> MeshBuffer<Flat<A, N>, G>
where
A: NonZero + typenum::Unsigned,
N: Copy + Integer + NumCast + Unsigned,
{
pub fn append<U, H>(&mut self, buffer: &mut MeshBuffer<U, H>)
where
U: IndexBuffer,
U::Item: Into<<Flat<A, N> as IndexBuffer>::Item>,
H: IntoGeometry<G>,
{
let offset = N::from(self.vertices.len()).unwrap();
self.vertices.extend(
buffer
.vertices
.drain(..)
.map(|vertex| vertex.into_geometry()),
);
self.indices
.extend(buffer.indices.drain(..).map(|index| index.into() + offset))
}
}
impl<P, G> MeshBuffer<Structured<P>, G>
where
P: Polygonal,
P::Vertex: Copy + Integer + NumCast + Unsigned,
Structured<P>: IndexBuffer,
{
pub fn into_polygons(
self,
) -> impl Clone + Iterator<Item = <<Structured<P> as IndexBuffer>::Item as Map<G>>::Output>
where
G: Clone,
<Structured<P> as IndexBuffer>::Item: Map<G>,
<<Structured<P> as IndexBuffer>::Item as Map<G>>::Output: Clone,
<<Structured<P> as IndexBuffer>::Item as Topological>::Vertex: ToPrimitive,
{
let (indices, vertices) = self.into_raw_buffers();
indices
.into_iter()
.map(|polygon| {
polygon.map(|index| vertices[<usize as NumCast>::from(index).unwrap()].clone())
})
.collect::<Vec<_>>()
.into_iter()
}
pub fn append<U, H>(&mut self, buffer: &mut MeshBuffer<U, H>)
where
U: IndexBuffer,
U::Item: Into<<Structured<P> as IndexBuffer>::Item>,
H: IntoGeometry<G>,
<Structured<P> as IndexBuffer>::Item: Copy
+ Map<Output = <Structured<P> as IndexBuffer>::Item>
+ Topological<Vertex = P::Vertex>,
{
let offset = <P::Vertex as NumCast>::from(self.vertices.len()).unwrap();
self.vertices.extend(
buffer
.vertices
.drain(..)
.map(|vertex| vertex.into_geometry()),
);
self.indices.extend(
buffer
.indices
.drain(..)
.map(|topology| topology.into().map(|index| index + offset)),
)
}
}
impl<A, N, P, G> FromIndexer<P, P> for MeshBuffer<Flat<A, N>, G>
where
A: NonZero + typenum::Unsigned,
N: Copy + Integer + NumCast + Unsigned,
P: Arity + IntoVertices + Polygonal,
P::Vertex: IntoGeometry<G>,
{
type Error = BufferError;
fn from_indexer<I, M>(input: I, indexer: M) -> Result<Self, Self::Error>
where
I: IntoIterator<Item = P>,
M: Indexer<P, P::Vertex>,
{
let (indices, vertices) = input.into_iter().flat_index_vertices(indexer);
MeshBuffer::<Flat<_, _>, _>::from_raw_buffers(
indices.into_iter().map(|index| N::from(index).unwrap()),
vertices.into_iter().map(|vertex| vertex.into_geometry()),
)
}
}
impl<P, Q, G> FromIndexer<P, P> for MeshBuffer<Structured<Q>, G>
where
P: Map<usize> + Polygonal,
P::Output: Map<Q::Vertex>,
P::Vertex: IntoGeometry<G>,
Q: Polygonal,
Q::Vertex: Copy + Integer + NumCast + Unsigned,
Structured<Q>: IndexBuffer,
<Structured<Q> as IndexBuffer>::Item: Copy
+ From<<P::Output as Map<Q::Vertex>>::Output>
+ IntoVertices
+ Topological<Vertex = Q::Vertex>,
{
type Error = BufferError;
fn from_indexer<I, M>(input: I, indexer: M) -> Result<Self, Self::Error>
where
I: IntoIterator<Item = P>,
M: Indexer<P, P::Vertex>,
{
let (indices, vertices) = input.into_iter().index_vertices(indexer);
MeshBuffer::<Structured<_>, _>::from_raw_buffers(
indices
.into_iter()
.map(|topology| topology.map(|index| <Q::Vertex as NumCast>::from(index).unwrap())),
vertices.into_iter().map(|vertex| vertex.into_geometry()),
)
}
}
impl<A, N, P, G> FromIterator<P> for MeshBuffer<Flat<A, N>, G>
where
A: NonZero + typenum::Unsigned,
N: Copy + Integer + NumCast + Unsigned,
P: Arity + IntoVertices + Polygonal,
P::Vertex: Copy + Eq + Hash + IntoGeometry<G>,
{
fn from_iter<I>(input: I) -> Self
where
I: IntoIterator<Item = P>,
{
Self::from_indexer(input, HashIndexer::default()).unwrap_or_else(|_| Self::default())
}
}
impl<P, Q, G> FromIterator<P> for MeshBuffer<Structured<Q>, G>
where
P: Map<usize> + Polygonal,
P::Output: Map<Q::Vertex>,
P::Vertex: Copy + Eq + Hash + IntoGeometry<G>,
Q: Polygonal,
Q::Vertex: Copy + Integer + NumCast + Unsigned,
Structured<Q>: IndexBuffer,
<Structured<Q> as IndexBuffer>::Item: Copy
+ From<<P::Output as Map<Q::Vertex>>::Output>
+ IntoVertices
+ Topological<Vertex = Q::Vertex>,
{
fn from_iter<I>(input: I) -> Self
where
I: IntoIterator<Item = P>,
{
Self::from_indexer(input, HashIndexer::default()).unwrap_or_else(|_| Self::default())
}
}
impl<A, N, M, G> FromRawBuffers<M, G> for MeshBuffer<Flat<A, N>, G>
where
A: NonZero + typenum::Unsigned,
N: Copy + Integer + NumCast + Unsigned,
M: Copy + Integer + NumCast + Unsigned,
<Flat<A, N> as IndexBuffer>::Item: ToPrimitive,
{
type Error = BufferError;
fn from_raw_buffers<I, J>(indices: I, vertices: J) -> Result<Self, BufferError>
where
I: IntoIterator<Item = M>,
J: IntoIterator<Item = G>,
{
let indices = indices
.into_iter()
.map(|index| <<Flat<A, N> as IndexBuffer>::Item as NumCast>::from(index).unwrap())
.collect::<Vec<_>>();
if indices.len() % Flat::<A, N>::ARITY.unwrap() != 0 {
Err(BufferError::ArityConflict)
}
else {
let vertices = vertices.into_iter().collect::<Vec<_>>();
let len = N::from(vertices.len()).unwrap();
if indices.iter().any(|index| *index >= len) {
Err(BufferError::IndexOutOfBounds)
}
else {
Ok(MeshBuffer { indices, vertices })
}
}
}
}
impl<P, Q, G> FromRawBuffers<Q, G> for MeshBuffer<Structured<P>, G>
where
P: Polygonal,
P::Vertex: Copy + Integer + NumCast + Unsigned,
Q: Into<<Structured<P> as IndexBuffer>::Item>,
Structured<P>: IndexBuffer,
<Structured<P> as IndexBuffer>::Item: Copy + IntoVertices + Topological<Vertex = P::Vertex>,
{
type Error = BufferError;
fn from_raw_buffers<I, J>(indices: I, vertices: J) -> Result<Self, BufferError>
where
I: IntoIterator<Item = Q>,
J: IntoIterator<Item = G>,
{
let indices = indices
.into_iter()
.map(|topology| topology.into())
.collect::<Vec<_>>();
let vertices = vertices
.into_iter()
.map(|geometry| geometry)
.collect::<Vec<_>>();
let len = <P::Vertex as NumCast>::from(vertices.len()).unwrap();
if indices.iter().any(|polygon| {
polygon
.into_vertices()
.into_iter()
.any(|index| index >= len)
}) {
Err(BufferError::IndexOutOfBounds)
}
else {
Ok(MeshBuffer { indices, vertices })
}
}
}
impl<A, N, G> IntoFlatIndex<A, G> for MeshBuffer<Flat<A, N>, G>
where
A: NonZero + typenum::Unsigned,
N: Copy + Integer + NumCast + Unsigned,
{
type Item = N;
fn into_flat_index(self) -> MeshBuffer<Flat<A, Self::Item>, G> {
self
}
}
impl<N, G> IntoFlatIndex<U3, G> for MeshBuffer<Structured3<N>, G>
where
N: Copy + Integer + NumCast + Unsigned,
{
type Item = N;
fn into_flat_index(self) -> MeshBuffer<Flat<U3, Self::Item>, G> {
let MeshBuffer { indices, vertices } = self;
MeshBuffer {
indices: indices
.into_iter()
.flat_map(|triangle| triangle.into_vertices())
.collect(),
vertices,
}
}
}
impl<N, G> IntoFlatIndex<U4, G> for MeshBuffer<Structured4<N>, G>
where
N: Copy + Integer + NumCast + Unsigned,
{
type Item = N;
fn into_flat_index(self) -> MeshBuffer<Flat<U4, Self::Item>, G> {
let MeshBuffer { indices, vertices } = self;
MeshBuffer {
indices: indices
.into_iter()
.flat_map(|quad| quad.into_vertices())
.collect(),
vertices,
}
}
}
impl<P, G> IntoStructuredIndex<G> for MeshBuffer<Structured<P>, G>
where
P: Polygonal,
P::Vertex: Copy + Integer + NumCast + Unsigned,
Structured<P>: IndexBuffer,
{
type Item = P;
fn into_structured_index(self) -> MeshBuffer<Structured<Self::Item>, G> {
self
}
}
impl<N, G> IntoStructuredIndex<G> for MeshBuffer<Flat3<N>, G>
where
N: Copy + Integer + NumCast + Unsigned,
{
type Item = Triangle<N>;
fn into_structured_index(self) -> MeshBuffer<Structured<Self::Item>, G> {
let MeshBuffer { indices, vertices } = self;
MeshBuffer {
indices: indices
.into_iter()
.chunks(Flat3::<N>::ARITY.unwrap())
.into_iter()
.map(|triangle| <Structured<Self::Item> as IndexBuffer>::Item::from_iter(triangle))
.collect(),
vertices,
}
}
}
impl<N, G> IntoStructuredIndex<G> for MeshBuffer<Flat4<N>, G>
where
N: Copy + Integer + NumCast + Unsigned,
{
type Item = Quad<N>;
fn into_structured_index(self) -> MeshBuffer<Structured<Self::Item>, G> {
let MeshBuffer { indices, vertices } = self;
MeshBuffer {
indices: indices
.into_iter()
.chunks(Flat4::<N>::ARITY.unwrap())
.into_iter()
.map(|quad| <Structured<Self::Item> as IndexBuffer>::Item::from_iter(quad))
.collect(),
vertices,
}
}
}
#[cfg(test)]
mod tests {
use nalgebra::Point3;
use crate::buffer::*;
use crate::graph::*;
use crate::primitive::cube::Cube;
use crate::primitive::decompose::*;
use crate::primitive::generate::*;
use crate::primitive::sphere::UvSphere;
#[test]
fn collect_topology_into_flat_buffer() {
let buffer = UvSphere::new(3, 2)
.polygons_with_position() .triangulate()
.collect::<MeshBuffer3<u32, Point3<f32>>>();
assert_eq!(18, buffer.as_index_slice().len());
assert_eq!(5, buffer.as_vertex_slice().len());
}
#[test]
fn collect_topology_into_structured_buffer() {
let buffer = UvSphere::new(3, 2)
.polygons_with_position() .collect::<MeshBufferN<u32, Point3<f32>>>();
assert_eq!(6, buffer.as_index_slice().len());
assert_eq!(5, buffer.as_vertex_slice().len());
}
#[test]
fn append_structured_buffers() {
let mut buffer = UvSphere::new(3, 2)
.polygons_with_position() .collect::<MeshBuffer<StructuredN<u32>, Point3<f32>>>();
buffer.append(
&mut Cube::new()
.polygons_with_position() .collect::<MeshBuffer<Structured4<u32>, Point3<f32>>>(),
);
assert_eq!(12, buffer.as_index_slice().len());
assert_eq!(13, buffer.as_vertex_slice().len());
}
#[test]
fn convert_mesh_to_buffer_by_vertex() {
let graph = UvSphere::new(3, 2)
.polygons_with_position() .collect::<MeshGraph<Point3<f32>>>();
let buffer = graph
.to_mesh_buffer_by_vertex::<U3, 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 graph = UvSphere::new(3, 2)
.polygons_with_position() .collect::<MeshGraph<Point3<f32>>>();
let buffer = graph
.to_mesh_buffer_by_face::<U3, u32, Point3<f32>>()
.unwrap();
assert_eq!(18, buffer.as_index_slice().len());
assert_eq!(18, buffer.as_vertex_slice().len());
}
}