#![warn(missing_docs)]
use buffer::DataBuffer;
use crate::index::*;
use crate::mesh::topology::*;
#[cfg(feature = "rayon")]
use rayon::prelude::*;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::marker::PhantomData;
use std::slice;
pub use std::collections::hash_map::Entry;
pub type AttribDict<I> = HashMap<String, Attribute<I>>;
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Attribute<I> {
data: DataBuffer,
default_element: Box<[u8]>,
phantom: PhantomData<I>,
}
pub trait Bytes
where
Self: Sized,
{
#[inline]
fn as_bytes(&self) -> &[u8] {
let byte_ptr = self as *const Self as *const u8;
unsafe { std::slice::from_raw_parts(byte_ptr, std::mem::size_of::<Self>()) }
}
#[inline]
fn interpret_bytes(bytes: &[u8]) -> &Self {
assert_eq!(bytes.len(), std::mem::size_of::<Self>());
let ptr = bytes.as_ptr() as *const Self;
unsafe { &*ptr }
}
}
impl<T: Sized> Bytes for T {}
#[test]
fn bytes_from_value_test() {
let val = 10.2_f64;
assert_eq!(&val, Bytes::interpret_bytes(val.as_bytes()));
}
impl<I> Attribute<I> {
pub fn with_size<T: Any + Clone>(n: usize, def: T) -> Self {
Attribute {
data: DataBuffer::with_size(n, def.clone()),
default_element: def.as_bytes().into(),
phantom: PhantomData,
}
}
pub fn from_vec<T: Any + Clone + Default>(vec: Vec<T>) -> Self {
Attribute {
data: DataBuffer::from_vec(vec),
default_element: T::default().as_bytes().into(),
phantom: PhantomData,
}
}
pub fn from_data_buffer(data: DataBuffer, def: &[u8]) -> Self {
Attribute {
data,
default_element: def.into(),
phantom: PhantomData,
}
}
#[inline]
pub fn from_slice<T: Any + Clone + Default>(data: &[T]) -> Self {
Self::from_vec(data.to_vec())
}
#[inline]
pub fn check<T: Any>(&self) -> Result<&Self, Error> {
match self.data.check_ref::<T>() {
Some(_) => Ok(self),
None => Err(Error::TypeMismatch),
}
}
#[inline]
pub fn check_mut<T: Any>(&mut self) -> Result<&mut Self, Error> {
match self.data.check_mut::<T>() {
Some(_) => Ok(self),
None => Err(Error::TypeMismatch),
}
}
#[inline]
pub fn element_type_id(&self) -> TypeId {
self.data.element_type_id()
}
#[inline]
pub fn as_slice<T: Any>(&self) -> Result<&[T], Error> {
self.data.as_slice().ok_or(Error::TypeMismatch)
}
#[inline]
pub fn as_mut_slice<T: Any>(&mut self) -> Result<&mut [T], Error> {
self.data.as_mut_slice().ok_or(Error::TypeMismatch)
}
#[inline]
pub fn clone_into_vec<T: Any + Clone>(&self) -> Result<Vec<T>, Error> {
self.data.clone_into_vec().ok_or(Error::TypeMismatch)
}
#[inline]
pub fn copy_into_vec<T: Any + Copy>(&self) -> Result<Vec<T>, Error> {
self.data.copy_into_vec().ok_or(Error::TypeMismatch)
}
#[inline]
pub fn iter<'a, T: Any + 'a>(&'a self) -> Result<slice::Iter<T>, Error> {
self.data.iter::<T>().ok_or(Error::TypeMismatch)
}
#[inline]
pub fn iter_mut<'a, T: Any + 'a>(&'a mut self) -> Result<slice::IterMut<T>, Error> {
self.data.iter_mut::<T>().ok_or(Error::TypeMismatch)
}
#[inline]
pub fn len(&self) -> usize {
self.data.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
#[inline]
pub unsafe fn get_unchecked<T: Any + Copy>(&self, i: usize) -> T {
self.data.get_unchecked(i)
}
#[inline]
pub unsafe fn get_unchecked_ref<T: Any>(&self, i: usize) -> &T {
self.data.get_unchecked_ref(i)
}
#[inline]
pub unsafe fn get_unchecked_mut<T: Any>(&mut self, i: usize) -> &mut T {
self.data.get_unchecked_mut(i)
}
#[inline]
pub fn buffer_ref(&self) -> &DataBuffer {
&self.data
}
#[inline]
pub fn buffer_mut(&mut self) -> &mut DataBuffer {
&mut self.data
}
#[inline]
pub fn into_buffer(self) -> DataBuffer {
self.data
}
#[inline]
pub fn extend_by(&mut self, n: usize) {
let Attribute {
data,
default_element,
..
} = self;
for _ in 0..n {
data.push_bytes(&default_element);
}
}
#[inline]
pub fn rotate_left(&mut self, mid: usize) {
self.data.rotate_left(mid);
}
#[inline]
pub fn rotate_right(&mut self, k: usize) {
self.data.rotate_right(k);
}
#[inline]
pub fn default_bytes(&self) -> &[u8] {
&self.default_element
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct IntrinsicAttribute<T, I> {
data: Vec<T>,
phantom: PhantomData<I>,
}
impl<T, I> IntrinsicAttribute<T, I> {
pub fn with_size(n: usize, def: T) -> Self
where
T: Clone,
{
IntrinsicAttribute {
data: vec![def; n],
phantom: PhantomData,
}
}
pub fn from_vec(vec: Vec<T>) -> Self {
IntrinsicAttribute {
data: vec,
phantom: PhantomData,
}
}
#[inline]
pub fn from_slice(data: &[T]) -> Self
where
T: Clone,
{
Self::from_vec(data.to_vec())
}
#[inline]
pub fn as_slice(&self) -> &[T] {
self.data.as_slice()
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [T] {
self.data.as_mut_slice()
}
#[inline]
pub fn into_vec(self) -> Vec<T> {
self.data
}
#[inline]
pub fn as_mut_vec(&mut self) -> &mut Vec<T> {
&mut self.data
}
#[inline]
pub fn clone_into_vec(&self) -> Vec<T>
where
T: Clone,
{
self.data.clone()
}
#[inline]
pub fn copy_into_vec(&self) -> Vec<T>
where
T: Copy,
{
let mut vec = Vec::with_capacity(self.len());
vec.extend(self.as_slice());
vec
}
#[inline]
pub fn iter(&self) -> slice::Iter<T> {
self.data.iter()
}
#[cfg(feature = "rayon")]
#[inline]
pub fn par_iter(&self) -> rayon::slice::Iter<T>
where
T: Sync,
{
self.data.par_iter()
}
#[inline]
pub fn iter_mut(&mut self) -> slice::IterMut<T> {
self.data.iter_mut()
}
#[cfg(feature = "rayon")]
#[inline]
pub fn par_iter_mut(&mut self) -> rayon::slice::IterMut<T>
where
T: Sync + Send,
{
self.data.par_iter_mut()
}
#[inline]
pub fn len(&self) -> usize {
self.data.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
impl<T, I> From<Vec<T>> for IntrinsicAttribute<T, I> {
#[inline]
fn from(vec: Vec<T>) -> Self {
Self::from_vec(vec)
}
}
impl<T, I> Into<Vec<T>> for IntrinsicAttribute<T, I> {
#[inline]
fn into(self) -> Vec<T> {
self.into_vec()
}
}
impl<T, I: Into<usize>, J: Into<I>> std::ops::Index<J> for IntrinsicAttribute<T, I> {
type Output = T;
fn index(&self, index: J) -> &T {
&self.data[index.into().into()]
}
}
impl<T, I: Into<usize>, J: Into<I>> std::ops::IndexMut<J> for IntrinsicAttribute<T, I> {
fn index_mut(&mut self, index: J) -> &mut T {
&mut self.data[index.into().into()]
}
}
impl<T, I> std::iter::IntoIterator for IntrinsicAttribute<T, I> {
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.into_vec().into_iter()
}
}
impl<T, I> std::iter::FromIterator<T> for IntrinsicAttribute<T, I> {
fn from_iter<J>(iter: J) -> Self
where
J: IntoIterator<Item = T>,
{
Self::from_vec(Vec::from_iter(iter))
}
}
#[cfg(feature = "rayon")]
impl<T: Send, I> rayon::iter::IntoParallelIterator for IntrinsicAttribute<T, I> {
type Item = T;
type Iter = rayon::vec::IntoIter<T>;
fn into_par_iter(self) -> Self::Iter {
self.into_vec().into_par_iter()
}
}
pub trait AttribIndex<M>
where
Self: ::std::marker::Sized + Clone,
{
fn attrib_size(mesh: &M) -> usize;
fn attrib_dict(mesh: &M) -> &AttribDict<Self>;
fn attrib_dict_mut(mesh: &mut M) -> &mut AttribDict<Self>;
}
macro_rules! impl_attrib_index {
($topo_attrib:ident, $type:ty, $topo_num:ident) => {
impl Attribute<$type> {
#[inline]
pub fn get<T: Any + Copy, I: Into<$type>>(&self, i: I) -> Result<T, Error> {
Index::from(i.into())
.map_or(None, move |x| self.data.get(x))
.ok_or(Error::TypeMismatch)
}
#[inline]
pub fn get_ref<T: Any, I: Into<$type>>(&self, i: I) -> Result<&T, Error> {
Index::from(i.into())
.map_or(None, move |x| self.data.get_ref(x))
.ok_or(Error::TypeMismatch)
}
#[inline]
pub fn get_mut<T: Any, I: Into<$type>>(&mut self, i: I) -> Result<&mut T, Error> {
Index::from(i.into())
.map_or(None, move |x| self.data.get_mut(x))
.ok_or(Error::TypeMismatch)
}
}
pub trait $topo_attrib {
fn impl_attrib_size(&self) -> usize;
fn impl_attrib_dict(&self) -> &AttribDict<$type>;
fn impl_attrib_dict_mut(&mut self) -> &mut AttribDict<$type>;
}
impl<M: $topo_attrib> AttribIndex<M> for $type {
#[inline]
fn attrib_size(mesh: &M) -> usize {
mesh.impl_attrib_size()
}
#[inline]
fn attrib_dict(mesh: &M) -> &AttribDict<Self> {
mesh.impl_attrib_dict()
}
#[inline]
fn attrib_dict_mut(mesh: &mut M) -> &mut AttribDict<Self> {
mesh.impl_attrib_dict_mut()
}
}
};
}
impl_attrib_index!(MeshAttrib, MeshIndex, num_meshes);
impl_attrib_index!(VertexAttrib, VertexIndex, num_verts);
impl_attrib_index!(EdgeAttrib, EdgeIndex, num_edges);
impl_attrib_index!(FaceAttrib, FaceIndex, num_faces);
impl_attrib_index!(CellAttrib, CellIndex, num_cells);
impl_attrib_index!(EdgeVertexAttrib, EdgeVertexIndex, num_edge_verts);
impl_attrib_index!(FaceVertexAttrib, FaceVertexIndex, num_face_verts);
impl_attrib_index!(FaceEdgeAttrib, FaceEdgeIndex, num_face_edges);
impl_attrib_index!(CellVertexAttrib, CellVertexIndex, num_cell_verts);
impl_attrib_index!(CellEdgeAttrib, CellEdgeIndex, num_cell_edges);
impl_attrib_index!(CellFaceAttrib, CellFaceIndex, num_cell_faces);
impl_attrib_index!(VertexEdgeAttrib, VertexEdgeIndex, num_vert_edges);
impl_attrib_index!(VertexFaceAttrib, VertexFaceIndex, num_vert_faces);
impl_attrib_index!(VertexCellAttrib, VertexCellIndex, num_vert_cell);
impl_attrib_index!(EdgeFaceAttrib, EdgeFaceIndex, num_edge_faces);
impl_attrib_index!(EdgeCellAttrib, EdgeCellIndex, num_edge_cells);
impl_attrib_index!(FaceCellAttrib, FaceCellIndex, num_face_cells);
pub trait Attrib
where
Self: ::std::marker::Sized,
{
fn attrib_size<I: AttribIndex<Self>>(&self) -> usize;
fn attrib_dict<I: AttribIndex<Self>>(&self) -> &AttribDict<I>;
fn attrib_dict_mut<I: AttribIndex<Self>>(&mut self) -> &mut AttribDict<I>;
fn add_attrib<'a, T, I: AttribIndex<Self>>(
&mut self,
name: &'a str,
def: T,
) -> Result<&mut Attribute<I>, Error>
where
T: Any + Clone,
{
let n = self.attrib_size::<I>();
match self.attrib_dict_mut().entry(name.to_owned()) {
Entry::Occupied(_) => Err(Error::AlreadyExists(name.to_owned())),
Entry::Vacant(entry) => Ok(entry.insert(Attribute::with_size(n, def))),
}
}
fn add_attrib_data<'a, T, I: AttribIndex<Self>>(
&mut self,
name: &'a str,
data: Vec<T>,
) -> Result<&mut Attribute<I>, Error>
where
T: Any + Clone + Default,
{
let expected_size = self.attrib_size::<I>();
let given_size = data.len();
if given_size != expected_size {
Err(Error::WrongSize {
expected_size,
given_size,
})
} else {
match self.attrib_dict_mut().entry(name.to_owned()) {
Entry::Occupied(_) => Err(Error::AlreadyExists(name.to_owned())),
Entry::Vacant(entry) => Ok(entry.insert(Attribute::from_vec(data))),
}
}
}
fn set_attrib<'a, T, I: AttribIndex<Self>>(
&mut self,
name: &'a str,
def: T,
) -> Result<&mut Attribute<I>, Error>
where
T: Any + Clone,
{
let n = self.attrib_size::<I>();
Ok(match self.attrib_dict_mut().entry(name.to_owned()) {
Entry::Occupied(mut entry) => {
entry.insert(Attribute::with_size(n, def));
entry.into_mut()
}
Entry::Vacant(entry) => entry.insert(Attribute::with_size(n, def)),
})
}
fn set_attrib_data<'a, T, I: AttribIndex<Self>>(
&mut self,
name: &'a str,
data: &[T],
) -> Result<&mut Attribute<I>, Error>
where
T: Any + Clone + Default,
{
let expected_size = self.attrib_size::<I>();
let given_size = data.len();
if given_size != expected_size {
Err(Error::WrongSize {
expected_size,
given_size,
})
} else {
Ok(match self.attrib_dict_mut().entry(name.to_owned()) {
Entry::Occupied(mut entry) => {
entry.insert(Attribute::from_slice(data));
entry.into_mut()
}
Entry::Vacant(entry) => entry.insert(Attribute::from_slice(data)),
})
}
}
fn duplicate_attrib<'a, 'b, T, I: AttribIndex<Self>>(
&mut self,
name: &'a str,
new_name: &'b str,
) -> Result<&mut Attribute<I>, Error>
where
T: Any + Clone,
{
let dup_attrib = self.attrib(name)?.clone();
match self.attrib_check::<T, I>(new_name) {
Ok(_) => Err(Error::AlreadyExists(new_name.to_owned())),
Err(Error::DoesNotExist(_)) => Ok(self
.attrib_dict_mut()
.entry(new_name.to_owned())
.or_insert(dup_attrib)),
Err(err) => Err(err),
}
}
fn remove_attrib<'a, I: AttribIndex<Self>>(
&mut self,
name: &'a str,
) -> Result<Attribute<I>, Error> {
match self.attrib_dict_mut().remove(name) {
Some(attrib) => Ok(attrib),
None => Err(Error::DoesNotExist(name.to_owned())),
}
}
fn insert_attrib<'a, I: AttribIndex<Self>>(
&mut self,
name: &'a str,
attrib: Attribute<I>,
) -> Result<Option<Attribute<I>>, Error> {
let expected_size = self.attrib_size::<I>();
let given_size = attrib.len();
if given_size != expected_size {
Err(Error::WrongSize {
expected_size,
given_size,
})
} else {
Ok(self.attrib_dict_mut().insert(name.to_owned(), attrib))
}
}
fn attrib_or_add<'a, T, I: AttribIndex<Self>>(
&mut self,
name: &'a str,
def: T,
) -> Result<&mut Attribute<I>, Error>
where
T: Any + Clone,
{
let n = self.attrib_size::<I>();
match self.attrib_dict_mut().entry(name.to_owned()) {
Entry::Occupied(entry) => entry.into_mut().check_mut::<T>(),
Entry::Vacant(entry) => Ok(entry.insert(Attribute::with_size(n, def))),
}
}
fn attrib_or_add_data<'a, T, I: AttribIndex<Self>>(
&mut self,
name: &'a str,
data: &[T],
) -> Result<&mut Attribute<I>, Error>
where
T: Any + Clone + Default,
{
let expected_size = self.attrib_size::<I>();
let given_size = data.len();
if given_size != expected_size {
Err(Error::WrongSize {
expected_size,
given_size,
})
} else {
match self.attrib_dict_mut().entry(name.to_owned()) {
Entry::Occupied(entry) => entry.into_mut().check_mut::<T>(),
Entry::Vacant(entry) => Ok(entry.insert(Attribute::from_slice(data))),
}
}
}
fn attrib_iter<'a, 'b, T, I: 'b + AttribIndex<Self>>(
&'b self,
name: &'a str,
) -> Result<slice::Iter<T>, Error>
where
T: 'static + Clone,
{
self.attrib::<I>(name)?.iter::<T>()
}
fn attrib_iter_mut<'a, 'b, T, I: 'b + AttribIndex<Self>>(
&'b mut self,
name: &'a str,
) -> Result<slice::IterMut<T>, Error>
where
T: 'static + Clone,
{
self.attrib_mut::<I>(name)?.iter_mut::<T>()
}
fn attrib_exists<'a, I: AttribIndex<Self>>(&self, name: &'a str) -> bool {
self.attrib_dict::<I>().contains_key(&name.to_owned())
}
fn attrib_check<'a, T: Any, I: AttribIndex<Self>>(&self, name: &'a str) -> Result<(), Error> {
self.attrib::<I>(name)?.check::<T>()?;
Ok(())
}
fn attrib_as_slice<'a, 'b, T: 'static, I: 'b + AttribIndex<Self>>(
&'b self,
name: &'a str,
) -> Result<&'b [T], Error> {
self.attrib::<I>(name)?.as_slice()
}
fn attrib_as_mut_slice<'a, 'b, T: 'static, I: 'b + AttribIndex<Self>>(
&'b mut self,
name: &'a str,
) -> Result<&'b mut [T], Error> {
self.attrib_mut::<I>(name)?.as_mut_slice()
}
fn attrib_clone_into_vec<'a, 'b, T, I: 'b + AttribIndex<Self>>(
&'b self,
name: &'a str,
) -> Result<Vec<T>, Error>
where
T: 'static + Clone,
{
self.attrib::<I>(name)?.clone_into_vec()
}
fn attrib<'a, I: AttribIndex<Self>>(&self, name: &'a str) -> Result<&Attribute<I>, Error> {
match self.attrib_dict().get(name) {
Some(attrib) => Ok(attrib),
None => Err(Error::DoesNotExist(name.to_owned())),
}
}
fn attrib_mut<'a, I: AttribIndex<Self>>(
&mut self,
name: &'a str,
) -> Result<&mut Attribute<I>, Error> {
match self.attrib_dict_mut().get_mut(name) {
Some(attrib) => Ok(attrib),
None => Err(Error::DoesNotExist(name.to_owned())),
}
}
}
#[derive(Debug, PartialEq)]
pub enum Error {
AlreadyExists(String),
TypeMismatch,
DoesNotExist(String),
WrongSize {
expected_size: usize,
given_size: usize,
},
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Error::AlreadyExists(attrib_name) => {
write!(f, "An attribute named \"{}\" already exists", attrib_name)
}
Error::TypeMismatch => write!(f, "Type mismatch"),
Error::DoesNotExist(attrib_name) => {
write!(f, "The attribute \"{}\" does not exist", attrib_name)
}
Error::WrongSize {
expected_size,
given_size,
} => write!(
f,
"Given attribute size: {}, does not match expected size: {}",
given_size, expected_size
),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use num_traits::Zero;
use crate::mesh::TriMesh;
use crate::mesh::VertexPositions;
#[test]
fn basic_test() {
use math::Vector3;
let pts = vec![
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[1.0, 1.0, 0.0],
];
let indices = vec![0, 1, 2, 1, 3, 2];
let mut trimesh = TriMesh::new(pts, indices);
{
let nml_attrib = trimesh
.add_attrib::<_, VertexIndex>("N", Vector3::<f64>::zero())
.unwrap();
assert_eq!(
nml_attrib.get::<Vector3<f64>, _>(VertexIndex::from(1)),
Ok(Vector3::zero())
);
}
assert!(trimesh
.attrib_check::<Vector3<f64>, VertexIndex>("N")
.is_ok());
let mut nml_attrib = trimesh.remove_attrib::<VertexIndex>("N").unwrap();
assert!(!trimesh.attrib_exists::<VertexIndex>("N"));
*nml_attrib.get_mut(2).unwrap() = Vector3::from([1.0, 2.0, 3.0]);
assert!(trimesh.insert_attrib("N", nml_attrib).unwrap().is_none());
assert!(trimesh.attrib_exists::<VertexIndex>("N"));
assert_eq!(
trimesh
.attrib::<VertexIndex>("N")
.unwrap()
.get::<Vector3<f64>, _>(2)
.unwrap(),
Vector3::from([1.0, 2.0, 3.0])
);
}
#[test]
fn set_attrib_test() {
use math::Vector3;
use num_traits::Zero;
let pts = vec![
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[1.0, 1.0, 0.0],
];
let indices = vec![0, 1, 2, 1, 3, 2];
let mut trimesh = TriMesh::new(pts, indices);
for nml in trimesh
.set_attrib::<_, VertexIndex>("N", Vector3::<f64>::zero())
.unwrap()
.iter::<Vector3<f64>>()
.unwrap()
{
assert_eq!(*nml, Vector3::zero());
}
for nml in trimesh
.set_attrib::<_, VertexIndex>("N", Vector3::from([1.0f64; 3]))
.unwrap()
.iter::<Vector3<f64>>()
.unwrap()
{
assert_eq!(*nml, Vector3::from([1.0; 3]));
}
let verts = trimesh.vertex_positions().to_vec();
trimesh
.set_attrib_data::<_, VertexIndex>("N", verts.as_slice())
.unwrap();
for (nml, p) in trimesh
.attrib_iter::<[f64; 3], VertexIndex>("N")
.unwrap()
.zip(trimesh.vertex_positions())
{
assert_eq!(*nml, *p);
}
let verts = trimesh.vertex_positions().to_vec();
trimesh
.set_attrib_data::<_, VertexIndex>("ref", verts.as_slice())
.unwrap();
for (r, p) in trimesh
.attrib_iter::<[f64; 3], VertexIndex>("ref")
.unwrap()
.zip(trimesh.vertex_positions())
{
assert_eq!(*r, *p);
}
}
#[test]
fn attrib_or_add_test() -> Result<(), Error> {
let pts = vec![
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[1.0, 1.0, 0.0],
];
let indices = vec![0, 1, 2, 1, 3, 2];
let mut trimesh = TriMesh::new(pts, indices);
for nml in trimesh
.attrib_or_add::<_, VertexIndex>("N", [0.0f64; 3])?
.iter::<[f64; 3]>()?
{
assert_eq!(*nml, [0.0; 3]);
}
for nml in trimesh
.attrib_or_add::<_, VertexIndex>("N", [1.0f64; 3])?
.iter::<[f64; 3]>()?
{
assert_eq!(*nml, [0.0; 3]);
}
let verts: Vec<_> = trimesh.vertex_positions().to_vec();
for nml in trimesh
.attrib_or_add_data::<_, VertexIndex>("N", verts.as_slice())?
.iter::<[f64; 3]>()?
{
assert_eq!(*nml, [0.0; 3]);
}
let verts = trimesh.vertex_positions().to_vec();
trimesh.attrib_or_add_data::<_, VertexIndex>("ref", verts.as_slice())?;
for (r, p) in trimesh
.attrib_iter::<[f64; 3], VertexIndex>("ref")?
.zip(trimesh.vertex_positions())
{
assert_eq!(*r, *p);
}
Ok(())
}
#[test]
fn multidim_test() {
let pts = vec![
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[1.0, 1.0, 0.0],
];
let indices = vec![0, 1, 2, 1, 3, 2];
let mut trimesh = TriMesh::new(pts, indices);
let data = vec![[0i8, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]];
{
let attrib = trimesh
.add_attrib_data::<_, VertexIndex>("attrib1", data.clone())
.unwrap();
for i in 0..data.len() {
assert_eq!(attrib.get::<[i8; 3], _>(VertexIndex::from(i)), Ok(data[i]));
}
}
assert_eq!(
trimesh
.attrib_clone_into_vec::<[i8; 3], VertexIndex>("attrib1")
.unwrap(),
data
);
assert_eq!(
trimesh
.attrib_as_slice::<[i8; 3], VertexIndex>("attrib1")
.unwrap(),
data.as_slice()
);
assert!(trimesh
.attrib_check::<[i8; 3], VertexIndex>("attrib1")
.is_ok());
assert!(trimesh.remove_attrib::<VertexIndex>("attrib1").is_ok());
assert!(!trimesh.attrib_exists::<VertexIndex>("attrib1"));
}
#[test]
fn misc_test() {
let pts = vec![
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[1.0, 1.0, 0.0],
];
let indices = vec![0, 1, 2, 1, 3, 2];
let mut trimesh = TriMesh::new(pts, indices);
let data = vec![[0i8, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]];
{
trimesh
.add_attrib_data::<_, VertexIndex>("attrib1", data.clone())
.unwrap();
}
{
trimesh
.duplicate_attrib::<[i8; 3], VertexIndex>("attrib1", "attrib2")
.ok()
.unwrap();
}
trimesh
.attrib_check::<[i8; 3], VertexIndex>("attrib2")
.ok()
.unwrap();
for (i, val) in trimesh
.attrib_iter::<[i8; 3], VertexIndex>("attrib2")
.unwrap()
.enumerate()
{
assert_eq!(*val, data[i]);
}
assert!(trimesh.remove_attrib::<VertexIndex>("attrib1").is_ok());
assert!(trimesh.remove_attrib::<VertexIndex>("attrib2").is_ok());
assert!(!trimesh.attrib_exists::<VertexIndex>("attrib1"));
assert!(!trimesh.attrib_exists::<VertexIndex>("attrib2"));
}
}