1use std::collections::HashMap;
2
3use crate::geometry_attribute::{GeometryAttributeType, PointAttribute};
4use crate::geometry_indices::{AttributeValueIndex, PointIndex};
5use crate::metadata::{AttributeMetadata, GeometryMetadata, Metadata};
6use crate::status::{DracoError, Status};
7
8#[derive(Debug, Default, Clone)]
10pub struct PointCloud {
11 attributes: Vec<PointAttribute>,
12 num_points: usize,
13 metadata: Option<GeometryMetadata>,
14 spare_storage: Vec<(Vec<u8>, Vec<AttributeValueIndex>)>,
19}
20
21impl PointCloud {
22 pub fn new() -> Self {
24 Self::default()
25 }
26
27 pub fn clear(&mut self) {
38 for mut attribute in self.attributes.drain(..) {
39 let storage = attribute.take_storage();
40 if storage.0.capacity() > 0 || storage.1.capacity() > 0 {
41 self.spare_storage.push(storage);
42 }
43 }
44 self.num_points = 0;
45 self.metadata = None;
46 }
47
48 pub fn release_spare_storage(&mut self) {
51 self.spare_storage = Vec::new();
52 }
53
54 fn adopt_spare_storage(&mut self, attribute: &mut PointAttribute) {
56 if attribute.buffer().has_storage() {
57 return;
58 }
59 if let Some(storage) = self.spare_storage.pop() {
60 attribute.adopt_storage(storage);
61 }
62 }
63
64 pub fn set_num_points(&mut self, num_points: usize) {
66 self.num_points = num_points;
67 }
68
69 pub fn add_attribute(&mut self, mut attribute: PointAttribute) -> i32 {
71 if self.num_points == 0 && attribute.size() > 0 {
72 self.num_points = attribute.size();
73 }
74 let id = self.attributes.len() as i32;
75 attribute.set_unique_id(id as u32);
76 self.adopt_spare_storage(&mut attribute);
77 self.attributes.push(attribute);
78 id
79 }
80
81 pub fn add_attribute_preserve_unique_id(&mut self, mut attribute: PointAttribute) -> i32 {
83 if self.num_points == 0 && attribute.size() > 0 {
84 self.num_points = attribute.size();
85 }
86 let id = self.attributes.len() as i32;
87 self.adopt_spare_storage(&mut attribute);
88 self.attributes.push(attribute);
89 id
90 }
91
92 pub fn set_attribute(&mut self, att_id: i32, mut attribute: PointAttribute) {
98 debug_assert!(att_id >= 0);
99 let index = att_id as usize;
100 if index >= self.attributes.len() {
101 self.attributes.resize_with(index + 1, PointAttribute::new);
102 }
103 attribute.set_unique_id(att_id as u32);
104 self.attributes[index] = attribute;
105 }
106
107 pub fn num_attributes(&self) -> i32 {
109 self.attributes.len() as i32
110 }
111
112 pub fn attribute_id_by_unique_id(&self, unique_id: u32) -> i32 {
116 for (i, att) in self.attributes.iter().enumerate() {
117 if att.unique_id() == unique_id {
118 return i as i32;
119 }
120 }
121 -1
122 }
123
124 pub fn attribute_by_unique_id(&self, unique_id: u32) -> Option<&PointAttribute> {
128 let id = self.attribute_id_by_unique_id(unique_id);
129 (id >= 0).then(|| &self.attributes[id as usize])
130 }
131
132 pub fn attribute(&self, att_id: i32) -> &PointAttribute {
134 &self.attributes[att_id as usize]
135 }
136
137 pub fn try_attribute(&self, att_id: i32) -> Result<&PointAttribute, DracoError> {
139 let Some(attribute) = (att_id >= 0)
140 .then_some(att_id as usize)
141 .and_then(|index| self.attributes.get(index))
142 else {
143 return Err(DracoError::general(
144 "Point cloud attribute id out of range".to_string(),
145 ));
146 };
147 Ok(attribute)
148 }
149
150 pub fn attribute_mut(&mut self, att_id: i32) -> &mut PointAttribute {
152 &mut self.attributes[att_id as usize]
153 }
154
155 pub fn try_attribute_mut(&mut self, att_id: i32) -> Result<&mut PointAttribute, DracoError> {
157 let Some(attribute) = (att_id >= 0)
158 .then_some(att_id as usize)
159 .and_then(|index| self.attributes.get_mut(index))
160 else {
161 return Err(DracoError::general(
162 "Point cloud attribute id out of range".to_string(),
163 ));
164 };
165 Ok(attribute)
166 }
167
168 pub fn named_attribute_id(&self, att_type: GeometryAttributeType) -> i32 {
170 for (i, att) in self.attributes.iter().enumerate() {
171 if att.attribute_type() == att_type {
172 return i as i32;
173 }
174 }
175 -1
176 }
177
178 pub fn named_attribute(&self, att_type: GeometryAttributeType) -> Option<&PointAttribute> {
180 let id = self.named_attribute_id(att_type);
181 if id >= 0 {
182 Some(&self.attributes[id as usize])
183 } else {
184 None
185 }
186 }
187
188 pub fn deduplicate_attribute_values(&mut self) -> Status {
196 if self.num_points() == 0 {
197 return Ok(());
198 }
199 for att_id in 0..self.num_attributes() {
200 self.attribute_mut(att_id).deduplicate_values()?;
201 }
202 Ok(())
203 }
204
205 pub fn deduplicate_point_ids(&mut self) {
214 self.deduplicate_point_ids_returning_map();
215 }
216
217 pub(crate) fn deduplicate_point_ids_returning_map(&mut self) -> Option<Vec<u32>> {
223 let num_points = self.num_points();
224 if num_points == 0 || self.num_attributes() == 0 {
225 return None;
226 }
227
228 let key_of = |pc: &Self, point: usize| -> Vec<u32> {
229 (0..pc.num_attributes())
230 .map(|att_id| {
231 pc.attribute(att_id)
232 .mapped_index(PointIndex(point as u32))
233 .0
234 })
235 .collect()
236 };
237
238 let mut first_seen: HashMap<Vec<u32>, u32> = HashMap::with_capacity(num_points);
239 let mut index_map: Vec<u32> = Vec::with_capacity(num_points);
240 let mut unique_points: Vec<u32> = Vec::new();
241 let mut num_unique = 0u32;
242 for point in 0..num_points {
243 match first_seen.entry(key_of(self, point)) {
244 std::collections::hash_map::Entry::Occupied(entry) => {
245 index_map.push(*entry.get());
246 }
247 std::collections::hash_map::Entry::Vacant(entry) => {
248 entry.insert(num_unique);
249 index_map.push(num_unique);
250 unique_points.push(point as u32);
251 num_unique += 1;
252 }
253 }
254 }
255 if num_unique as usize == num_points {
256 return None;
257 }
258
259 for att_id in 0..self.num_attributes() {
267 let values: Vec<AttributeValueIndex> = unique_points
268 .iter()
269 .map(|old| self.attribute(att_id).mapped_index(PointIndex(*old)))
270 .collect();
271 self.attribute_mut(att_id)
272 .set_explicit_mapping_from(&values);
273 }
274 self.set_num_points(num_unique as usize);
275 Some(index_map)
276 }
277
278 pub fn num_points(&self) -> usize {
279 self.num_points
280 }
281
282 pub fn metadata(&self) -> Option<&GeometryMetadata> {
284 self.metadata.as_ref()
285 }
286
287 pub fn metadata_mut(&mut self) -> Option<&mut GeometryMetadata> {
289 self.metadata.as_mut()
290 }
291
292 pub fn metadata_or_insert(&mut self) -> &mut GeometryMetadata {
294 self.metadata.get_or_insert_with(GeometryMetadata::new)
295 }
296
297 pub fn set_metadata(&mut self, metadata: Option<GeometryMetadata>) {
299 self.metadata = metadata;
300 }
301
302 pub fn attribute_metadata_by_unique_id(
304 &self,
305 attribute_unique_id: u32,
306 ) -> Option<&AttributeMetadata> {
307 self.metadata
308 .as_ref()
309 .and_then(|metadata| metadata.attribute_metadata_by_unique_id(attribute_unique_id))
310 }
311
312 pub fn attribute_metadata_by_string_entry(
314 &self,
315 entry_name: &str,
316 entry_value: &str,
317 ) -> Option<&AttributeMetadata> {
318 self.metadata.as_ref().and_then(|metadata| {
319 metadata.attribute_metadata_by_string_entry(entry_name, entry_value)
320 })
321 }
322
323 pub fn set_attribute_metadata(
325 &mut self,
326 att_id: i32,
327 metadata: Metadata,
328 ) -> Result<(), DracoError> {
329 let unique_id = self.try_attribute(att_id)?.unique_id();
330 self.metadata_or_insert()
331 .set_attribute_metadata(unique_id, metadata);
332 Ok(())
333 }
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339 use crate::draco_types::DataType;
340 use crate::geometry_indices::INVALID_ATTRIBUTE_VALUE_INDEX;
341
342 fn attribute_with_values(num_values: usize, fill: u8) -> PointAttribute {
343 let mut attribute = PointAttribute::new();
344 attribute.init(
345 GeometryAttributeType::Position,
346 3,
347 DataType::Float32,
348 false,
349 num_values,
350 );
351 attribute.buffer_mut().data_mut().fill(fill);
352 attribute.set_explicit_mapping(num_values);
353 attribute
354 }
355
356 #[test]
361 fn clear_keeps_attribute_storage_for_the_next_attributes_and_hands_it_over_empty() {
362 let mut point_cloud = PointCloud::new();
363 point_cloud.add_attribute(attribute_with_values(100, 0xAB));
364 point_cloud.clear();
365 assert_eq!(point_cloud.num_attributes(), 0);
366 assert_eq!(point_cloud.spare_storage.len(), 1);
367
368 let mut next = PointAttribute::new();
369 next.init_deferred(
370 GeometryAttributeType::Position,
371 3,
372 DataType::Float32,
373 false,
374 10,
375 )
376 .unwrap();
377 let id = point_cloud.add_attribute(next);
378 assert!(point_cloud.spare_storage.is_empty());
379 let next = point_cloud.attribute_mut(id);
380 assert!(next.buffer().has_storage(), "the storage was handed over");
381 assert_eq!(next.buffer().data_size(), 0);
382 next.resize_unique_entries(10).unwrap();
383 assert!(next.buffer().data().iter().all(|&b| b == 0));
384 next.set_explicit_mapping(10);
385 assert!((0..10).all(|p| next.mapped_index(PointIndex(p)) == INVALID_ATTRIBUTE_VALUE_INDEX));
386 }
387
388 #[test]
391 fn an_attribute_with_its_own_storage_does_not_take_a_spare() {
392 let mut point_cloud = PointCloud::new();
393 point_cloud.add_attribute(attribute_with_values(100, 0xAB));
394 point_cloud.clear();
395 point_cloud.add_attribute(attribute_with_values(5, 0xCD));
396 assert_eq!(point_cloud.spare_storage.len(), 1);
397 assert!(point_cloud
398 .attribute(0)
399 .buffer()
400 .data()
401 .iter()
402 .all(|&b| b == 0xCD));
403 }
404
405 #[test]
406 fn release_spare_storage_drops_what_clear_kept() {
407 let mut point_cloud = PointCloud::new();
408 point_cloud.add_attribute(attribute_with_values(100, 0xAB));
409 point_cloud.clear();
410 point_cloud.release_spare_storage();
411 assert!(point_cloud.spare_storage.is_empty());
412 }
413
414 #[test]
415 fn try_attribute_rejects_out_of_range_ids() {
416 let mut point_cloud = PointCloud::new();
417
418 assert!(point_cloud.try_attribute(-1).is_err());
419 assert!(point_cloud.try_attribute(0).is_err());
420 assert!(point_cloud.try_attribute_mut(-1).is_err());
421 assert!(point_cloud.try_attribute_mut(0).is_err());
422 }
423
424 #[test]
427 fn identical_values_merge_and_the_mapping_turns_explicit() {
428 use crate::draco_types::DataType;
429
430 let positions: [f32; 12] = [
431 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0,
435 ];
436 let mut attribute = PointAttribute::new();
437 attribute.init(
438 GeometryAttributeType::Position,
439 3,
440 DataType::Float32,
441 false,
442 4,
443 );
444 let bytes: Vec<u8> = positions.iter().flat_map(|v| v.to_le_bytes()).collect();
445 attribute.buffer_mut().data_mut().copy_from_slice(&bytes);
446 attribute.set_identity_mapping();
447
448 let mut point_cloud = PointCloud::new();
449 point_cloud.set_num_points(4);
450 point_cloud.add_attribute(attribute);
451 point_cloud
452 .deduplicate_attribute_values()
453 .expect("supported");
454
455 let attribute = point_cloud.attribute(0);
456 assert_eq!(attribute.size(), 3, "the duplicate value survived");
457 assert!(!attribute.is_mapping_identity());
458 assert_eq!(
459 attribute.mapped_index(PointIndex(1)),
460 AttributeValueIndex(1)
461 );
462 assert_eq!(
463 attribute.mapped_index(PointIndex(2)),
464 AttributeValueIndex(1),
465 "the duplicate does not point at the value that replaced it"
466 );
467 assert_eq!(
468 attribute.mapped_index(PointIndex(3)),
469 AttributeValueIndex(2)
470 );
471 }
472
473 #[test]
476 fn points_naming_the_same_values_merge_in_arrival_order() {
477 use crate::draco_types::DataType;
478
479 let mut attribute = PointAttribute::new();
480 attribute.init(
481 GeometryAttributeType::Position,
482 3,
483 DataType::Float32,
484 false,
485 2,
486 );
487 let bytes: Vec<u8> = [0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0]
488 .iter()
489 .flat_map(|v| v.to_le_bytes())
490 .collect();
491 attribute.buffer_mut().data_mut().copy_from_slice(&bytes);
492 attribute.set_explicit_mapping_from(&[
494 AttributeValueIndex(0),
495 AttributeValueIndex(1),
496 AttributeValueIndex(1),
497 AttributeValueIndex(0),
498 ]);
499
500 let mut point_cloud = PointCloud::new();
501 point_cloud.set_num_points(4);
502 point_cloud.add_attribute(attribute);
503 point_cloud.deduplicate_point_ids();
504
505 assert_eq!(point_cloud.num_points(), 2);
506 let attribute = point_cloud.attribute(0);
507 assert_eq!(
508 attribute.mapped_index(PointIndex(0)),
509 AttributeValueIndex(0)
510 );
511 assert_eq!(
512 attribute.mapped_index(PointIndex(1)),
513 AttributeValueIndex(1)
514 );
515 }
516
517 #[test]
521 fn wide_values_are_compared_over_their_whole_width() {
522 use crate::draco_types::DataType;
523
524 let mut attribute = PointAttribute::new();
525 attribute.init(
526 GeometryAttributeType::Generic,
527 4,
528 DataType::Float64,
529 false,
530 3,
531 );
532 let bytes: Vec<u8> = [
533 [1.0f64, 2.0, 3.0, 4.0],
534 [1.0, 2.0, 3.0, 5.0],
535 [1.0, 2.0, 3.0, 4.0],
536 ]
537 .iter()
538 .flatten()
539 .flat_map(|v| v.to_le_bytes())
540 .collect();
541 attribute.buffer_mut().data_mut().copy_from_slice(&bytes);
542 attribute.set_identity_mapping();
543
544 let mut point_cloud = PointCloud::new();
545 point_cloud.set_num_points(3);
546 point_cloud.add_attribute(attribute);
547 point_cloud.deduplicate_attribute_values().unwrap();
548
549 let attribute = point_cloud.attribute(0);
550 assert_eq!(attribute.size(), 2);
551 assert_eq!(attribute.buffer().data(), &bytes[..64]);
552 let mapped: Vec<u32> = (0..3)
553 .map(|point| attribute.mapped_index(PointIndex(point)).0)
554 .collect();
555 assert_eq!(mapped, [0, 1, 0]);
556 }
557
558 #[test]
561 fn a_component_count_upstream_does_not_deduplicate_is_refused() {
562 use crate::draco_types::DataType;
563
564 let mut attribute = PointAttribute::new();
565 attribute.init(
566 GeometryAttributeType::Generic,
567 5,
568 DataType::Float32,
569 false,
570 2,
571 );
572 attribute.set_identity_mapping();
573
574 let mut point_cloud = PointCloud::new();
575 point_cloud.set_num_points(2);
576 point_cloud.add_attribute(attribute);
577 assert!(point_cloud.deduplicate_attribute_values().is_err());
578 }
579}