where
Self::Facet: FromGeometry,
T: AsRef<[K]>,
{
let keys = keys.as_ref();
if keys.len() == N {
self.indices.extend(keys.iter());
Ok(())
}
else {
// TODO: These numbers do not necessarily represent arity (i.e., the
// number of edges of each topological structure). Use a
// different error variant to express this.
Err(BufferError::ArityConflict {
expected: N,
actual: keys.len(),
})
}
}
}
impl FacetBuilder for BufferBuilder
where
P: Grouping + Topological,
P::Vertex: Copy + Hash + Integer + Unsigned,
Vec: IndexBuffer
,
{
type Facet = ();
type Key = ();
fn insert_facet(&mut self, keys: T, _: U) -> Result
where
Self::Facet: FromGeometry,
T: AsRef<[P::Vertex]>,
{
let arity = keys.as_ref().len();
P::try_from_slice(keys)
.ok_or(BufferError::ArityConflict {
expected: P::ARITY.into_interval().0,
actual: arity,
})
.map(|polygon| self.indices.push(polygon))
}
}
impl MeshBuilder for BufferBuilder
where
Self: SurfaceBuilder,
R: Grouping,
VertexKey: Hash,
Vec: IndexBuffer,
{
type Builder = Self;
type Vertex = G;
type Facet = ();
fn surface_with(&mut self, f: F) -> Result
where
Self::Error: From,
F: FnOnce(&mut Self::Builder) -> Result,
{
f(self).map_err(|error| error.into())
}
}
impl SurfaceBuilder for BufferBuilder
where
Self: FacetBuilder, Facet = ()>,
Self::Error: From, // TODO: Why is this necessary?
R: Grouping,
VertexKey: Hash + NumCast,
Vec: IndexBuffer,
{
type Builder = Self;
type Key = VertexKey;
type Vertex = G;
type Facet = ();
fn facets_with(&mut self, f: F) -> Result
where
Self::Error: From,
F: FnOnce(&mut Self::Builder) -> Result,
{
f(self).map_err(|error| error.into())
}
fn insert_vertex(&mut self, data: T) -> Result
where
Self::Vertex: FromGeometry,
{
let key = as NumCast>::from(self.vertices.len())
.ok_or(BufferError::IndexOverflow)?;
self.vertices.push(data.into_geometry());
Ok(key)
}
}
impl Transact<::Input> for BufferBuilder
where
R: Grouping,
Vec: IndexBuffer,
{
type Commit = MeshBuffer;
type Abort = ();
type Error = BufferError;
fn commit(self) -> Result {
let BufferBuilder { indices, vertices } = self;
Ok(MeshBuffer::from_raw_buffers_unchecked(indices, vertices))
}
fn abort(self) -> Self::Abort {}
}
================================================
FILE: plexus/src/buffer/mod.rs
================================================
//! Linear representations of polygonal meshes.
//!
//! This module provides types and traits that describe polygonal meshes as
//! buffers of vertex data and buffers of indices into that vertex data. These
//! buffers are called the _vertex buffer_ and _index buffer_, respectively, and
//! are typically used for indexed drawing. The [`MeshBuffer`] type unifies
//! vertex and index buffers and maintains their consistency.
//!
//! Note that only _composite_ vertex buffers are supported, in which each
//! element of a vertex buffer completely describes all attributes of a vertex.
//! Plexus does not support _component_ buffers, in which each attribute of a
//! vertex is stored in a dedicated buffer.
//!
//! Plexus refers to independent vertex and index buffers as _raw buffers_. For
//! example, a [`Vec`] of index data is a raw buffer and can be modified without
//! regard to consistency with any particular vertex buffer. The
//! [`FromRawBuffers`] trait provides a way to construct mesh data structures
//! from such raw buffers.
//!
//! Index buffers may contain either _flat_ or _structured_ data. See the
//! [`index`] module for more about these buffers and how they are defined.
//!
//! # Examples
//!
//! Generating a flat [`MeshBuffer`] from a [$uv$-sphere][`UvSphere`]:
//!
//! ```rust
//! # extern crate decorum;
//! # extern crate nalgebra;
//! # extern crate plexus;
//! #
//! use decorum::R32;
//! use nalgebra::Point3;
//! use plexus::buffer::MeshBuffer3;
//! use plexus::prelude::*;
//! use plexus::primitive::generate::Position;
//! use plexus::primitive::sphere::UvSphere;
//!
//! let buffer: MeshBuffer3> = UvSphere::new(16, 16)
//! .polygons::>>()
//! .triangulate()
//! .collect();
//! let indices = buffer.as_index_slice();
//! let positions = buffer.as_vertex_slice();
//! ```
//!
//! Converting a [`MeshGraph`] to a structured [`MeshBuffer`]:
//!
//! ```rust
//! # extern crate decorum;
//! # extern crate nalgebra;
//! # extern crate plexus;
//! #
//! use decorum::R64;
//! use nalgebra::Point3;
//! use plexus::buffer::MeshBufferN;
//! use plexus::graph::MeshGraph;
//! use plexus::prelude::*;
//! use plexus::primitive::cube::Cube;
//! use plexus::primitive::generate::Position;
//!
//! type E3 = Point3;
//!
//! let graph: MeshGraph = Cube::new().polygons::>().collect();
//! let buffer: MeshBufferN = graph.to_mesh_by_vertex().unwrap();
//! ```
//!
//! [`Vec`]: std::vec::Vec
//! [`FromRawBuffers`]: crate::buffer::FromRawBuffers
//! [`MeshBuffer`]: crate::buffer::MeshBuffer
//! [`MeshGraph`]: crate::graph::MeshGraph
//! [`index`]: crate::index
//! [`UvSphere`]: crate::primitive::sphere::UvSphere
// `MeshBuffer`s must convert and sum indices into their vertex data. Some of
// these conversions may fail, but others are never expected to fail, because of
// invariants enforced by `MeshBuffer`.
//
// Index types require `Unsigned` and `Vec` capacity is limited by word size
// (the width of `usize`). An overflow cannot occur in some contexts, because a
// consistent `MeshBuffer` cannot index into a `Vec` with an index larger than
// its maximum addressable capacity (the maximum value that `usize` can
// represent).
// TODO: More consistently `expect` or `ok_or` index conversions and sums.
mod builder;
use itertools::Itertools;
use num::{Integer, NumCast, Unsigned};
use std::fmt::Debug;
use std::hash::Hash;
use std::iter::FromIterator;
use std::vec;
use theon::adjunct::Map;
use thiserror::Error;
use typenum::{self, NonZero, Unsigned as _, U3, U4};
use crate::buffer::builder::BufferBuilder;
use crate::builder::{Buildable, MeshBuilder};
use crate::constant::{Constant, ToType, TypeOf};
use crate::encoding::{FaceDecoder, FromEncoding, VertexDecoder};
use crate::geometry::{FromGeometry, IntoGeometry};
use crate::index::{
BufferOf, Flat, Flat3, Flat4, FromIndexer, Grouping, HashIndexer, IndexBuffer, IndexOf,
IndexVertices, Indexer, Push,
};
use crate::primitive::decompose::IntoVertices;
use crate::primitive::{
BoundedPolygon, IntoIndexed, IntoPolygons, Polygonal, Tetragon, Topological, Trigon,
UnboundedPolygon,
};
use crate::{Arity, DynamicArity, MeshArity, Monomorphic, StaticArity, TryFromIterator};
/// Errors concerning raw buffers and [`MeshBuffer`]s.
///
/// [`MeshBuffer`]: crate::buffer::MeshBuffer
#[derive(Debug, Eq, Error, PartialEq)]
pub enum BufferError {
/// An index into vertex data is out of bounds.
///
/// This error occurs when an index read from an index buffer is out of
/// bounds of the vertex buffer it indexes.
#[error("index into vertex data out of bounds")]
IndexOutOfBounds,
/// The computation of an index causes an overflow.
///
/// This error occurs if the result of computing an index overflows the type
/// used to represent indices. For example, if the elements of an index
/// buffer are `u8`s and an operation results in indices greater than
/// [`u8::MAX`], then this error will occur.
///
/// [`u8::MAX`]: std::u8::MAX
#[error("index overflow")]
IndexOverflow,
#[error("index buffer conflicts with arity")]
/// The number of indices in a flat index buffer is incompatible with the
/// arity of the buffer.
///
/// This error may occur if a flat index buffer contains a number of indices
/// that is indivisible by the arity of the topology it represents. For
/// example, this may error occur if a triangular index buffer contains a
/// number of indices that is not divisible by three.
IndexUnaligned,
/// The arity of a buffer or other data structure is not compatible with an
/// operation.
#[error("conflicting arity; expected {expected}, but got {actual}")]
ArityConflict {
/// The expected arity.
expected: usize,
/// The incompatible arity that was encountered.
actual: usize,
},
}
/// Triangular [`MeshBuffer`].
///
/// The index buffer for this type contains [`Trigon`]s. For applications where
/// a flat index buffer is necessary, consider [`IntoFlatIndex`] or the
/// [`Flat3`] meta-grouping.
///
/// [`IntoFlatIndex`]: crate::buffer::IntoFlatIndex
/// [`MeshBuffer`]: crate::buffer::MeshBuffer
/// [`Flat3`]: crate::index::Flat3
/// [`Trigon`]: crate::primitive::Trigon
pub type MeshBuffer3 = MeshBuffer, G>;
/// Quadrilateral [`MeshBuffer`].
///
/// The index buffer for this type contains [`Tetragon`]s. For applications
/// where a flat index buffer is necessary, consider [`IntoFlatIndex`] or the
/// [`Flat4`] meta-grouping.
///
/// [`IntoFlatIndex`]: crate::buffer::IntoFlatIndex
/// [`MeshBuffer`]: crate::buffer::MeshBuffer
/// [`Flat4`]: crate::index::Flat4
/// [`Tetragon`]: crate::primitive::Tetragon
pub type MeshBuffer4 = MeshBuffer, G>;
/// [`MeshBuffer`] that supports polygons with arbitrary [arity][`Arity`].
///
/// [`MeshBuffer`]: crate::buffer::MeshBuffer
/// [`Arity`]: crate::Arity
pub type MeshBufferN = MeshBuffer, G>;
/// Conversion from raw buffers.
pub trait FromRawBuffers: Sized {
type Error: Debug;
/// Creates a type from raw buffers.
///
/// # Errors
///
/// Returns an error if the raw buffers are inconsistent or the implementor
/// cannot be constructed from the buffers. The latter typically occurs if
/// the given topology is unsupported.
fn from_raw_buffers(indices: I, vertices: J) -> Result
where
I: IntoIterator- ,
J: IntoIterator
- ;
}
/// Conversion from raw buffers that do not encode their arity.
pub trait FromRawBuffersWithArity: Sized {
type Error: Debug;
/// Creates a type from raw buffers with the given arity.
///
/// # Errors
///
/// Returns an error if the raw buffers are inconsistent, the raw buffers
/// are incompatible with the given arity, or the implementor cannot be
/// constructed from the buffers. The latter typically occurs if the given
/// topology is unsupported.
fn from_raw_buffers_with_arity(
indices: I,
vertices: J,
arity: usize,
) -> Result
where
I: IntoIterator
- ,
J: IntoIterator
- ;
}
// TODO: Provide a similar trait for index buffers instead. `MeshBuffer` could
// use such a trait to provide this API.
pub trait IntoFlatIndex
where
Constant: ToType,
TypeOf: NonZero,
{
type Item: Copy + Integer + Unsigned;
fn into_flat_index(self) -> MeshBuffer, G>;
}
// TODO: Provide a similar trait for index buffers instead. `MeshBuffer` could
// use such a trait to provide this API.
pub trait IntoStructuredIndex
where
::Vertex: Copy + Integer + Unsigned,
{
type Item: Polygonal;
fn into_structured_index(self) -> MeshBuffer;
}
/// Polygonal mesh composed of vertex and index buffers.
///
/// A `MeshBuffer` is a linear representation of a polygonal mesh that is
/// composed of two separate but related linear buffers: an _index buffer_ and a
/// _vertex buffer_. The index buffer contains ordered indices into the data in
/// the vertex buffer and describes the topology of the mesh. The vertex buffer
/// contains arbitrary data that describes each vertex.
///
/// `MeshBuffer` only explicitly respresents vertices via the vertex buffer and
/// surfaces via the index buffer. There is no explicit representation of
/// structures like edges and faces.
///
/// The `R` type parameter specifies the [`Grouping`] of the index buffer. See
/// the [`index`] module documention for more information.
///
/// [`Grouping`]: crate::index::Grouping
/// [`index`]: crate::index
#[derive(Debug)]
pub struct MeshBuffer
where
R: Grouping,
{
indices: Vec,
vertices: Vec,
}
impl MeshBuffer
where
R: Grouping,
Vec: IndexBuffer,
{
pub(in crate::buffer) fn from_raw_buffers_unchecked(
indices: Vec,
vertices: Vec,
) -> Self {
MeshBuffer { indices, vertices }
}
/// Creates an empty `MeshBuffer`.
///
/// # Examples
///
/// ```rust
/// use plexus::buffer::MeshBuffer;
/// use plexus::index::Flat3;
///
/// let buffer = MeshBuffer::, (f64, f64, f64)>::new();
/// ```
pub fn new() -> Self {
Self::default()
}
}
impl MeshBuffer
where
R: Grouping,
{
/// Converts a `MeshBuffer` into its index and vertex buffers.
pub fn into_raw_buffers(self) -> (Vec, Vec) {
let MeshBuffer { indices, vertices } = self;
(indices, vertices)
}
/// Maps over the vertex data in a `MeshBuffer`.
///
/// # Examples
///
/// Translating the position data in a buffer:
///
/// ```rust
/// # extern crate decorum;
/// # extern crate nalgebra;
/// # extern crate plexus;
/// #
/// use decorum::R64;
/// use nalgebra::{Point3, Vector3};
/// use plexus::buffer::MeshBuffer3;
/// use plexus::prelude::*;
/// use plexus::primitive::generate::Position;
/// use plexus::primitive::sphere::UvSphere;
///
/// let buffer: MeshBuffer3> = UvSphere::new(16, 8)
/// .polygons::>>()
/// .triangulate()
/// .collect();
/// // Translate the positions.
/// let translation = Vector3::::x() * 2.0;
/// let buffer = buffer.map_vertices(|position| position + translation);
/// ```
pub fn map_vertices(self, f: F) -> MeshBuffer
where
F: FnMut(G) -> H,
{
let (indices, vertices) = self.into_raw_buffers();
MeshBuffer {
indices,
vertices: vertices.into_iter().map(f).collect::>(),
}
}
/// Gets a slice over the index data.
pub fn as_index_slice(&self) -> &[R::Group] {
self.indices.as_slice()
}
/// Gets a slice over the vertex data.
pub fn as_vertex_slice(&self) -> &[G] {
self.vertices.as_slice()
}
}
/// Exposes a [`MeshBuilder`] that can be used to construct a [`MeshBuffer`]
/// incrementally from _surfaces_ and _facets_.
///
/// Note that the facet data for [`MeshBuffer`] is always the unit type `()`.
///
/// See the documentation for the [`builder`] module.
///
/// # Examples
///
/// Creating a [`MeshBuffer`] from a triangle:
///
/// ```rust
/// # extern crate nalgebra;
/// # extern crate plexus;
/// #
/// use nalgebra::Point2;
/// use plexus::buffer::MeshBuffer3;
/// use plexus::builder::Buildable;
/// use plexus::prelude::*;
///
/// let mut builder = MeshBuffer3::>::builder();
/// let buffer = builder
/// .surface_with(|builder| {
/// let a = builder.insert_vertex((0.0, 0.0))?;
/// let b = builder.insert_vertex((1.0, 0.0))?;
/// let c = builder.insert_vertex((0.0, 1.0))?;
/// builder.facets_with(|builder| builder.insert_facet(&[a, b, c], ()))
/// })
/// .and_then(|_| builder.build())
/// .unwrap();
/// ```
///
/// [`MeshBuffer`]: crate::buffer::MeshBuffer
/// [`MeshBuilder`]: crate::builder::MeshBuilder
/// [`builder`]: crate::builder
impl Buildable for MeshBuffer
where
R: Grouping,
Vec: IndexBuffer,
BufferBuilder: MeshBuilder,
{
type Builder = BufferBuilder;
type Error = BufferError;
type Vertex = G;
type Facet = ();
fn builder() -> Self::Builder {
BufferBuilder::default()
}
}
impl Default for MeshBuffer
where
R: Grouping,
Vec: IndexBuffer,
{
fn default() -> Self {
MeshBuffer {
indices: Default::default(),
vertices: Default::default(),
}
}
}
impl DynamicArity for MeshBuffer, G>
where
Constant: ToType,
TypeOf: NonZero,
T: Copy + Integer + Unsigned,
{
type Dynamic = as StaticArity>::Static;
fn arity(&self) -> Self::Dynamic {
Flat::::ARITY
}
}
impl
DynamicArity for MeshBuffer
where
P: Grouping + Monomorphic + Polygonal,
P::Vertex: Copy + Integer + Unsigned,
{
type Dynamic =
::Static;
fn arity(&self) -> Self::Dynamic {
P::ARITY
}
}
impl DynamicArity for MeshBuffer, G>
where
N: Copy + Integer + Unsigned,
{
type Dynamic = MeshArity;
fn arity(&self) -> Self::Dynamic {
MeshArity::from_components::, _>(self.indices.iter())
}
}
impl DynamicArity for MeshBuffer, G>
where
N: Copy + Integer + Unsigned,
{
type Dynamic = MeshArity;
fn arity(&self) -> Self::Dynamic {
MeshArity::from_components::, _>(self.indices.iter())
}
}
impl Monomorphic for MeshBuffer where R: Grouping + Monomorphic {}
impl StaticArity for MeshBuffer
where
R: Grouping,
{
type Static = ::Static;
const ARITY: Self::Static = R::ARITY;
}
impl MeshBuffer, G>
where
Constant: ToType,
TypeOf: NonZero,
T: Copy + Integer + NumCast + Unsigned,
{
/// Appends the contents of a flat `MeshBuffer` into another `MeshBuffer`.
/// The source buffer is drained.
///
/// # Errors
///
/// Returns an error if an index overflows.
pub fn append(&mut self, buffer: &mut MeshBuffer) -> Result<(), BufferError>
where
G: FromGeometry,
R: Grouping,
R::Group: Into< as Grouping>::Group>,
{
let offset = T::from(self.vertices.len()).ok_or(BufferError::IndexOverflow)?;
self.vertices.extend(
buffer
.vertices
.drain(..)
.map(|vertex| vertex.into_geometry()),
);
self.indices
.extend(buffer.indices.drain(..).map(|index| index.into() + offset));
Ok(())
}
}
impl MeshBuffer
where
P: Grouping + Polygonal,
P::Vertex: Copy + Integer + NumCast + Unsigned,
{
/// Appends the contents of a structured `MeshBuffer` into another
/// `MeshBuffer`. The source buffer is drained.
///
/// # Errors
///
/// Returns an error if an index overflows.
pub fn append(&mut self, buffer: &mut MeshBuffer) -> Result<(), BufferError>
where
G: FromGeometry,
R: Grouping,
R::Group: Into<::Group>,
::Group:
Map::Group> + Topological,
{
let offset =
::from(self.vertices.len()).ok_or(BufferError::IndexOverflow)?;
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)),
);
Ok(())
}
}
impl From
for MeshBuffer
where
P: IntoIndexed