use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::marker::PhantomData;
use slotmap::Key;
use crate::ids::{FaceId, HalfEdgeId, VertexId};
use crate::storage::{Face, HalfEdge, MeshStorage, Vertex};
#[inline]
fn idx_of<K: Key>(id: K) -> usize {
id.data().as_ffi() as u32 as usize
}
pub struct PropertyStore<T> {
data: Vec<Option<T>>,
}
impl<T> PropertyStore<T> {
pub fn new() -> Self {
Self { data: Vec::new() }
}
#[inline]
fn ensure_capacity(&mut self, idx: usize) {
if idx >= self.data.len() {
self.data.resize_with(idx + 1, || None);
}
}
pub fn get(&self, idx: usize) -> Option<&T> {
self.data.get(idx).and_then(|opt| opt.as_ref())
}
pub fn get_mut(&mut self, idx: usize) -> Option<&mut T> {
self.data.get_mut(idx).and_then(|opt| opt.as_mut())
}
pub fn set(&mut self, idx: usize, val: T) {
self.ensure_capacity(idx);
self.data[idx] = Some(val);
}
pub fn remove(&mut self, idx: usize) -> Option<T> {
if idx < self.data.len() {
self.data[idx].take()
} else {
None
}
}
pub fn contains(&self, idx: usize) -> bool {
self.get(idx).is_some()
}
pub fn len(&self) -> usize {
self.data.iter().filter(|opt| opt.is_some()).count()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn clear(&mut self) {
self.data.clear();
}
pub fn iter(&self) -> impl Iterator<Item = (usize, &T)> {
self.data
.iter()
.enumerate()
.filter_map(|(i, opt)| opt.as_ref().map(|v| (i, v)))
}
}
impl<T> Default for PropertyStore<T> {
fn default() -> Self {
Self::new()
}
}
trait ErasedProperty {
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn remove_by_index(&mut self, idx: usize);
fn clear_all(&mut self);
}
impl<T: 'static> ErasedProperty for PropertyStore<T> {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn remove_by_index(&mut self, idx: usize) {
if idx < self.data.len() {
self.data[idx] = None;
}
}
fn clear_all(&mut self) {
self.data.clear();
}
}
pub struct PropertyHandle<T> {
_marker: PhantomData<T>,
}
impl<T> PropertyHandle<T> {
pub fn new() -> Self {
Self {
_marker: PhantomData,
}
}
}
impl<T> Default for PropertyHandle<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Clone for PropertyHandle<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for PropertyHandle<T> {}
impl<T> std::fmt::Debug for PropertyHandle<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PropertyHandle")
.field("type", &std::any::type_name::<T>())
.finish()
}
}
pub struct MeshProperties {
vertex_props: HashMap<TypeId, Box<dyn ErasedProperty>>,
halfedge_props: HashMap<TypeId, Box<dyn ErasedProperty>>,
face_props: HashMap<TypeId, Box<dyn ErasedProperty>>,
}
impl MeshProperties {
pub fn new() -> Self {
Self {
vertex_props: HashMap::new(),
halfedge_props: HashMap::new(),
face_props: HashMap::new(),
}
}
pub fn vertex_prop_type_count(&self) -> usize {
self.vertex_props.len()
}
pub fn halfedge_prop_type_count(&self) -> usize {
self.halfedge_props.len()
}
pub fn face_prop_type_count(&self) -> usize {
self.face_props.len()
}
pub fn has_vertex_prop<T: 'static>(&self) -> bool {
self.vertex_props.contains_key(&TypeId::of::<T>())
}
pub fn has_halfedge_prop<T: 'static>(&self) -> bool {
self.halfedge_props.contains_key(&TypeId::of::<T>())
}
pub fn has_face_prop<T: 'static>(&self) -> bool {
self.face_props.contains_key(&TypeId::of::<T>())
}
pub fn add_vertex_prop<T: 'static>(&mut self) -> PropertyHandle<T> {
self.vertex_props
.insert(TypeId::of::<T>(), Box::new(PropertyStore::<T>::new()));
PropertyHandle::new()
}
pub fn get_vertex_prop<T: 'static>(
&self,
_handle: PropertyHandle<T>,
id: VertexId,
) -> Option<&T> {
let entry = self.vertex_props.get(&TypeId::of::<T>())?;
let store = entry.as_any().downcast_ref::<PropertyStore<T>>()?;
store.get(idx_of(id))
}
pub fn get_vertex_prop_mut<T: 'static>(
&mut self,
_handle: PropertyHandle<T>,
id: VertexId,
) -> Option<&mut T> {
let entry = self.vertex_props.get_mut(&TypeId::of::<T>())?;
let store = entry.as_any_mut().downcast_mut::<PropertyStore<T>>()?;
store.get_mut(idx_of(id))
}
pub fn set_vertex_prop<T: 'static>(
&mut self,
_handle: PropertyHandle<T>,
id: VertexId,
val: T,
) {
let type_id = TypeId::of::<T>();
let entry = self
.vertex_props
.get_mut(&type_id)
.and_then(|e| e.as_any_mut().downcast_mut::<PropertyStore<T>>());
match entry {
Some(store) => store.set(idx_of(id), val),
None => {
let mut store = PropertyStore::<T>::new();
store.set(idx_of(id), val);
self.vertex_props.insert(type_id, Box::new(store));
}
}
}
pub fn remove_vertex_prop<T: 'static>(
&mut self,
_handle: PropertyHandle<T>,
id: VertexId,
) -> Option<T> {
let entry = self.vertex_props.get_mut(&TypeId::of::<T>())?;
let store = entry.as_any_mut().downcast_mut::<PropertyStore<T>>()?;
store.remove(idx_of(id))
}
pub fn remove_vertex_all_props(&mut self, id: VertexId) {
let idx = idx_of(id);
for store in self.vertex_props.values_mut() {
store.remove_by_index(idx);
}
}
pub fn add_halfedge_prop<T: 'static>(&mut self) -> PropertyHandle<T> {
self.halfedge_props
.insert(TypeId::of::<T>(), Box::new(PropertyStore::<T>::new()));
PropertyHandle::new()
}
pub fn get_halfedge_prop<T: 'static>(
&self,
_handle: PropertyHandle<T>,
id: HalfEdgeId,
) -> Option<&T> {
let entry = self.halfedge_props.get(&TypeId::of::<T>())?;
let store = entry.as_any().downcast_ref::<PropertyStore<T>>()?;
store.get(idx_of(id))
}
pub fn get_halfedge_prop_mut<T: 'static>(
&mut self,
_handle: PropertyHandle<T>,
id: HalfEdgeId,
) -> Option<&mut T> {
let entry = self.halfedge_props.get_mut(&TypeId::of::<T>())?;
let store = entry.as_any_mut().downcast_mut::<PropertyStore<T>>()?;
store.get_mut(idx_of(id))
}
pub fn set_halfedge_prop<T: 'static>(
&mut self,
_handle: PropertyHandle<T>,
id: HalfEdgeId,
val: T,
) {
let type_id = TypeId::of::<T>();
let entry = self
.halfedge_props
.get_mut(&type_id)
.and_then(|e| e.as_any_mut().downcast_mut::<PropertyStore<T>>());
match entry {
Some(store) => store.set(idx_of(id), val),
None => {
let mut store = PropertyStore::<T>::new();
store.set(idx_of(id), val);
self.halfedge_props.insert(type_id, Box::new(store));
}
}
}
pub fn remove_halfedge_prop<T: 'static>(
&mut self,
_handle: PropertyHandle<T>,
id: HalfEdgeId,
) -> Option<T> {
let entry = self.halfedge_props.get_mut(&TypeId::of::<T>())?;
let store = entry.as_any_mut().downcast_mut::<PropertyStore<T>>()?;
store.remove(idx_of(id))
}
pub fn remove_halfedge_all_props(&mut self, id: HalfEdgeId) {
let idx = idx_of(id);
for store in self.halfedge_props.values_mut() {
store.remove_by_index(idx);
}
}
pub fn add_face_prop<T: 'static>(&mut self) -> PropertyHandle<T> {
self.face_props
.insert(TypeId::of::<T>(), Box::new(PropertyStore::<T>::new()));
PropertyHandle::new()
}
pub fn get_face_prop<T: 'static>(&self, _handle: PropertyHandle<T>, id: FaceId) -> Option<&T> {
let entry = self.face_props.get(&TypeId::of::<T>())?;
let store = entry.as_any().downcast_ref::<PropertyStore<T>>()?;
store.get(idx_of(id))
}
pub fn get_face_prop_mut<T: 'static>(
&mut self,
_handle: PropertyHandle<T>,
id: FaceId,
) -> Option<&mut T> {
let entry = self.face_props.get_mut(&TypeId::of::<T>())?;
let store = entry.as_any_mut().downcast_mut::<PropertyStore<T>>()?;
store.get_mut(idx_of(id))
}
pub fn set_face_prop<T: 'static>(&mut self, _handle: PropertyHandle<T>, id: FaceId, val: T) {
let type_id = TypeId::of::<T>();
let entry = self
.face_props
.get_mut(&type_id)
.and_then(|e| e.as_any_mut().downcast_mut::<PropertyStore<T>>());
match entry {
Some(store) => store.set(idx_of(id), val),
None => {
let mut store = PropertyStore::<T>::new();
store.set(idx_of(id), val);
self.face_props.insert(type_id, Box::new(store));
}
}
}
pub fn remove_face_prop<T: 'static>(
&mut self,
_handle: PropertyHandle<T>,
id: FaceId,
) -> Option<T> {
let entry = self.face_props.get_mut(&TypeId::of::<T>())?;
let store = entry.as_any_mut().downcast_mut::<PropertyStore<T>>()?;
store.remove(idx_of(id))
}
pub fn remove_face_all_props(&mut self, id: FaceId) {
let idx = idx_of(id);
for store in self.face_props.values_mut() {
store.remove_by_index(idx);
}
}
pub fn clear_vertex_props(&mut self) {
for store in self.vertex_props.values_mut() {
store.clear_all();
}
}
pub fn clear_halfedge_props(&mut self) {
for store in self.halfedge_props.values_mut() {
store.clear_all();
}
}
pub fn clear_face_props(&mut self) {
for store in self.face_props.values_mut() {
store.clear_all();
}
}
}
impl Default for MeshProperties {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for MeshProperties {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MeshProperties")
.field("vertex_prop_types", &self.vertex_props.len())
.field("halfedge_prop_types", &self.halfedge_props.len())
.field("face_prop_types", &self.face_props.len())
.finish()
}
}
pub fn remove_vertex_with_props(
mesh: &mut MeshStorage,
props: &mut MeshProperties,
id: VertexId,
) -> Option<Vertex> {
let result = mesh.remove_vertex(id);
props.remove_vertex_all_props(id);
result
}
pub fn remove_halfedge_with_props(
mesh: &mut MeshStorage,
props: &mut MeshProperties,
id: HalfEdgeId,
) -> Option<HalfEdge> {
let result = mesh.remove_halfedge(id);
props.remove_halfedge_all_props(id);
result
}
pub fn remove_face_with_props(
mesh: &mut MeshStorage,
props: &mut MeshProperties,
id: FaceId,
) -> Option<Face> {
let result = mesh.remove_face(id);
props.remove_face_all_props(id);
result
}
#[cfg(test)]
mod tests {
use super::*;
use crate::io::build_mesh_from_vertices_and_faces;
#[test]
fn property_store_basic_crud() {
let mut store = PropertyStore::<f64>::new();
assert!(store.is_empty());
assert_eq!(store.len(), 0);
store.set(0, 1.5);
store.set(1, 2.5);
store.set(2, 3.5);
assert_eq!(store.len(), 3);
assert!(!store.is_empty());
assert!(store.contains(1));
assert!(!store.contains(99));
assert_eq!(store.get(0), Some(&1.5));
assert_eq!(store.get(1), Some(&2.5));
assert_eq!(store.get(99), None);
*store.get_mut(1).unwrap() = 9.9;
assert_eq!(store.get(1), Some(&9.9));
assert_eq!(store.remove(1), Some(9.9));
assert_eq!(store.remove(1), None);
assert_eq!(store.len(), 2);
}
#[test]
fn property_store_iter() {
let mut store = PropertyStore::<String>::new();
store.set(0, "a".to_string());
store.set(5, "b".to_string());
store.set(10, "c".to_string());
let mut entries: Vec<(usize, String)> = store.iter().map(|(i, v)| (i, v.clone())).collect();
entries.sort_by_key(|(i, _)| *i);
assert_eq!(
entries,
vec![
(0, "a".to_string()),
(5, "b".to_string()),
(10, "c".to_string())
]
);
}
#[test]
fn property_store_clear() {
let mut store = PropertyStore::<i32>::new();
store.set(0, 1);
store.set(1, 2);
store.clear();
assert!(store.is_empty());
assert_eq!(store.get(0), None);
}
#[test]
fn add_and_get_vertex_prop() {
let (_, vids) = build_triangle_mesh();
let mut props = MeshProperties::new();
let h = props.add_vertex_prop::<f64>();
assert!(props.has_vertex_prop::<f64>());
assert_eq!(props.vertex_prop_type_count(), 1);
props.set_vertex_prop(h, vids[0], 1.5);
props.set_vertex_prop(h, vids[1], 2.5);
props.set_vertex_prop(h, vids[2], 3.5);
assert_eq!(props.get_vertex_prop(h, vids[0]), Some(&1.5));
assert_eq!(props.get_vertex_prop(h, vids[1]), Some(&2.5));
assert_eq!(props.get_vertex_prop(h, vids[2]), Some(&3.5));
}
#[test]
fn get_vertex_prop_mut_works() {
let (_, vids) = build_triangle_mesh();
let mut props = MeshProperties::new();
let h = props.add_vertex_prop::<f64>();
props.set_vertex_prop(h, vids[0], 1.0);
if let Some(val) = props.get_vertex_prop_mut(h, vids[0]) {
*val = 10.0;
}
assert_eq!(props.get_vertex_prop(h, vids[0]), Some(&10.0));
}
#[test]
fn remove_vertex_prop() {
let (_, vids) = build_triangle_mesh();
let mut props = MeshProperties::new();
let h = props.add_vertex_prop::<f64>();
props.set_vertex_prop(h, vids[0], 1.5);
let removed = props.remove_vertex_prop(h, vids[0]);
assert_eq!(removed, Some(1.5));
assert_eq!(props.get_vertex_prop(h, vids[0]), None);
assert_eq!(props.remove_vertex_prop(h, vids[0]), None);
}
#[test]
fn remove_vertex_all_props_clears_all_types() {
let (_, vids) = build_triangle_mesh();
let mut props = MeshProperties::new();
let h1 = props.add_vertex_prop::<f64>();
let h2 = props.add_vertex_prop::<i32>();
let h3 = props.add_vertex_prop::<String>();
props.set_vertex_prop(h1, vids[0], 1.5);
props.set_vertex_prop(h2, vids[0], 42);
props.set_vertex_prop(h3, vids[0], "hello".to_string());
props.set_vertex_prop(h1, vids[1], 2.5);
props.set_vertex_prop(h2, vids[1], 99);
props.remove_vertex_all_props(vids[0]);
assert_eq!(props.get_vertex_prop(h1, vids[0]), None);
assert_eq!(props.get_vertex_prop(h2, vids[0]), None);
assert_eq!(props.get_vertex_prop(h3, vids[0]), None);
assert_eq!(props.get_vertex_prop(h1, vids[1]), Some(&2.5));
assert_eq!(props.get_vertex_prop(h2, vids[1]), Some(&99));
}
#[test]
fn different_types_do_not_interfere() {
let (_, vids) = build_triangle_mesh();
let mut props = MeshProperties::new();
let h_f64 = props.add_vertex_prop::<f64>();
let h_i32 = props.add_vertex_prop::<i32>();
let h_string = props.add_vertex_prop::<String>();
props.set_vertex_prop(h_f64, vids[0], 1.5);
props.set_vertex_prop(h_i32, vids[0], 42);
props.set_vertex_prop(h_string, vids[0], "abc".to_string());
assert_eq!(props.get_vertex_prop(h_f64, vids[0]), Some(&1.5));
assert_eq!(props.get_vertex_prop(h_i32, vids[0]), Some(&42));
assert_eq!(
props.get_vertex_prop(h_string, vids[0]),
Some(&"abc".to_string())
);
props.remove_vertex_prop(h_f64, vids[0]);
assert_eq!(props.get_vertex_prop(h_f64, vids[0]), None);
assert_eq!(props.get_vertex_prop(h_i32, vids[0]), Some(&42));
assert_eq!(
props.get_vertex_prop(h_string, vids[0]),
Some(&"abc".to_string())
);
}
#[test]
fn vertex_halfedge_face_props_are_independent() {
let (mesh, vids) = build_triangle_mesh();
let fids: Vec<FaceId> = mesh.face_ids().collect();
let mut props = MeshProperties::new();
let hv = props.add_vertex_prop::<f64>();
let hf = props.add_face_prop::<f64>();
props.set_vertex_prop(hv, vids[0], 1.0);
props.set_face_prop(hf, fids[0], 99.0);
assert_eq!(props.get_vertex_prop(hv, vids[0]), Some(&1.0));
assert_eq!(props.get_face_prop(hf, fids[0]), Some(&99.0));
assert_eq!(props.vertex_prop_type_count(), 1);
assert_eq!(props.face_prop_type_count(), 1);
}
#[test]
fn get_unregistered_prop_returns_none() {
let (_, vids) = build_triangle_mesh();
let props = MeshProperties::new();
let h: PropertyHandle<f64> = PropertyHandle::new();
assert_eq!(props.get_vertex_prop(h, vids[0]), None);
}
#[test]
fn set_auto_registers_prop() {
let (_, vids) = build_triangle_mesh();
let mut props = MeshProperties::new();
let h: PropertyHandle<f64> = PropertyHandle::new();
props.set_vertex_prop(h, vids[0], 42.0);
assert!(props.has_vertex_prop::<f64>());
assert_eq!(props.get_vertex_prop(h, vids[0]), Some(&42.0));
}
#[test]
fn halfedge_and_face_props_work() {
let (mesh, _vids) = build_triangle_mesh();
let fids: Vec<FaceId> = mesh.face_ids().collect();
let heids: Vec<HalfEdgeId> = mesh.halfedge_ids().collect();
let mut props = MeshProperties::new();
let hh = props.add_halfedge_prop::<bool>();
let hf = props.add_face_prop::<u32>();
props.set_halfedge_prop(hh, heids[0], true);
props.set_halfedge_prop(hh, heids[1], false);
props.set_face_prop(hf, fids[0], 7);
assert_eq!(props.get_halfedge_prop(hh, heids[0]), Some(&true));
assert_eq!(props.get_halfedge_prop(hh, heids[1]), Some(&false));
assert_eq!(props.get_face_prop(hf, fids[0]), Some(&7));
props.remove_halfedge_all_props(heids[0]);
assert_eq!(props.get_halfedge_prop(hh, heids[0]), None);
assert_eq!(props.get_halfedge_prop(hh, heids[1]), Some(&false));
props.remove_face_all_props(fids[0]);
assert_eq!(props.get_face_prop(hf, fids[0]), None);
}
#[test]
fn remove_vertex_with_props_wrapper() {
let (mut mesh, vids) = build_triangle_mesh();
let mut props = MeshProperties::new();
let h = props.add_vertex_prop::<f64>();
props.set_vertex_prop(h, vids[0], 1.0);
props.set_vertex_prop(h, vids[1], 2.0);
let removed = remove_vertex_with_props(&mut mesh, &mut props, vids[0]);
assert!(removed.is_some());
assert!(!mesh.contains_vertex(vids[0]));
assert_eq!(props.get_vertex_prop(h, vids[0]), None);
assert_eq!(props.get_vertex_prop(h, vids[1]), Some(&2.0));
}
#[test]
fn property_handle_is_copy_and_debug() {
let h1: PropertyHandle<f64> = PropertyHandle::new();
let _h2 = h1; let _h3 = h1;
let debug_str = format!("{:?}", h1);
assert!(debug_str.contains("PropertyHandle"));
assert!(debug_str.contains("f64"));
}
#[test]
fn newtype_wrapper_for_multiple_same_type_props() {
#[derive(Debug, PartialEq)]
struct Weight(f64);
#[derive(Debug, PartialEq)]
struct Temperature(f64);
let (_, vids) = build_triangle_mesh();
let mut props = MeshProperties::new();
let hw = props.add_vertex_prop::<Weight>();
let ht = props.add_vertex_prop::<Temperature>();
props.set_vertex_prop(hw, vids[0], Weight(1.0));
props.set_vertex_prop(ht, vids[0], Temperature(25.5));
assert_eq!(props.get_vertex_prop(hw, vids[0]), Some(&Weight(1.0)));
assert_eq!(props.get_vertex_prop(ht, vids[0]), Some(&Temperature(25.5)));
assert_eq!(props.vertex_prop_type_count(), 2);
}
#[test]
fn clear_all_vertex_props() {
let (_, vids) = build_triangle_mesh();
let mut props = MeshProperties::new();
let h1 = props.add_vertex_prop::<f64>();
let h2 = props.add_vertex_prop::<i32>();
props.set_vertex_prop(h1, vids[0], 1.0);
props.set_vertex_prop(h2, vids[0], 42);
props.clear_vertex_props();
assert_eq!(props.get_vertex_prop(h1, vids[0]), None);
assert_eq!(props.get_vertex_prop(h2, vids[0]), None);
assert!(props.has_vertex_prop::<f64>());
assert!(props.has_vertex_prop::<i32>());
}
#[test]
fn remove_then_reuse_slot_cleans_up_with_remove_all() {
let (mut mesh, vids) = build_triangle_mesh();
let mut props = MeshProperties::new();
let h = props.add_vertex_prop::<f64>();
props.set_vertex_prop(h, vids[0], 42.0);
assert_eq!(props.get_vertex_prop(h, vids[0]), Some(&42.0));
remove_vertex_with_props(&mut mesh, &mut props, vids[0]);
assert_eq!(props.get_vertex_prop(h, vids[0]), None);
let new_v = mesh.add_vertex(crate::storage::Vertex::new([5.0, 5.0, 5.0]));
assert_eq!(props.get_vertex_prop(h, new_v), None);
props.set_vertex_prop(h, new_v, 99.0);
assert_eq!(props.get_vertex_prop(h, new_v), Some(&99.0));
}
#[test]
fn property_store_default_is_empty() {
let store = PropertyStore::<f64>::default();
assert!(store.is_empty());
assert_eq!(store.len(), 0);
}
#[test]
fn property_store_sparse_indices() {
let mut store = PropertyStore::<i32>::new();
store.set(0, 1);
store.set(100, 2);
assert_eq!(store.len(), 2);
assert_eq!(store.get(0), Some(&1));
assert_eq!(store.get(100), Some(&2));
assert_eq!(store.get(50), None);
assert!(store.contains(0));
assert!(store.contains(100));
assert!(!store.contains(50));
}
#[test]
fn property_store_overwrite_value() {
let mut store = PropertyStore::<f64>::new();
store.set(3, 1.0);
store.set(3, 2.0);
assert_eq!(store.len(), 1);
assert_eq!(store.get(3), Some(&2.0));
}
#[test]
fn property_store_remove_out_of_bounds_returns_none() {
let mut store = PropertyStore::<f64>::new();
store.set(0, 1.0);
assert_eq!(store.remove(999), None);
assert_eq!(store.len(), 1);
}
#[test]
fn property_store_get_mut_none_for_unset() {
let mut store = PropertyStore::<f64>::new();
store.set(0, 1.0);
assert!(store.get_mut(1).is_none());
assert!(store.get_mut(99).is_none());
}
#[test]
fn property_store_array_type() {
let mut store = PropertyStore::<[f64; 3]>::new();
store.set(0, [1.0, 2.0, 3.0]);
store.set(1, [4.0, 5.0, 6.0]);
assert_eq!(store.get(0), Some(&[1.0, 2.0, 3.0]));
assert_eq!(store.get(1), Some(&[4.0, 5.0, 6.0]));
if let Some(v) = store.get_mut(0) {
v[1] = 99.0;
}
assert_eq!(store.get(0), Some(&[1.0, 99.0, 3.0]));
}
#[test]
fn property_store_bool_type() {
let mut store = PropertyStore::<bool>::new();
store.set(0, true);
store.set(1, false);
assert_eq!(store.get(0), Some(&true));
assert_eq!(store.get(1), Some(&false));
assert_eq!(store.len(), 2);
}
#[test]
fn property_store_iter_empty() {
let store = PropertyStore::<f64>::new();
assert_eq!(store.iter().count(), 0);
}
#[test]
fn property_store_iter_after_remove() {
let mut store = PropertyStore::<i32>::new();
store.set(0, 10);
store.set(1, 20);
store.set(2, 30);
store.remove(1);
let entries: Vec<(usize, i32)> = store.iter().map(|(i, &v)| (i, v)).collect();
assert_eq!(entries, vec![(0, 10), (2, 30)]);
}
#[test]
fn property_store_large_sparse_index() {
let mut store = PropertyStore::<u32>::new();
store.set(10_000, 42);
assert_eq!(store.len(), 1);
assert_eq!(store.get(10_000), Some(&42));
assert!(store.contains(10_000));
assert_eq!(store.get(9_999), None);
}
#[test]
fn multiple_property_types_on_same_mesh() {
let (_, vids) = build_triangle_mesh();
let mut props = MeshProperties::new();
let h_f64 = props.add_vertex_prop::<f64>();
let h_i32 = props.add_vertex_prop::<i32>();
let h_bool = props.add_vertex_prop::<bool>();
let h_arr = props.add_vertex_prop::<[f64; 3]>();
props.set_vertex_prop(h_f64, vids[0], 1.5);
props.set_vertex_prop(h_i32, vids[0], -7);
props.set_vertex_prop(h_bool, vids[0], true);
props.set_vertex_prop(h_arr, vids[0], [1.0, 2.0, 3.0]);
assert_eq!(props.get_vertex_prop(h_f64, vids[0]), Some(&1.5));
assert_eq!(props.get_vertex_prop(h_i32, vids[0]), Some(&-7));
assert_eq!(props.get_vertex_prop(h_bool, vids[0]), Some(&true));
assert_eq!(
props.get_vertex_prop(h_arr, vids[0]),
Some(&[1.0, 2.0, 3.0])
);
assert_eq!(props.vertex_prop_type_count(), 4);
}
#[test]
fn property_detach_and_reattach() {
let (_, vids) = build_triangle_mesh();
let mut props = MeshProperties::new();
let h = props.add_vertex_prop::<f64>();
props.set_vertex_prop(h, vids[0], 1.0);
props.set_vertex_prop(h, vids[1], 2.0);
assert_eq!(props.vertex_prop_type_count(), 1);
props.clear_vertex_props();
assert_eq!(props.get_vertex_prop(h, vids[0]), None);
assert_eq!(props.get_vertex_prop(h, vids[1]), None);
assert!(props.has_vertex_prop::<f64>());
props.set_vertex_prop(h, vids[0], 99.0);
assert_eq!(props.get_vertex_prop(h, vids[0]), Some(&99.0));
assert_eq!(props.get_vertex_prop(h, vids[1]), None);
}
#[test]
fn clear_halfedge_and_face_props() {
let (mesh, _vids) = build_triangle_mesh();
let heids: Vec<HalfEdgeId> = mesh.halfedge_ids().collect();
let fids: Vec<FaceId> = mesh.face_ids().collect();
let mut props = MeshProperties::new();
let hh = props.add_halfedge_prop::<i32>();
let hf = props.add_face_prop::<f64>();
for (i, &he) in heids.iter().enumerate() {
props.set_halfedge_prop(hh, he, i as i32);
}
props.set_face_prop(hf, fids[0], 2.71);
props.clear_halfedge_props();
props.clear_face_props();
for he in &heids {
assert_eq!(props.get_halfedge_prop(hh, *he), None);
}
assert_eq!(props.get_face_prop(hf, fids[0]), None);
assert!(props.has_halfedge_prop::<i32>());
assert!(props.has_face_prop::<f64>());
}
#[test]
fn remove_halfedge_with_props_wrapper() {
let (mesh, _vids) = build_triangle_mesh();
let heids: Vec<HalfEdgeId> = mesh.halfedge_ids().collect();
let mut mesh = mesh;
let mut props = MeshProperties::new();
let hh = props.add_halfedge_prop::<f64>();
props.set_halfedge_prop(hh, heids[0], 1.0);
props.set_halfedge_prop(hh, heids[1], 2.0);
let removed = remove_halfedge_with_props(&mut mesh, &mut props, heids[0]);
assert!(removed.is_some());
assert!(!mesh.contains_halfedge(heids[0]));
assert_eq!(props.get_halfedge_prop(hh, heids[0]), None);
assert_eq!(props.get_halfedge_prop(hh, heids[1]), Some(&2.0));
}
#[test]
fn remove_face_with_props_wrapper() {
let (mesh, _vids) = build_triangle_mesh();
let fids: Vec<FaceId> = mesh.face_ids().collect();
let mut mesh = mesh;
let mut props = MeshProperties::new();
let hf = props.add_face_prop::<u32>();
props.set_face_prop(hf, fids[0], 42);
let removed = remove_face_with_props(&mut mesh, &mut props, fids[0]);
assert!(removed.is_some());
assert!(!mesh.contains_face(fids[0]));
assert_eq!(props.get_face_prop(hf, fids[0]), None);
}
#[test]
fn halfedge_prop_mut_works() {
let (mesh, _) = build_triangle_mesh();
let heids: Vec<HalfEdgeId> = mesh.halfedge_ids().collect();
let mut props = MeshProperties::new();
let hh = props.add_halfedge_prop::<f64>();
props.set_halfedge_prop(hh, heids[0], 1.0);
if let Some(val) = props.get_halfedge_prop_mut(hh, heids[0]) {
*val = 10.0;
}
assert_eq!(props.get_halfedge_prop(hh, heids[0]), Some(&10.0));
}
#[test]
fn face_prop_mut_works() {
let (mesh, _) = build_triangle_mesh();
let fids: Vec<FaceId> = mesh.face_ids().collect();
let mut props = MeshProperties::new();
let hf = props.add_face_prop::<f64>();
props.set_face_prop(hf, fids[0], 1.0);
if let Some(val) = props.get_face_prop_mut(hf, fids[0]) {
*val = 20.0;
}
assert_eq!(props.get_face_prop(hf, fids[0]), Some(&20.0));
}
#[test]
fn has_halfedge_and_face_prop_queries() {
let mut props = MeshProperties::new();
assert!(!props.has_halfedge_prop::<f64>());
assert!(!props.has_face_prop::<i32>());
assert_eq!(props.halfedge_prop_type_count(), 0);
assert_eq!(props.face_prop_type_count(), 0);
let _ = props.add_halfedge_prop::<f64>();
let _ = props.add_face_prop::<i32>();
assert!(props.has_halfedge_prop::<f64>());
assert!(props.has_face_prop::<i32>());
assert_eq!(props.halfedge_prop_type_count(), 1);
assert_eq!(props.face_prop_type_count(), 1);
}
#[test]
fn unset_property_returns_none_as_default() {
let (_, vids) = build_triangle_mesh();
let mut props = MeshProperties::new();
let h = props.add_vertex_prop::<f64>();
assert_eq!(props.get_vertex_prop(h, vids[0]), None);
assert_eq!(props.get_vertex_prop(h, vids[1]), None);
}
#[test]
fn add_prop_overwrites_existing_store() {
let (_, vids) = build_triangle_mesh();
let mut props = MeshProperties::new();
let h = props.add_vertex_prop::<f64>();
props.set_vertex_prop(h, vids[0], 1.0);
assert_eq!(props.get_vertex_prop(h, vids[0]), Some(&1.0));
let _ = props.add_vertex_prop::<f64>();
assert_eq!(props.get_vertex_prop(h, vids[0]), None);
assert_eq!(props.vertex_prop_type_count(), 1);
}
#[test]
fn property_handle_default() {
let h: PropertyHandle<i32> = PropertyHandle::default();
let debug = format!("{:?}", h);
assert!(debug.contains("i32"));
}
#[test]
fn mesh_properties_debug() {
let mut props = MeshProperties::new();
let _ = props.add_vertex_prop::<f64>();
let _ = props.add_face_prop::<i32>();
let debug = format!("{:?}", props);
assert!(debug.contains("vertex_prop_types"));
assert!(debug.contains("face_prop_types"));
}
#[test]
fn empty_mesh_properties() {
let props = MeshProperties::new();
assert_eq!(props.vertex_prop_type_count(), 0);
assert_eq!(props.halfedge_prop_type_count(), 0);
assert_eq!(props.face_prop_type_count(), 0);
assert!(!props.has_vertex_prop::<f64>());
assert!(!props.has_halfedge_prop::<bool>());
assert!(!props.has_face_prop::<i32>());
}
#[test]
fn single_vertex_property() {
let mut mesh = MeshStorage::new();
let v = mesh.add_vertex(Vertex::new([1.0, 2.0, 3.0]));
let mut props = MeshProperties::new();
let h = props.add_vertex_prop::<f64>();
props.set_vertex_prop(h, v, 42.0);
assert_eq!(props.get_vertex_prop(h, v), Some(&42.0));
assert!(props.get_vertex_prop_mut(h, v).is_some());
}
#[test]
fn all_vertices_deleted_props_cleared() {
let (mut mesh, vids) = build_triangle_mesh();
let mut props = MeshProperties::new();
let h = props.add_vertex_prop::<f64>();
for v in &vids {
props.set_vertex_prop(h, *v, 1.0);
}
for v in &vids {
remove_vertex_with_props(&mut mesh, &mut props, *v);
}
for v in &vids {
assert_eq!(props.get_vertex_prop(h, *v), None);
}
assert_eq!(mesh.vertex_count(), 0);
}
#[test]
fn large_mesh_property_correctness() {
let mesh = crate::test_util::build_icosphere(1);
let vids: Vec<VertexId> = mesh.vertex_ids().collect();
let fids: Vec<FaceId> = mesh.face_ids().collect();
let mut props = MeshProperties::new();
let hv = props.add_vertex_prop::<f64>();
let hf = props.add_face_prop::<u32>();
for (i, &v) in vids.iter().enumerate() {
props.set_vertex_prop(hv, v, i as f64);
}
for (i, &f) in fids.iter().enumerate() {
props.set_face_prop(hf, f, i as u32);
}
for (i, &v) in vids.iter().enumerate() {
assert_eq!(props.get_vertex_prop(hv, v), Some(&(i as f64)));
}
for (i, &f) in fids.iter().enumerate() {
assert_eq!(props.get_face_prop(hf, f), Some(&(i as u32)));
}
for &v in vids.iter().step_by(3) {
props.remove_vertex_all_props(v);
assert_eq!(props.get_vertex_prop(hv, v), None);
}
}
fn build_triangle_mesh() -> (MeshStorage, [VertexId; 3]) {
let verts = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
let faces = vec![[0, 1, 2]];
let mesh = build_mesh_from_vertices_and_faces(&verts, &faces).unwrap();
let vids: Vec<VertexId> = mesh.vertex_ids().collect();
(mesh, [vids[0], vids[1], vids[2]])
}
}