#![warn(missing_docs)]
use std::any::{Any, TypeId};
use hashbrown::HashMap;
use std::slice;
use dync::{traits::HasDrop, VecDyn};
use crate::mesh::topology::*;
mod attribute;
mod bytes;
mod index;
pub use hashbrown::hash_map::Entry;
pub use attribute::*;
pub use bytes::*;
pub use index::*;
pub type AttribDict<I> = HashMap<String, Attribute<I>>;
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 attrib_dict_and_cache_mut<I: AttribIndex<Self>>(
&mut self,
) -> (&mut AttribDict<I>, Option<&mut AttribValueCache>);
fn add_attrib<'a, T, I: AttribIndex<Self>>(
&mut self,
name: &'a str,
def: T,
) -> Result<&mut Attribute<I>, Error>
where
T: AttributeValue,
{
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::direct_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: AttributeValue + 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::direct_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: AttributeValue,
{
let n = self.attrib_size::<I>();
Ok(match self.attrib_dict_mut().entry(name.to_owned()) {
Entry::Occupied(mut entry) => {
entry.insert(Attribute::direct_with_size(n, def));
entry.into_mut()
}
Entry::Vacant(entry) => entry.insert(Attribute::direct_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: AttributeValue + 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::direct_from_slice(data));
entry.into_mut()
}
Entry::Vacant(entry) => entry.insert(Attribute::direct_from_slice(data)),
})
}
}
fn add_indirect_attrib<'a, T, I: AttribIndex<Self>>(
&mut self,
name: &'a str,
def: T,
) -> Result<(&mut Attribute<I>, &mut AttribValueCache), Error>
where
T: AttributeValueHash,
{
let n = self.attrib_size::<I>();
if let (dict, Some(cache)) = self.attrib_dict_and_cache_mut() {
match dict.entry(name.to_owned()) {
Entry::Occupied(_) => Err(Error::AlreadyExists(name.to_owned())),
Entry::Vacant(entry) => {
Ok((entry.insert(Attribute::indirect_with_size(n, def)), cache))
}
}
} else {
Err(Error::MissingAttributeValueCache)
}
}
fn set_indirect_attrib<'a, T, I: AttribIndex<Self>>(
&mut self,
name: &'a str,
def: T,
) -> Result<(&mut Attribute<I>, &mut AttribValueCache), Error>
where
T: AttributeValueHash,
{
let n = self.attrib_size::<I>();
if let (dict, Some(cache)) = self.attrib_dict_and_cache_mut() {
Ok((
match dict.entry(name.to_owned()) {
Entry::Occupied(mut entry) => {
entry.insert(Attribute::indirect_with_size(n, def));
entry.into_mut()
}
Entry::Vacant(entry) => entry.insert(Attribute::indirect_with_size(n, def)),
},
cache,
))
} else {
Err(Error::MissingAttributeValueCache)
}
}
fn add_indirect_attrib_data<'a, I: AttribIndex<Self>>(
&mut self,
name: &'a str,
data: IndirectData,
) -> Result<(&mut Attribute<I>, &mut AttribValueCache), Error> {
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 if let (dict, Some(cache)) = self.attrib_dict_and_cache_mut() {
match dict.entry(name.to_owned()) {
Entry::Occupied(_) => Err(Error::AlreadyExists(name.to_owned())),
Entry::Vacant(entry) => {
Ok((entry.insert(Attribute::indirect_from_data(data)), cache))
}
}
} else {
Err(Error::MissingAttributeValueCache)
}
}
fn set_indirect_attrib_data<'a, I: AttribIndex<Self>>(
&mut self,
name: &'a str,
data: IndirectData,
) -> Result<(&mut Attribute<I>, &mut AttribValueCache), Error> {
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 if let (dict, Some(cache)) = self.attrib_dict_and_cache_mut() {
Ok((
match dict.entry(name.to_owned()) {
Entry::Occupied(mut entry) => {
entry.insert(Attribute::indirect_from_data(data));
entry.into_mut()
}
Entry::Vacant(entry) => entry.insert(Attribute::indirect_from_data(data)),
},
cache,
))
} else {
Err(Error::MissingAttributeValueCache)
}
}
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: AttributeValue,
{
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::direct_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: AttributeValue + 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::direct_from_slice(data))),
}
}
}
fn attrib_or_add_indirect<'a, T, I: AttribIndex<Self>>(
&mut self,
name: &'a str,
def: T,
) -> Result<(&mut Attribute<I>, &mut AttribValueCache), Error>
where
T: AttributeValueHash,
{
let n = self.attrib_size::<I>();
if let (dict, Some(cache)) = self.attrib_dict_and_cache_mut() {
match dict.entry(name.to_owned()) {
Entry::Occupied(entry) => entry.into_mut().check_mut::<T>().map(|a| (a, cache)),
Entry::Vacant(entry) => {
Ok((entry.insert(Attribute::indirect_with_size(n, def)), cache))
}
}
} else {
Err(Error::MissingAttributeValueCache)
}
}
fn direct_attrib_iter<'a, 'b, T, I: 'b + AttribIndex<Self>>(
&'b self,
name: &'a str,
) -> Result<slice::Iter<T>, Error>
where
T: Any + Clone,
{
self.attrib::<I>(name)?.direct_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: Any + Clone,
{
self.attrib_mut::<I>(name)?.direct_iter_mut::<T>()
}
fn attrib_iter<'b, T, I: 'b + AttribIndex<Self>>(
&'b self,
name: &str,
) -> Result<Box<dyn Iterator<Item = &'b T> + 'b>, Error>
where
T: Any + Clone,
{
self.attrib::<I>(name)?.iter::<T>()
}
fn indirect_attrib_update_with<'a, 'b, T, I, F>(
&'b mut self,
name: &'a str,
f: F,
) -> Result<(&'b mut Attribute<I>, &'b mut AttribValueCache), Error>
where
T: AttributeValueHash,
I: 'b + AttribIndex<Self>,
F: FnMut(usize, &Irc<T>) -> Option<Irc<T>>,
{
let (dict, cache) = self.attrib_dict_and_cache_mut();
if let Some(cache) = cache {
match dict.get_mut(name) {
Some(attrib) => attrib
.indirect_update_with::<T, _>(f, cache)
.map(|a| (a, cache)),
None => Err(Error::DoesNotExist(name.to_owned())),
}
} else {
Err(Error::MissingAttributeValueCache)
}
}
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<&Attribute<I>, Error> {
self.attrib::<I>(name)?.check::<T>()
}
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: AttributeValueHash,
{
self.attrib::<I>(name)?.clone_into_vec()
}
fn direct_attrib_clone_into_vec<'a, 'b, T, I: 'b + AttribIndex<Self>>(
&'b self,
name: &'a str,
) -> Result<Vec<T>, Error>
where
T: AttributeValue,
{
self.attrib::<I>(name)?.direct_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())),
}
}
}
pub trait AttribPromote<SI, TI>
where
Self: Sized,
SI: AttribIndex<Self>,
TI: AttribIndex<Self>,
{
fn attrib_promote<'a, U, F>(
&mut self,
name: &'a str,
combine: F,
) -> Result<&Attribute<TI>, Error>
where
U: Clone + 'static,
F: for<'b> FnMut(&'b mut U, &'b U);
}
impl<T: crate::Real> AttribPromote<FaceVertexIndex, VertexIndex> for crate::mesh::TriMesh<T> {
fn attrib_promote<'a, U, F>(
&mut self,
name: &'a str,
mut combine: F,
) -> Result<&Attribute<VertexIndex>, Error>
where
U: Any + Clone,
F: for<'b> FnMut(&'b mut U, &'b U),
{
self.attrib_check::<U, FaceVertexIndex>(name)?;
let attrib = self.remove_attrib::<FaceVertexIndex>(name)?;
let num_verts = self.num_vertices();
let crate::mesh::TriMesh {
vertex_attributes,
indices,
..
} = self;
match vertex_attributes.entry(name.to_owned()) {
Entry::Occupied(mut entry) => {
let other = entry.get_mut().as_mut_slice::<U>()?;
for (fv_idx, fv_val) in attrib.direct_iter::<U>()?.enumerate() {
let vtx_idx = indices[fv_idx / 3][fv_idx % 3];
combine(&mut other[vtx_idx], &fv_val);
}
Ok(entry.into_mut())
}
Entry::Vacant(entry) => {
let new_attrib: Attribute<VertexIndex> =
attrib.promote_with_len(num_verts, move |mut new, orig| {
let new = new.as_slice::<U>().unwrap();
let mut seen = vec![false; new.len()];
for (fv_idx, fv_val) in orig.iter_as::<U>().unwrap().enumerate() {
let vtx_idx = indices[fv_idx / 3][fv_idx % 3];
if !seen[vtx_idx] {
new[vtx_idx] = fv_val.clone();
seen[vtx_idx] = true;
} else {
combine(&mut new[vtx_idx], fv_val);
}
}
});
Ok(entry.insert(new_attrib))
}
}
}
}
#[derive(Debug, PartialEq)]
pub enum Error {
AlreadyExists(String),
TypeMismatch {
expected: TypeId,
actual: TypeId,
},
DoesNotExist(String),
WrongSize {
expected_size: usize,
given_size: usize,
},
KindMismatchFoundDirect,
KindMismatchFoundIndirect,
MissingAttributeValueCache,
}
impl Error {
fn type_mismatch_from_buf<T: Any, V: HasDrop>(data: &VecDyn<V>) -> Self {
Self::type_mismatch_id::<T>(data.element_type_id())
}
fn type_mismatch_id<T: Any>(actual: TypeId) -> Self {
Error::TypeMismatch {
expected: TypeId::of::<T>(),
actual,
}
}
}
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 { expected, actual } => write!(
f,
"Type mismatch.\nExpected: {:?},\nActual: {:?}",
expected, actual
),
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
),
Error::KindMismatchFoundDirect => write!(
f,
"Expected an indirect attribute, found a direct attribute",
),
Error::KindMismatchFoundIndirect => write!(
f,
"Expected a direct attribute, found an indirect attribute",
),
Error::MissingAttributeValueCache => write!(
f,
"Missing an attribute value cache needed for indirect attributes"
),
}
}
}
impl From<dync::Error> for Error {
fn from(dync_err: dync::Error) -> Self {
match dync_err {
dync::Error::ValueTooLarge => {
unreachable!()
}
dync::Error::MismatchedTypes { expected, actual } => {
Error::TypeMismatch { expected, actual }
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mesh::TriMesh;
use crate::mesh::VertexPositions;
use num_traits::Zero;
#[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")
.unwrap();
}
trimesh
.attrib_check::<[i8; 3], VertexIndex>("attrib2")
.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"));
}
#[test]
fn attrib_promote() {
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::<_, FaceVertexIndex>("attrib1", data.clone())
.unwrap();
}
{
let first_attempt = trimesh.attrib_promote::<[f32; 2], _>("attrib1", |_, _| {
panic!("Wrong attribute promote executed");
});
assert!(first_attempt.is_err());
trimesh
.attrib_promote::<[i8; 2], _>("attrib1", |a, b| {
a[0] += b[0];
a[1] += b[1];
})
.unwrap();
}
trimesh
.attrib_check::<[i8; 2], VertexIndex>("attrib1")
.unwrap();
assert_eq!(
trimesh
.attrib_as_slice::<[i8; 2], VertexIndex>("attrib1")
.unwrap(),
&[[0i8, 1], [8, 10], [14, 16], [8, 9]][..]
);
}
}