1use crate::safety_assert;
2use serde::Serialize;
3
4use kiddo::immutable::float::kdtree::ImmutableKdTree;
5use kiddo::SquaredEuclidean;
6
7use super::buffer;
8use crate::bit_coder::{ByteWriter, Reader};
9use crate::types::DataValue;
10use crate::types::{AttributeValueIdx, PointIdx, VecPointIdx, Vector};
11
12fn vector_to_f64_array<Data: Vector<N>, const N: usize>(v: &Data) -> [f64; N] {
13 let mut out = [0.0f64; N];
14 for (i, slot) in out.iter_mut().enumerate() {
15 *slot = (*v.get(i)).to_f64();
16 }
17 out
18}
19
20#[derive(Debug, thiserror::Error)]
22pub enum Err {
23 #[error("Invalid attribute domain id: {0}")]
25 InvalidAttributeDomainId(u8),
26 #[error("Reader error: {0}")]
28 ReaderError(#[from] crate::bit_coder::ReaderErr),
29 #[error("Invalid DataTypeId: {0}")]
31 InvalidDataTypeId(u8),
32}
33
34#[derive(Debug, Clone)]
40pub struct Attribute {
41 id: AttributeId,
43
44 buffer: buffer::attribute::AttributeBuffer,
46
47 att_type: AttributeType,
49
50 domain: AttributeDomain,
52
53 parents: Vec<AttributeId>,
55
56 point_to_att_val_map: Option<VecPointIdx<AttributeValueIdx>>,
60
61 name: Option<String>,
63}
64
65impl Attribute {
66 pub fn new<Data, const N: usize>(
69 data: Vec<Data>,
70 att_type: AttributeType,
71 domain: AttributeDomain,
72 parents: Vec<AttributeId>,
73 ) -> Self
74 where
75 Data: Vector<N>,
76 {
77 let id = AttributeId::new(0); let buffer = buffer::attribute::AttributeBuffer::from_vec(data);
79 let mut out = Self {
80 id,
81 buffer,
82 parents,
83 att_type,
84 domain,
85 point_to_att_val_map: None,
86 name: None,
87 };
88 out.remove_duplicate_values::<Data, N>();
89 out
90 }
91
92 pub fn new_empty(
95 id: AttributeId,
96 att_type: AttributeType,
97 domain: AttributeDomain,
98 component_type: ComponentDataType,
99 num_components: usize,
100 ) -> Self {
101 let buffer = buffer::attribute::AttributeBuffer::new(component_type, num_components);
102 Self {
103 id,
104 buffer,
105 parents: Vec::new(),
106 att_type,
107 domain,
108 point_to_att_val_map: None,
109 name: None,
110 }
111 }
112
113 pub fn from<Data, const N: usize>(
116 id: AttributeId,
117 data: Vec<Data>,
118 att_type: AttributeType,
119 domain: AttributeDomain,
120 parents: Vec<AttributeId>,
121 ) -> Self
122 where
123 Data: Vector<N>,
124 {
125 let buffer = buffer::attribute::AttributeBuffer::from_vec(data);
126 let mut out = Self {
127 id,
128 buffer,
129 parents,
130 att_type,
131 domain,
132 point_to_att_val_map: None,
133 name: None,
134 };
135 out.remove_duplicate_values::<Data, N>();
136 out
137 }
138
139 pub fn from_without_removing_duplicates<Data, const N: usize>(
142 id: AttributeId,
143 data: Vec<Data>,
144 att_type: AttributeType,
145 domain: AttributeDomain,
146 parents: Vec<AttributeId>,
147 ) -> Self
148 where
149 Data: Vector<N>,
150 {
151 let buffer = buffer::attribute::AttributeBuffer::from_vec(data);
152 Self {
153 id,
154 buffer,
155 parents,
156 att_type,
157 domain,
158 point_to_att_val_map: None,
159 name: None,
160 }
161 }
162
163 pub fn get<Data, const N: usize>(&self, p_idx: PointIdx) -> Data
165 where
166 Data: Vector<N>,
167 Data::Component: DataValue,
168 {
169 self.buffer.get(self.get_unique_val_idx(p_idx))
170 }
171
172 pub fn get_unique_val<Data, const N: usize>(&self, val_idx: AttributeValueIdx) -> Data
174 where
175 Data: Vector<N>,
176 Data::Component: DataValue,
177 {
178 self.buffer.get(val_idx)
179 }
180
181 pub fn get_component_type(&self) -> ComponentDataType {
183 self.buffer.get_component_type()
184 }
185
186 pub fn get_data_as_bytes(&self) -> &[u8] {
188 self.buffer.as_slice_u8()
189 }
190
191 pub fn set_point_to_att_val_map(
193 &mut self,
194 point_to_att_val_map: Option<VecPointIdx<AttributeValueIdx>>,
195 ) {
196 self.point_to_att_val_map = point_to_att_val_map;
197 }
198
199 pub fn take_point_to_att_val_map(self) -> Option<VecPointIdx<AttributeValueIdx>> {
201 self.point_to_att_val_map
202 }
203
204 #[inline]
206 pub fn point_map_as_slice(&self) -> Option<&[AttributeValueIdx]> {
207 self.point_to_att_val_map.as_ref().map(|m| m.as_slice())
208 }
209
210 #[inline]
214 pub fn set_point_att_val(&mut self, p_idx: PointIdx, val_idx: AttributeValueIdx) {
215 self.point_to_att_val_map
216 .as_mut()
217 .expect("point-to-value map must be initialized before per-point assignment")[p_idx] =
218 val_idx;
219 }
220
221 #[inline]
227 pub unsafe fn set_point_att_val_unchecked(
228 &mut self,
229 p_idx: PointIdx,
230 val_idx: AttributeValueIdx,
231 ) {
232 match self.point_to_att_val_map.as_mut() {
233 Some(map) => *map.get_unchecked_mut(p_idx) = val_idx,
234 None => core::hint::unreachable_unchecked(),
236 }
237 }
238
239 #[inline]
241 pub fn get_id(&self) -> AttributeId {
242 self.id
243 }
244
245 #[inline]
247 pub fn get_num_components(&self) -> usize {
248 self.buffer.get_num_components()
249 }
250
251 #[inline]
253 pub fn get_attribute_type(&self) -> AttributeType {
254 self.att_type
255 }
256
257 #[inline]
259 pub fn get_domain(&self) -> AttributeDomain {
260 self.domain
261 }
262
263 #[inline]
265 pub fn get_parents(&self) -> &Vec<AttributeId> {
266 self.parents.as_ref()
267 }
268
269 #[inline(always)]
273 pub fn len(&self) -> usize {
274 if let Some(f) = &self.point_to_att_val_map {
275 f.len()
276 } else {
277 self.buffer.len()
278 }
279 }
280
281 #[inline(always)]
283 pub fn is_empty(&self) -> bool {
284 self.len() == 0
285 }
286
287 #[inline(always)]
289 pub fn num_unique_values(&self) -> usize {
290 self.buffer.len()
291 }
292
293 pub fn mint(&mut self, src: PointIdx) -> PointIdx {
302 let src_val = self.get_unique_val_idx(src);
303 let num_unique = self.num_unique_values();
304 let map = self.point_to_att_val_map.get_or_insert_with(|| {
305 (0..num_unique)
306 .map(AttributeValueIdx::from)
307 .collect::<Vec<_>>()
308 .into()
309 });
310 let new_idx = PointIdx::from(map.len());
311 map.push(src_val);
312 new_idx
313 }
314
315 #[inline]
318 pub fn get_unique_val_idx(&self, idx: PointIdx) -> AttributeValueIdx {
319 let idx_usize = usize::from(idx);
320 assert!(
321 idx_usize < self.len(),
322 "Index out of bounds: idx = {}, len = {}",
323 idx_usize,
324 self.len()
325 );
326 if let Some(ref point_to_att_val_map) = self.point_to_att_val_map {
327 point_to_att_val_map[idx]
328 } else {
329 idx_usize.into()
331 }
332 }
333
334 #[inline]
336 pub fn set_name(&mut self, name: String) {
337 self.name = Some(name);
338 }
339
340 #[inline]
342 pub fn get_name(&self) -> Option<&String> {
343 self.name.as_ref()
344 }
345
346 #[inline]
350 pub fn unique_vals_as_slice<Data>(&self) -> &[Data] {
351 assert_eq!(
352 self.buffer.get_num_components() * self.buffer.get_component_type().size(),
353 std::mem::size_of::<Data>(),
354 );
355 unsafe { self.buffer.as_slice::<Data>() }
356 }
357
358 #[inline]
362 pub fn unique_vals_as_slice_mut<Data>(&mut self) -> &mut [Data] {
363 assert_eq!(
364 self.buffer.get_num_components() * self.buffer.get_component_type().size(),
365 std::mem::size_of::<Data>(),
366 );
367 unsafe { self.buffer.as_slice_mut::<Data>() }
368 }
369
370 #[inline]
375 pub unsafe fn unique_vals_as_slice_unchecked<Data>(&self) -> &[Data] {
376 self.buffer.as_slice::<Data>()
378 }
379
380 #[inline]
386 pub unsafe fn unique_vals_as_slice_unchecked_mut<Data>(&mut self) -> &mut [Data] {
387 self.buffer.as_slice_mut::<Data>()
389 }
390
391 pub fn permute(&mut self, indices: &[usize]) {
394 assert!(
395 indices.len() == self.len(),
396 "Indices length must match the buffer length: indices.len() = {}, self.len() = {}",
397 indices.len(),
398 self.len()
399 );
400 assert!(
401 indices.iter().all(|&i| i < self.len()),
402 "All indices must be within the buffer length: indices = {:?}, self.len() = {}",
403 indices,
404 self.len()
405 );
406 unsafe {
407 self.buffer.permute_unchecked(indices);
408 }
409 }
410
411 pub fn permute_unchecked(&mut self, indices: &[usize]) {
416 safety_assert!(
417 indices.len() == self.len(),
418 "Indices length must match the buffer length: indices.len() = {}, self.len() = {}",
419 indices.len(),
420 self.len()
421 );
422 safety_assert!(
423 indices.iter().all(|&i| i < self.len()),
424 "All indices must be within the buffer length: indices = {:?}, self.len() = {}",
425 indices,
426 self.len()
427 );
428 unsafe {
429 self.buffer.permute_unchecked(indices);
430 }
431 }
432
433 pub fn swap(&mut self, i: usize, j: usize) {
435 assert!(
436 i < self.len() && j < self.len(),
437 "Indices out of bounds: i = {}, j = {}, len = {}",
438 i,
439 j,
440 self.len()
441 );
442 unsafe {
443 self.buffer.swap_unchecked(i, j);
444 }
445 }
446
447 pub fn take_values<Data, const N: usize>(self) -> Vec<Data>
450 where
451 Data: Vector<N>,
452 {
453 assert_eq!(self.get_num_components(), N,);
454 assert_eq!(self.get_component_type(), Data::Component::get_dyn(),);
455
456 unsafe { self.buffer.into_vec_unchecked::<Data, N>() }
457 }
458
459 pub fn into_parts<Data, const N: usize>(
463 mut self,
464 ) -> (Vec<Data>, Option<VecPointIdx<AttributeValueIdx>>, Self)
465 where
466 Data: Vector<N>,
467 {
468 let num_components = self.get_num_components();
469 let component_type = self.get_component_type();
470 assert_eq!(num_components, N,);
471 assert_eq!(component_type, Data::Component::get_dyn(),);
472 let mut new_buffer = buffer::attribute::AttributeBuffer::from_vec(Vec::<Data>::new());
473 std::mem::swap(&mut self.buffer, &mut new_buffer);
474 let data = unsafe { new_buffer.into_vec_unchecked::<Data, N>() };
475
476 let mut point_to_att_val_map = None;
477 std::mem::swap(&mut point_to_att_val_map, &mut self.point_to_att_val_map);
478
479 (data, point_to_att_val_map, self)
480 }
481
482 pub fn set_values<Data, const N: usize>(&mut self, data: Vec<Data>)
485 where
486 Data: Vector<N>,
487 {
488 assert_eq!(self.get_num_components(), N,);
489 assert_eq!(self.get_component_type(), Data::Component::get_dyn(),);
490 assert_eq!(self.len(), 0);
491 self.buffer = buffer::attribute::AttributeBuffer::from_vec(data);
492 }
493
494 pub fn remove_duplicate_values<Data, const N: usize>(&mut self)
497 where
498 Data: Vector<N>,
499 {
500 let n = self.len();
501 if n <= 1 {
502 return;
503 }
504
505 let values = self.unique_vals_as_slice::<Data>();
506
507 let f64_points: Vec<[f64; N]> = values.iter().map(|v| vector_to_f64_array(v)).collect();
509
510 let tree = ImmutableKdTree::<f64, u32, N, 32>::new_from_slice(&f64_points);
512
513 let mut canonical_index = vec![usize::MAX; n];
516 let mut has_duplicates = false;
517
518 for i in 0..n {
519 if canonical_index[i] != usize::MAX {
520 continue;
522 }
523 canonical_index[i] = i; let neighbors = tree.within_unsorted::<SquaredEuclidean>(&f64_points[i], f64::EPSILON);
527
528 for neighbor in &neighbors {
529 let j = neighbor.item as usize;
530 if j <= i || canonical_index[j] != usize::MAX {
531 continue;
532 }
533 if values[i] == values[j] {
535 canonical_index[j] = i;
536 has_duplicates = true;
537 }
538 }
539 }
540
541 if !has_duplicates {
542 return;
543 }
544
545 let mut old_to_new = vec![0usize; n];
547 let mut keep_indices = Vec::new();
548 let mut new_idx = 0;
549 for i in 0..n {
550 if canonical_index[i] == i {
551 old_to_new[i] = new_idx;
553 keep_indices.push(i);
554 new_idx += 1;
555 }
556 }
557
558 let map_data: Vec<AttributeValueIdx> = (0..n)
560 .map(|i| old_to_new[canonical_index[i]].into())
561 .collect();
562 self.point_to_att_val_map = Some(VecPointIdx::<_>::from(map_data));
563
564 self.buffer.retain_indices(&keep_indices);
566 }
567
568 pub fn remove_unique_val_dyn(&mut self, val_idx: usize) {
569 assert!(
570 val_idx < self.num_unique_values(),
571 "Attribute value index out of bounds: {}",
572 val_idx
573 );
574 match self.get_component_type().size() * self.get_num_components() {
575 1 => self.buffer.remove::<u8, 1>(val_idx),
576 2 => self.buffer.remove::<u16, 1>(val_idx),
577 4 => self.buffer.remove::<u32, 1>(val_idx),
578 6 => self.buffer.remove::<u16, 3>(val_idx),
579 8 => self.buffer.remove::<u64, 1>(val_idx),
580 12 => self.buffer.remove::<u32, 3>(val_idx),
581 16 => self.buffer.remove::<u64, 2>(val_idx),
582 18 => self.buffer.remove::<u64, 3>(val_idx),
583 _ => panic!(
584 "Unsupported component size: {}",
585 self.get_component_type().size()
586 ),
587 }
588 }
589
590 pub fn retain_points_dyn(&mut self, keep_point_indices: &[usize]) {
593 if let Some(ref map) = self.point_to_att_val_map {
594 let num_unique = self.buffer.len();
596 let mut unique_val_referenced = vec![false; num_unique];
597 let mut new_map = Vec::with_capacity(keep_point_indices.len());
598
599 for &p in keep_point_indices {
600 let val_idx = map[PointIdx::from(p)];
601 unique_val_referenced[usize::from(val_idx)] = true;
602 new_map.push(val_idx);
603 }
604
605 let mut old_unique_to_new = vec![0usize; num_unique];
607 let mut keep_unique_indices = Vec::new();
608 let mut new_unique_idx = 0;
609 for i in 0..num_unique {
610 if unique_val_referenced[i] {
611 old_unique_to_new[i] = new_unique_idx;
612 keep_unique_indices.push(i);
613 new_unique_idx += 1;
614 }
615 }
616
617 let new_map: Vec<AttributeValueIdx> = new_map
619 .iter()
620 .map(|&val_idx| old_unique_to_new[usize::from(val_idx)].into())
621 .collect();
622 self.point_to_att_val_map = Some(VecPointIdx::from(new_map));
623
624 self.buffer.retain_indices(&keep_unique_indices);
626 } else {
627 self.buffer.retain_indices(keep_point_indices);
629 }
630 }
631}
632
633#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)]
635pub enum ComponentDataType {
636 I8,
638 U8,
640 I16,
642 U16,
644 I32,
646 U32,
648 I64,
650 U64,
652 F32,
654 F64,
656 Invalid,
658}
659
660impl ComponentDataType {
661 #[inline]
663 pub fn size(self) -> usize {
664 match self {
665 ComponentDataType::F32 => 4,
666 ComponentDataType::F64 => 8,
667 ComponentDataType::U8 => 1,
668 ComponentDataType::U16 => 2,
669 ComponentDataType::U32 => 4,
670 ComponentDataType::U64 => 8,
671 ComponentDataType::I8 => 1,
672 ComponentDataType::I16 => 2,
673 ComponentDataType::I32 => 4,
674 ComponentDataType::I64 => 8,
675 ComponentDataType::Invalid => 0,
676 }
677 }
678
679 #[inline]
680 pub fn is_float(self) -> bool {
681 matches!(self, ComponentDataType::F32 | ComponentDataType::F64)
682 }
683
684 #[inline]
686 pub fn get_id(self) -> u8 {
687 match self {
688 ComponentDataType::I8 => 1,
689 ComponentDataType::U8 => 2,
690 ComponentDataType::I16 => 3,
691 ComponentDataType::U16 => 4,
692 ComponentDataType::I32 => 5,
693 ComponentDataType::U32 => 6,
694 ComponentDataType::I64 => 7,
695 ComponentDataType::U64 => 8,
696 ComponentDataType::F32 => 9,
697 ComponentDataType::F64 => 10,
698 ComponentDataType::Invalid => u8::MAX, }
700 }
701
702 pub fn is_integer(self) -> bool {
704 matches!(
705 self,
706 ComponentDataType::I8
707 | ComponentDataType::U8
708 | ComponentDataType::I16
709 | ComponentDataType::U16
710 | ComponentDataType::I32
711 | ComponentDataType::U32
712 | ComponentDataType::I64
713 | ComponentDataType::U64
714 )
715 }
716
717 #[inline]
719 pub fn write_to<W: ByteWriter>(self, writer: &mut W) {
720 writer.write_u8(self.get_id());
721 }
722
723 #[inline]
725 pub fn from_id(id: usize) -> Option<Self> {
726 match id {
727 1 => Some(ComponentDataType::I8),
728 2 => Some(ComponentDataType::U8),
729 3 => Some(ComponentDataType::I16),
730 4 => Some(ComponentDataType::U16),
731 5 => Some(ComponentDataType::I32),
732 6 => Some(ComponentDataType::U32),
733 7 => Some(ComponentDataType::I64),
734 8 => Some(ComponentDataType::U64),
735 9 => Some(ComponentDataType::F32),
736 10 => Some(ComponentDataType::F64),
737 _ => None,
738 }
739 }
740
741 #[inline]
743 pub fn read_from(reader: &mut Reader<'_>) -> Result<Self, Err> {
744 let id = reader.read_u8()?;
745 Self::from_id(id as usize).ok_or(Err::InvalidDataTypeId(id))
746 }
747}
748
749#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
751pub enum AttributeType {
752 Position,
754 Normal,
756 Color,
758 TextureCoordinate,
760 Custom,
762 Tangent,
764 Material,
766 Joint,
768 Weight,
770 Invalid,
772}
773
774impl AttributeType {
775 pub fn get_minimum_dependency(&self) -> Vec<Self> {
777 match self {
778 Self::Position => Vec::new(),
779 Self::Normal => Vec::new(),
780 Self::Color => Vec::new(),
781 Self::TextureCoordinate => vec![Self::Position],
782 Self::Tangent => Vec::new(),
783 Self::Material => Vec::new(),
784 Self::Joint => Vec::new(),
785 Self::Weight => Vec::new(),
786 Self::Custom => Vec::new(),
787 Self::Invalid => Vec::new(),
788 }
789 }
790
791 #[inline]
793 pub fn get_id(&self) -> u8 {
794 match self {
795 Self::Position => 0,
796 Self::Normal => 1,
797 Self::Color => 2,
798 Self::TextureCoordinate => 3,
799 Self::Custom => 4,
800 Self::Tangent => 5,
801 Self::Material => 6,
802 Self::Joint => 7,
803 Self::Weight => 8,
804 Self::Invalid => u8::MAX, }
806 }
807
808 #[inline]
813 pub fn wire_type(&self) -> AttributeType {
814 match self {
815 Self::Tangent | Self::Material | Self::Joint | Self::Weight => Self::Custom,
816 other => *other,
817 }
818 }
819
820 #[inline]
822 pub fn write_to<W: ByteWriter>(&self, writer: &mut W) {
823 writer.write_u8(self.wire_type().get_id());
824 }
825
826 #[inline]
828 pub fn from_id(id: u8) -> Result<Self, Err> {
829 match id {
830 0 => Ok(Self::Position),
831 1 => Ok(Self::Normal),
832 2 => Ok(Self::Color),
833 3 => Ok(Self::TextureCoordinate),
834 4 => Ok(Self::Custom),
835 5 => Ok(Self::Tangent),
836 6 => Ok(Self::Material),
837 7 => Ok(Self::Joint),
838 8 => Ok(Self::Weight),
839 _ => Err(Err::InvalidDataTypeId(id)),
840 }
841 }
842
843 #[inline]
845 pub fn read_from(reader: &mut Reader<'_>) -> Result<Self, Err> {
846 let id = reader.read_u8()?;
847 Self::from_id(id)
848 }
849}
850
851#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
853pub enum AttributeDomain {
854 Position,
856 Corner,
858}
859
860impl AttributeDomain {
861 pub fn write_to<W: ByteWriter>(&self, writer: &mut W) {
863 match self {
864 Self::Position => writer.write_u8(0),
865 Self::Corner => writer.write_u8(1),
866 }
867 }
868
869 pub fn read_from(reader: &mut Reader<'_>) -> Result<Self, Err> {
871 let id = reader.read_u8()?;
872 match id {
873 0 => Ok(Self::Position),
874 1 => Ok(Self::Corner),
875 _ => Err(Err::InvalidAttributeDomainId(id)),
876 }
877 }
878}
879
880#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)]
882pub struct AttributeId(usize);
883
884impl AttributeId {
885 pub fn new(id: usize) -> Self {
887 Self(id)
888 }
889
890 pub fn as_usize(&self) -> usize {
892 self.0
893 }
894}
895
896#[cfg(test)]
897mod tests {
898 use super::*;
899 use crate::types::NdVector;
900
901 #[test]
902 fn test_attribute() {
903 let data = vec![
904 NdVector::from([1.0f32, 2.0, 3.0]),
905 NdVector::from([4.0f32, 5.0, 6.0]),
906 NdVector::from([7.0f32, 8.0, 9.0]),
907 ];
908 let att = super::Attribute::from(
909 AttributeId::new(0),
910 data.clone(),
911 super::AttributeType::Position,
912 super::AttributeDomain::Position,
913 Vec::new(),
914 );
915 assert_eq!(att.len(), data.len());
916 assert_eq!(
917 att.get::<NdVector<3, f32>, 3>(0.into()),
918 data[0],
919 "{:b}!={:b}",
920 att.get::<NdVector<3, f32>, 3>(0.into()).get(0).to_bits(),
921 data[0].get(0).to_bits()
922 );
923 assert_eq!(att.get_component_type(), super::ComponentDataType::F32);
924 assert_eq!(att.get_num_components(), 3);
925 assert_eq!(att.get_attribute_type(), super::AttributeType::Position);
926 }
927
928 #[test]
929 fn test_attribute_remap() {
930 let positions = vec![
931 NdVector::from([0.0f32, 0.0, 0.0]), NdVector::from([1.0f32, 0.0, 0.0]), NdVector::from([0.5f32, 1.0, 0.0]), NdVector::from([0.0f32, 0.0, 0.0]), NdVector::from([1.0f32, 0.0, 0.0]), NdVector::from([2.0f32, 0.0, 0.0]), ];
938
939 let att = Attribute::new(
940 positions,
941 AttributeType::Position,
942 AttributeDomain::Position,
943 vec![],
944 );
945
946 assert_eq!(
947 att.point_to_att_val_map
948 .unwrap()
949 .into_iter()
950 .map(usize::from)
951 .collect::<Vec<_>>(),
952 vec![0, 1, 2, 0, 1, 3],
953 )
954 }
955
956 #[test]
958 fn transcoder_only_attribute_types_downgrade_on_the_wire() {
959 for ty in [
960 AttributeType::Tangent,
961 AttributeType::Material,
962 AttributeType::Joint,
963 AttributeType::Weight,
964 ] {
965 let mut buf = Vec::new();
966 ty.write_to(&mut buf);
967 assert_eq!(buf, vec![AttributeType::Custom.get_id()]);
968 }
969 }
970
971 #[test]
974 fn component_type_ids_match_reference() {
975 let expected = [
976 (ComponentDataType::I8, 1),
977 (ComponentDataType::U8, 2),
978 (ComponentDataType::I16, 3),
979 (ComponentDataType::U16, 4),
980 (ComponentDataType::I32, 5),
981 (ComponentDataType::U32, 6),
982 (ComponentDataType::I64, 7),
983 (ComponentDataType::U64, 8),
984 (ComponentDataType::F32, 9),
985 (ComponentDataType::F64, 10),
986 ];
987 for (ty, id) in expected {
988 assert_eq!(ty.get_id(), id, "{ty:?}");
989 assert_eq!(ComponentDataType::from_id(id as usize), Some(ty));
990 }
991 }
992}