hapi_rs/attribute/
mod.rs

1//! Geometry attributes access and iterators
2//!
3//! Geometry attributes of different types are represented as trait objects
4//! and need to be downcast to concrete types
5//!
6//! ```
7//!
8//! use hapi_rs::session::new_in_process;
9//! use hapi_rs::geometry::*;
10//! use hapi_rs::attribute::*;
11//! let session = new_in_process(None).unwrap();
12//! let lib = session.load_asset_file("otls/hapi_geo.hda").unwrap();
13//! let node = lib.try_create_first().unwrap();
14//! let geo = node.geometry().unwrap().unwrap();
15//! geo.node.cook_blocking().unwrap();
16//! let attr_p = geo.get_attribute(0, AttributeOwner::Point, "P").unwrap().expect("P exists");
17//! let attr_p = attr_p.downcast::<NumericAttr<f32>>().unwrap();
18//! attr_p.get(0).expect("read_attribute");
19//!
20//! ```
21mod array;
22mod async_;
23mod bindings;
24
25use crate::errors::Result;
26pub use crate::ffi::enums::StorageType;
27pub use crate::ffi::AttributeInfo;
28use crate::node::HoudiniNode;
29use crate::stringhandle::{StringArray, StringHandle};
30pub use array::*;
31use async_::AsyncAttribResult;
32use std::any::Any;
33use std::borrow::Cow;
34use std::ffi::{CStr, CString};
35use std::marker::PhantomData;
36
37pub type JobId = i32;
38
39mod private {
40    pub trait Sealed {}
41}
42pub trait AttribAccess: private::Sealed + Clone + Default + Send + Sized + 'static {
43    fn storage() -> StorageType;
44    fn storage_array() -> StorageType;
45    fn get(
46        name: &CStr,
47        node: &HoudiniNode,
48        info: &AttributeInfo,
49        part_id: i32,
50        buffer: &mut Vec<Self>,
51    ) -> Result<()>;
52    fn get_async(
53        name: &CStr,
54        node: &HoudiniNode,
55        info: &AttributeInfo,
56        part_id: i32,
57        buffer: &mut Vec<Self>,
58    ) -> Result<JobId>;
59    fn set(
60        name: &CStr,
61        node: &HoudiniNode,
62        info: &AttributeInfo,
63        part_id: i32,
64        data: &[Self],
65        start: i32,
66        len: i32,
67    ) -> Result<()>;
68
69    fn set_async(
70        name: &CStr,
71        node: &HoudiniNode,
72        info: &AttributeInfo,
73        part: i32,
74        data: &[Self],
75        start: i32,
76        len: i32,
77    ) -> Result<JobId>;
78
79    fn set_unique(
80        name: &CStr,
81        node: &HoudiniNode,
82        info: &AttributeInfo,
83        part_id: i32,
84        data: &[Self],
85        start: i32,
86    ) -> Result<()>;
87
88    fn set_unique_async(
89        name: &CStr,
90        node: &HoudiniNode,
91        info: &AttributeInfo,
92        part_id: i32,
93        data: &[Self],
94        start: i32,
95    ) -> Result<JobId>;
96
97    fn get_array(
98        name: &CStr,
99        node: &HoudiniNode,
100        info: &AttributeInfo,
101        part: i32,
102    ) -> Result<DataArray<'static, Self>>
103    where
104        [Self]: ToOwned<Owned = Vec<Self>>;
105
106    fn get_array_async(
107        name: &CStr,
108        node: &HoudiniNode,
109        info: &AttributeInfo,
110        data: &mut [Self],
111        sizes: &mut [i32],
112        part: i32,
113    ) -> Result<JobId>;
114    fn set_array(
115        name: &CStr,
116        node: &HoudiniNode,
117        info: &AttributeInfo,
118        part: i32,
119        data: &[Self],
120        sizes: &[i32],
121    ) -> Result<()>
122    where
123        [Self]: ToOwned<Owned = Vec<Self>>;
124
125    fn set_array_async(
126        name: &CStr,
127        node: &HoudiniNode,
128        info: &AttributeInfo,
129        part: i32,
130        data: &[Self],
131        sizes: &[i32],
132    ) -> Result<JobId>;
133}
134
135macro_rules! impl_sealed {
136    ($($x:ident),+ $(,)?) => {
137        $(impl private::Sealed for $x {})+
138    }
139}
140
141impl_sealed!(u8, i8, i16, i32, i64, f32, f64);
142
143impl StorageType {
144    // Helper for matching array types to actual data type,
145    // e.g. StorageType::Array is actually an array of StorageType::Int,
146    // StorageType::FloatArray is StorageType::Float
147    pub(crate) fn type_matches(&self, other: StorageType) -> bool {
148        use StorageType::*;
149        match other {
150            IntArray | Uint8Array | Int8Array | Int16Array | Int64Array => {
151                matches!(*self, Int | Uint8 | Int16 | Int64)
152            }
153            FloatArray | Float64Array => matches!(*self, Float | Float64),
154            StringArray => matches!(*self, StringArray),
155            _st => matches!(*self, _st),
156        }
157    }
158}
159
160pub(crate) struct AttributeBundle {
161    pub(crate) info: AttributeInfo,
162    pub(crate) name: CString,
163    pub(crate) node: HoudiniNode,
164}
165
166pub struct NumericAttr<T: AttribAccess>(pub(crate) AttributeBundle, PhantomData<T>);
167
168pub struct NumericArrayAttr<T: AttribAccess>(pub(crate) AttributeBundle, PhantomData<T>);
169
170pub struct StringAttr(pub(crate) AttributeBundle);
171
172pub struct StringArrayAttr(pub(crate) AttributeBundle);
173
174pub struct DictionaryAttr(pub(crate) AttributeBundle);
175
176pub struct DictionaryArrayAttr(pub(crate) AttributeBundle);
177
178impl<T: AttribAccess> NumericArrayAttr<T>
179where
180    [T]: ToOwned<Owned = Vec<T>>,
181{
182    pub(crate) fn new(
183        name: CString,
184        info: AttributeInfo,
185        node: HoudiniNode,
186    ) -> NumericArrayAttr<T> {
187        NumericArrayAttr(AttributeBundle { info, name, node }, PhantomData)
188    }
189    pub fn get(&self, part_id: i32) -> Result<DataArray<T>> {
190        debug_assert!(self.0.info.storage().type_matches(T::storage()));
191        T::get_array(&self.0.name, &self.0.node, &self.0.info, part_id)
192    }
193
194    pub fn get_async(&self, part_id: i32) -> Result<(JobId, DataArray<T>)> {
195        let info = &self.0.info;
196        debug_assert!(info.storage().type_matches(T::storage()));
197        let mut data = vec![T::default(); info.total_array_elements() as usize];
198        let mut sizes = vec![0i32; info.count() as usize];
199        let job_id = T::get_array_async(
200            &self.0.name,
201            &self.0.node,
202            info,
203            &mut data,
204            &mut sizes,
205            part_id,
206        )?;
207        Ok((job_id, DataArray::new_owned(data, sizes)))
208    }
209
210    pub fn set(&self, part_id: i32, values: &DataArray<T>) -> Result<()> {
211        debug_assert!(self.0.info.storage().type_matches(T::storage()));
212        debug_assert_eq!(
213            self.0.info.count(),
214            values.sizes().len() as i32,
215            "sizes array must be the same as AttributeInfo::count"
216        );
217        debug_assert_eq!(
218            self.0.info.total_array_elements(),
219            values.data().len() as i64,
220            "data array must be the same as AttributeInfo::total_array_elements"
221        );
222        T::set_array(
223            &self.0.name,
224            &self.0.node,
225            &self.0.info,
226            part_id,
227            values.data(),
228            values.sizes(),
229        )
230    }
231}
232
233impl<T: AttribAccess> NumericAttr<T> {
234    pub(crate) fn new(name: CString, info: AttributeInfo, node: HoudiniNode) -> NumericAttr<T> {
235        NumericAttr(AttributeBundle { info, name, node }, PhantomData)
236    }
237    /// Get attribute value. Allocates a new vector on every call
238    pub fn get(&self, part_id: i32) -> Result<Vec<T>> {
239        debug_assert_eq!(self.0.info.storage(), T::storage());
240        let mut buffer = vec![];
241        T::get(
242            &self.0.name,
243            &self.0.node,
244            &self.0.info,
245            part_id,
246            &mut buffer,
247        )?;
248        Ok(buffer)
249    }
250    /// Start filling a given buffer asynchronously and return a job id.
251    /// It's important to keep the buffer alive until the job is complete
252    pub fn read_async_into(&self, part_id: i32, buffer: &mut Vec<T>) -> Result<i32> {
253        // TODO: Get an updated attribute info since point count can change between calls.
254        // but there's looks like some use after free on the C side, when AttributeInfo gets
255        // accessed after it gets dropped in Rust, so we can't get new AttributeInfo here.
256        let info = &self.0.info;
257        buffer.resize((info.count() * info.tuple_size()) as usize, T::default());
258        T::get_async(&self.0.name, &self.0.node, &self.0.info, part_id, buffer)
259    }
260
261    pub fn get_async(&self, part_id: i32) -> Result<AsyncAttribResult<T>> {
262        let info = &self.0.info;
263        let size = (info.count() * info.tuple_size()) as usize;
264        let mut data = Vec::<T>::with_capacity(size);
265        let job_id = T::get_async(&self.0.name, &self.0.node, info, part_id, &mut data)?;
266        Ok(AsyncAttribResult {
267            job_id,
268            data,
269            size,
270            session: self.0.node.session.clone(),
271        })
272    }
273
274    /// Read the attribute data into a provided buffer. The buffer will be auto-resized
275    /// from the attribute info.
276    pub fn read_into(&self, part_id: i32, buffer: &mut Vec<T>) -> Result<()> {
277        debug_assert_eq!(self.0.info.storage(), T::storage());
278        // Get an updated attribute info since point count can change between calls
279        let info = AttributeInfo::new(&self.0.node, part_id, self.0.info.owner(), &self.0.name)?;
280        T::get(&self.0.name, &self.0.node, &info, part_id, buffer)
281    }
282    pub fn set(&self, part_id: i32, values: &[T]) -> Result<()> {
283        debug_assert_eq!(self.0.info.storage(), T::storage());
284        T::set(
285            &self.0.name,
286            &self.0.node,
287            &self.0.info,
288            part_id,
289            values,
290            0,
291            self.0.info.count().min(values.len() as i32),
292        )
293    }
294
295    /// Set multiple attribute data to the same value.
296    /// value length must be less or equal to attribute tuple size.
297    pub fn set_unique(&self, part_id: i32, value: &[T]) -> Result<()> {
298        debug_assert_eq!(self.0.info.storage(), T::storage());
299        debug_assert!(value.len() <= self.0.info.tuple_size() as usize);
300        T::set_unique(&self.0.name, &self.0.node, &self.0.info, part_id, value, 0)
301    }
302}
303
304impl StringAttr {
305    pub(crate) fn new(name: CString, info: AttributeInfo, node: HoudiniNode) -> StringAttr {
306        StringAttr(AttributeBundle { info, name, node })
307    }
308    pub fn get(&self, part_id: i32) -> Result<StringArray> {
309        debug_assert!(self.0.node.is_valid()?);
310        bindings::get_attribute_string_data(
311            &self.0.node,
312            part_id,
313            self.0.name.as_c_str(),
314            &self.0.info.0,
315        )
316    }
317
318    pub fn get_async(&self, part_id: i32) -> Result<AsyncAttribResult<StringHandle>> {
319        bindings::get_attribute_string_data_async(
320            &self.0.node,
321            part_id,
322            self.0.name.as_c_str(),
323            &self.0.info.0,
324        )
325    }
326
327    pub fn set(&self, part_id: i32, values: &[impl AsRef<CStr>]) -> Result<()> {
328        let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
329        bindings::set_attribute_string_data(
330            &self.0.node,
331            part_id,
332            self.0.name.as_c_str(),
333            &self.0.info.0,
334            ptrs.as_mut(),
335        )
336    }
337
338    pub fn set_async(&self, part_id: i32, values: &[impl AsRef<CStr>]) -> Result<JobId> {
339        let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
340        bindings::set_attribute_string_data_async(
341            &self.0.node,
342            self.0.name.as_c_str(),
343            part_id,
344            &self.0.info.0,
345            ptrs.as_mut(),
346        )
347    }
348    /// Set multiple attribute data to the same value.
349    /// value length must be less or equal to attribute tuple size.
350    pub fn set_unique(&self, part: i32, value: &CStr) -> Result<()> {
351        bindings::set_attribute_string_unique_data(
352            &self.0.node,
353            self.0.name.as_c_str(),
354            &self.0.info.0,
355            part,
356            value.as_ptr(),
357        )
358    }
359
360    /// Set multiple attribute string data to the same unique value asynchronously.
361    pub fn set_unique_async(&self, part: i32, value: &CStr) -> Result<JobId> {
362        bindings::set_attribute_string_unique_data_async(
363            &self.0.node,
364            self.0.name.as_c_str(),
365            &self.0.info.0,
366            part,
367            value.as_ptr(),
368        )
369    }
370
371    /// Set attribute string data by index.
372    pub fn set_indexed(
373        &self,
374        part_id: i32,
375        values: &[impl AsRef<CStr>],
376        indices: &[i32],
377    ) -> Result<()> {
378        let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
379        bindings::set_attribute_indexed_string_data(
380            &self.0.node,
381            part_id,
382            self.0.name.as_c_str(),
383            &self.0.info.0,
384            ptrs.as_mut(),
385            indices,
386        )
387    }
388
389    pub fn set_indexed_async(
390        &self,
391        part_id: i32,
392        values: &[impl AsRef<CStr>],
393        indices: &[i32],
394    ) -> Result<JobId> {
395        let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
396        bindings::set_attribute_indexed_string_data_async(
397            &self.0.node,
398            part_id,
399            self.0.name.as_c_str(),
400            &self.0.info.0,
401            ptrs.as_mut(),
402            indices,
403        )
404    }
405}
406
407impl StringArrayAttr {
408    pub(crate) fn new(name: CString, info: AttributeInfo, node: HoudiniNode) -> StringArrayAttr {
409        StringArrayAttr(AttributeBundle { info, name, node })
410    }
411    pub fn get(&self, part_id: i32) -> Result<StringMultiArray> {
412        debug_assert!(self.0.node.is_valid()?);
413        bindings::get_attribute_string_array_data(
414            &self.0.node,
415            self.0.name.as_c_str(),
416            part_id,
417            &self.0.info,
418        )
419    }
420
421    pub fn get_async(&self, part_id: i32) -> Result<(JobId, StringMultiArray)> {
422        let mut handles = vec![StringHandle(-1); self.0.info.total_array_elements() as usize];
423        let mut sizes = vec![0; self.0.info.count() as usize];
424        let job_id = bindings::get_attribute_string_array_data_async(
425            &self.0.node,
426            self.0.name.as_c_str(),
427            part_id,
428            &self.0.info.0,
429            &mut handles,
430            &mut sizes,
431        )?;
432        Ok((
433            job_id,
434            StringMultiArray {
435                handles,
436                sizes,
437                session: self.0.node.session.clone(),
438            },
439        ))
440    }
441    pub fn set(&self, part_id: i32, values: &[impl AsRef<CStr>], sizes: &[i32]) -> Result<()> {
442        debug_assert!(self.0.node.is_valid()?);
443        let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
444        bindings::set_attribute_string_array_data(
445            &self.0.node,
446            self.0.name.as_c_str(),
447            &self.0.info.0,
448            part_id,
449            ptrs.as_mut(),
450            sizes,
451        )
452    }
453
454    pub fn set_async(
455        &self,
456        part_id: i32,
457        values: &[impl AsRef<CStr>],
458        sizes: &[i32],
459    ) -> Result<JobId> {
460        debug_assert!(self.0.node.is_valid()?);
461        let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
462        bindings::set_attribute_string_array_data_async(
463            &self.0.node,
464            self.0.name.as_c_str(),
465            part_id,
466            &self.0.info.0,
467            ptrs.as_mut(),
468            sizes,
469        )
470    }
471}
472
473impl DictionaryAttr {
474    pub(crate) fn new(name: CString, info: AttributeInfo, node: HoudiniNode) -> Self {
475        DictionaryAttr(AttributeBundle { info, name, node })
476    }
477
478    pub fn get(&self, part_id: i32) -> Result<StringArray> {
479        debug_assert!(self.0.node.is_valid()?);
480        bindings::get_attribute_dictionary_data(
481            &self.0.node,
482            part_id,
483            self.0.name.as_c_str(),
484            &self.0.info.0,
485        )
486    }
487
488    pub fn set_async(&self, part_id: i32, values: &[impl AsRef<CStr>]) -> Result<JobId> {
489        let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
490        bindings::set_attribute_dictionary_data_async(
491            &self.0.node,
492            self.0.name.as_c_str(),
493            part_id,
494            &self.0.info.0,
495            ptrs.as_mut(),
496        )
497    }
498
499    pub fn get_async(&self, part_id: i32) -> Result<AsyncAttribResult<StringHandle>> {
500        bindings::get_attribute_dictionary_data_async(
501            &self.0.node,
502            part_id,
503            self.0.name.as_c_str(),
504            &self.0.info.0,
505        )
506    }
507
508    /// Set dictionary attribute values where each string should be a JSON-encoded value.
509    pub fn set(&self, part_id: i32, values: &[impl AsRef<CStr>]) -> Result<()> {
510        let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
511        bindings::set_attribute_dictionary_data(
512            &self.0.node,
513            part_id,
514            self.0.name.as_c_str(),
515            &self.0.info.0,
516            ptrs.as_mut(),
517        )
518    }
519}
520
521impl DictionaryArrayAttr {
522    pub(crate) fn new(name: CString, info: AttributeInfo, node: HoudiniNode) -> Self {
523        DictionaryArrayAttr(AttributeBundle { info, name, node })
524    }
525    pub fn get(&self, part_id: i32) -> Result<StringMultiArray> {
526        debug_assert!(self.0.node.is_valid()?);
527        bindings::get_attribute_dictionary_array_data(
528            &self.0.node,
529            &self.0.name,
530            part_id,
531            &self.0.info,
532        )
533    }
534
535    pub fn get_async(&self, part_id: i32) -> Result<(JobId, StringMultiArray)> {
536        let mut handles = vec![StringHandle(-1); self.0.info.total_array_elements() as usize];
537        let mut sizes = vec![0; self.0.info.count() as usize];
538        let job_id = bindings::get_attribute_dictionary_array_data_async(
539            &self.0.node,
540            self.0.name.as_c_str(),
541            part_id,
542            &self.0.info.0,
543            &mut handles,
544            &mut sizes,
545        )?;
546        Ok((
547            job_id,
548            StringMultiArray {
549                handles,
550                sizes,
551                session: self.0.node.session.clone(),
552            },
553        ))
554    }
555    pub fn set(&self, part_id: i32, values: &[impl AsRef<CStr>], sizes: &[i32]) -> Result<()> {
556        debug_assert!(self.0.node.is_valid()?);
557        let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
558        bindings::set_attribute_dictionary_array_data(
559            &self.0.node,
560            self.0.name.as_c_str(),
561            &self.0.info.0,
562            part_id,
563            ptrs.as_mut(),
564            sizes,
565        )
566    }
567
568    pub fn set_async(
569        &self,
570        part_id: i32,
571        values: &[impl AsRef<CStr>],
572        sizes: &[i32],
573    ) -> Result<JobId> {
574        debug_assert!(self.0.node.is_valid()?);
575        let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
576        bindings::set_attribute_dictionary_array_data_async(
577            &self.0.node,
578            self.0.name.as_c_str(),
579            part_id,
580            &self.0.info.0,
581            ptrs.as_mut(),
582            sizes,
583        )
584    }
585}
586
587#[doc(hidden)]
588pub trait AsAttribute: Send {
589    fn info(&self) -> &AttributeInfo;
590    fn storage(&self) -> StorageType;
591    fn boxed(self) -> Box<dyn AnyAttribWrapper>
592    where
593        Self: Sized + 'static,
594    {
595        Box::new(self)
596    }
597    fn name(&self) -> &CStr;
598    fn node(&self) -> &HoudiniNode;
599}
600
601impl<T: AttribAccess> AsAttribute for NumericAttr<T> {
602    fn info(&self) -> &AttributeInfo {
603        &self.0.info
604    }
605    fn storage(&self) -> StorageType {
606        T::storage()
607    }
608
609    fn name(&self) -> &CStr {
610        &self.0.name
611    }
612
613    fn node(&self) -> &HoudiniNode {
614        &self.0.node
615    }
616}
617
618impl<T: AttribAccess> AsAttribute for NumericArrayAttr<T> {
619    fn info(&self) -> &AttributeInfo {
620        &self.0.info
621    }
622    fn storage(&self) -> StorageType {
623        T::storage()
624    }
625    fn name(&self) -> &CStr {
626        &self.0.name
627    }
628    fn node(&self) -> &HoudiniNode {
629        &self.0.node
630    }
631}
632
633impl AsAttribute for StringAttr {
634    fn info(&self) -> &AttributeInfo {
635        &self.0.info
636    }
637
638    fn storage(&self) -> StorageType {
639        StorageType::String
640    }
641
642    fn name(&self) -> &CStr {
643        &self.0.name
644    }
645
646    fn node(&self) -> &HoudiniNode {
647        &self.0.node
648    }
649}
650
651impl AsAttribute for StringArrayAttr {
652    fn info(&self) -> &AttributeInfo {
653        &self.0.info
654    }
655
656    fn storage(&self) -> StorageType {
657        StorageType::StringArray
658    }
659
660    fn name(&self) -> &CStr {
661        &self.0.name
662    }
663
664    fn node(&self) -> &HoudiniNode {
665        &self.0.node
666    }
667}
668
669impl AsAttribute for DictionaryAttr {
670    fn info(&self) -> &AttributeInfo {
671        &self.0.info
672    }
673
674    fn storage(&self) -> StorageType {
675        StorageType::Dictionary
676    }
677
678    fn name(&self) -> &CStr {
679        &self.0.name
680    }
681
682    fn node(&self) -> &HoudiniNode {
683        &self.0.node
684    }
685}
686
687impl AsAttribute for DictionaryArrayAttr {
688    fn info(&self) -> &AttributeInfo {
689        &self.0.info
690    }
691
692    fn storage(&self) -> StorageType {
693        StorageType::DictionaryArray
694    }
695
696    fn name(&self) -> &CStr {
697        &self.0.name
698    }
699
700    fn node(&self) -> &HoudiniNode {
701        &self.0.node
702    }
703}
704
705#[doc(hidden)]
706pub trait AnyAttribWrapper: Any + AsAttribute {
707    fn as_any(&self) -> &dyn Any;
708}
709
710impl<T: AsAttribute + 'static> AnyAttribWrapper for T {
711    fn as_any(&self) -> &dyn Any {
712        self
713    }
714}
715
716pub struct Attribute(Box<dyn AnyAttribWrapper>);
717
718impl Attribute {
719    pub(crate) fn new(attr_obj: Box<dyn AnyAttribWrapper>) -> Self {
720        Attribute(attr_obj)
721    }
722    pub fn downcast<T: AnyAttribWrapper>(&self) -> Option<&T> {
723        self.0.as_any().downcast_ref::<T>()
724    }
725    pub fn name(&self) -> Cow<str> {
726        self.0.name().to_string_lossy()
727    }
728    pub fn storage(&self) -> StorageType {
729        self.0.storage()
730    }
731    pub fn info(&self) -> &AttributeInfo {
732        self.0.info()
733    }
734    pub fn delete(self, part_id: i32) -> Result<()> {
735        crate::ffi::delete_attribute(self.0.node(), part_id, self.0.name(), &self.0.info().0)
736    }
737}