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 #[cfg(feature = "decoder")]
341 use crate::geometry_indices::INVALID_ATTRIBUTE_VALUE_INDEX;
342
343 fn attribute_with_values(num_values: usize, fill: u8) -> PointAttribute {
344 let mut attribute = PointAttribute::new();
345 attribute.init(
346 GeometryAttributeType::Position,
347 3,
348 DataType::Float32,
349 false,
350 num_values,
351 );
352 attribute.buffer_mut().data_mut().fill(fill);
353 attribute.set_explicit_mapping(num_values);
354 attribute
355 }
356
357 #[cfg(feature = "decoder")]
358 #[test]
363 fn clear_keeps_attribute_storage_for_the_next_attributes_and_hands_it_over_empty() {
364 let mut point_cloud = PointCloud::new();
365 point_cloud.add_attribute(attribute_with_values(100, 0xAB));
366 point_cloud.clear();
367 assert_eq!(point_cloud.num_attributes(), 0);
368 assert_eq!(point_cloud.spare_storage.len(), 1);
369
370 let mut next = PointAttribute::new();
371 next.init_deferred(
372 GeometryAttributeType::Position,
373 3,
374 DataType::Float32,
375 false,
376 10,
377 )
378 .unwrap();
379 let id = point_cloud.add_attribute(next);
380 assert!(point_cloud.spare_storage.is_empty());
381 let next = point_cloud.attribute_mut(id);
382 assert!(next.buffer().has_storage(), "the storage was handed over");
383 assert_eq!(next.buffer().data_size(), 0);
384 next.resize_unique_entries(10).unwrap();
385 assert!(next.buffer().data().iter().all(|&b| b == 0));
386 next.set_explicit_mapping(10);
387 assert!((0..10).all(|p| next.mapped_index(PointIndex(p)) == INVALID_ATTRIBUTE_VALUE_INDEX));
388 }
389
390 #[test]
393 fn an_attribute_with_its_own_storage_does_not_take_a_spare() {
394 let mut point_cloud = PointCloud::new();
395 point_cloud.add_attribute(attribute_with_values(100, 0xAB));
396 point_cloud.clear();
397 point_cloud.add_attribute(attribute_with_values(5, 0xCD));
398 assert_eq!(point_cloud.spare_storage.len(), 1);
399 assert!(point_cloud
400 .attribute(0)
401 .buffer()
402 .data()
403 .iter()
404 .all(|&b| b == 0xCD));
405 }
406
407 #[test]
408 fn release_spare_storage_drops_what_clear_kept() {
409 let mut point_cloud = PointCloud::new();
410 point_cloud.add_attribute(attribute_with_values(100, 0xAB));
411 point_cloud.clear();
412 point_cloud.release_spare_storage();
413 assert!(point_cloud.spare_storage.is_empty());
414 }
415
416 #[test]
417 fn try_attribute_rejects_out_of_range_ids() {
418 let mut point_cloud = PointCloud::new();
419
420 assert!(point_cloud.try_attribute(-1).is_err());
421 assert!(point_cloud.try_attribute(0).is_err());
422 assert!(point_cloud.try_attribute_mut(-1).is_err());
423 assert!(point_cloud.try_attribute_mut(0).is_err());
424 }
425
426 #[test]
429 fn identical_values_merge_and_the_mapping_turns_explicit() {
430 use crate::draco_types::DataType;
431
432 let positions: [f32; 12] = [
433 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0,
437 ];
438 let mut attribute = PointAttribute::new();
439 attribute.init(
440 GeometryAttributeType::Position,
441 3,
442 DataType::Float32,
443 false,
444 4,
445 );
446 let bytes: Vec<u8> = positions.iter().flat_map(|v| v.to_le_bytes()).collect();
447 attribute.buffer_mut().data_mut().copy_from_slice(&bytes);
448 attribute.set_identity_mapping();
449
450 let mut point_cloud = PointCloud::new();
451 point_cloud.set_num_points(4);
452 point_cloud.add_attribute(attribute);
453 point_cloud
454 .deduplicate_attribute_values()
455 .expect("supported");
456
457 let attribute = point_cloud.attribute(0);
458 assert_eq!(attribute.size(), 3, "the duplicate value survived");
459 assert!(!attribute.is_mapping_identity());
460 assert_eq!(
461 attribute.mapped_index(PointIndex(1)),
462 AttributeValueIndex(1)
463 );
464 assert_eq!(
465 attribute.mapped_index(PointIndex(2)),
466 AttributeValueIndex(1),
467 "the duplicate does not point at the value that replaced it"
468 );
469 assert_eq!(
470 attribute.mapped_index(PointIndex(3)),
471 AttributeValueIndex(2)
472 );
473 }
474
475 #[test]
478 fn points_naming_the_same_values_merge_in_arrival_order() {
479 use crate::draco_types::DataType;
480
481 let mut attribute = PointAttribute::new();
482 attribute.init(
483 GeometryAttributeType::Position,
484 3,
485 DataType::Float32,
486 false,
487 2,
488 );
489 let bytes: Vec<u8> = [0.0f32, 0.0, 0.0, 1.0, 0.0, 0.0]
490 .iter()
491 .flat_map(|v| v.to_le_bytes())
492 .collect();
493 attribute.buffer_mut().data_mut().copy_from_slice(&bytes);
494 attribute.set_explicit_mapping_from(&[
496 AttributeValueIndex(0),
497 AttributeValueIndex(1),
498 AttributeValueIndex(1),
499 AttributeValueIndex(0),
500 ]);
501
502 let mut point_cloud = PointCloud::new();
503 point_cloud.set_num_points(4);
504 point_cloud.add_attribute(attribute);
505 point_cloud.deduplicate_point_ids();
506
507 assert_eq!(point_cloud.num_points(), 2);
508 let attribute = point_cloud.attribute(0);
509 assert_eq!(
510 attribute.mapped_index(PointIndex(0)),
511 AttributeValueIndex(0)
512 );
513 assert_eq!(
514 attribute.mapped_index(PointIndex(1)),
515 AttributeValueIndex(1)
516 );
517 }
518
519 #[test]
523 fn wide_values_are_compared_over_their_whole_width() {
524 use crate::draco_types::DataType;
525
526 let mut attribute = PointAttribute::new();
527 attribute.init(
528 GeometryAttributeType::Generic,
529 4,
530 DataType::Float64,
531 false,
532 3,
533 );
534 let bytes: Vec<u8> = [
535 [1.0f64, 2.0, 3.0, 4.0],
536 [1.0, 2.0, 3.0, 5.0],
537 [1.0, 2.0, 3.0, 4.0],
538 ]
539 .iter()
540 .flatten()
541 .flat_map(|v| v.to_le_bytes())
542 .collect();
543 attribute.buffer_mut().data_mut().copy_from_slice(&bytes);
544 attribute.set_identity_mapping();
545
546 let mut point_cloud = PointCloud::new();
547 point_cloud.set_num_points(3);
548 point_cloud.add_attribute(attribute);
549 point_cloud.deduplicate_attribute_values().unwrap();
550
551 let attribute = point_cloud.attribute(0);
552 assert_eq!(attribute.size(), 2);
553 assert_eq!(attribute.buffer().data(), &bytes[..64]);
554 let mapped: Vec<u32> = (0..3)
555 .map(|point| attribute.mapped_index(PointIndex(point)).0)
556 .collect();
557 assert_eq!(mapped, [0, 1, 0]);
558 }
559
560 #[test]
563 fn a_component_count_upstream_does_not_deduplicate_is_refused() {
564 use crate::draco_types::DataType;
565
566 let mut attribute = PointAttribute::new();
567 attribute.init(
568 GeometryAttributeType::Generic,
569 5,
570 DataType::Float32,
571 false,
572 2,
573 );
574 attribute.set_identity_mapping();
575
576 let mut point_cloud = PointCloud::new();
577 point_cloud.set_num_points(2);
578 point_cloud.add_attribute(attribute);
579 assert!(point_cloud.deduplicate_attribute_values().is_err());
580 }
581}