1 // Copyright 2016 The Draco Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 #include "draco/point_cloud/point_cloud.h"
16 
17 #include <algorithm>
18 #include <unordered_map>
19 
20 namespace draco {
21 
PointCloud()22 PointCloud::PointCloud() : num_points_(0) {}
23 
NumNamedAttributes(GeometryAttribute::Type type) const24 int32_t PointCloud::NumNamedAttributes(GeometryAttribute::Type type) const {
25   if (type == GeometryAttribute::INVALID ||
26       type >= GeometryAttribute::NAMED_ATTRIBUTES_COUNT) {
27     return 0;
28   }
29   return static_cast<int32_t>(named_attribute_index_[type].size());
30 }
31 
GetNamedAttributeId(GeometryAttribute::Type type) const32 int32_t PointCloud::GetNamedAttributeId(GeometryAttribute::Type type) const {
33   return GetNamedAttributeId(type, 0);
34 }
35 
GetNamedAttributeId(GeometryAttribute::Type type,int i) const36 int32_t PointCloud::GetNamedAttributeId(GeometryAttribute::Type type,
37                                         int i) const {
38   if (NumNamedAttributes(type) <= i) {
39     return -1;
40   }
41   return named_attribute_index_[type][i];
42 }
43 
GetNamedAttribute(GeometryAttribute::Type type) const44 const PointAttribute *PointCloud::GetNamedAttribute(
45     GeometryAttribute::Type type) const {
46   return GetNamedAttribute(type, 0);
47 }
48 
GetNamedAttribute(GeometryAttribute::Type type,int i) const49 const PointAttribute *PointCloud::GetNamedAttribute(
50     GeometryAttribute::Type type, int i) const {
51   const int32_t att_id = GetNamedAttributeId(type, i);
52   if (att_id == -1) {
53     return nullptr;
54   }
55   return attributes_[att_id].get();
56 }
57 
GetNamedAttributeByUniqueId(GeometryAttribute::Type type,uint32_t unique_id) const58 const PointAttribute *PointCloud::GetNamedAttributeByUniqueId(
59     GeometryAttribute::Type type, uint32_t unique_id) const {
60   for (size_t att_id = 0; att_id < named_attribute_index_[type].size();
61        ++att_id) {
62     if (attributes_[named_attribute_index_[type][att_id]]->unique_id() ==
63         unique_id) {
64       return attributes_[named_attribute_index_[type][att_id]].get();
65     }
66   }
67   return nullptr;
68 }
69 
GetAttributeByUniqueId(uint32_t unique_id) const70 const PointAttribute *PointCloud::GetAttributeByUniqueId(
71     uint32_t unique_id) const {
72   const int32_t att_id = GetAttributeIdByUniqueId(unique_id);
73   if (att_id == -1) {
74     return nullptr;
75   }
76   return attributes_[att_id].get();
77 }
78 
GetAttributeIdByUniqueId(uint32_t unique_id) const79 int32_t PointCloud::GetAttributeIdByUniqueId(uint32_t unique_id) const {
80   for (size_t att_id = 0; att_id < attributes_.size(); ++att_id) {
81     if (attributes_[att_id]->unique_id() == unique_id) {
82       return static_cast<int32_t>(att_id);
83     }
84   }
85   return -1;
86 }
87 
AddAttribute(std::unique_ptr<PointAttribute> pa)88 int PointCloud::AddAttribute(std::unique_ptr<PointAttribute> pa) {
89   SetAttribute(static_cast<int>(attributes_.size()), std::move(pa));
90   return static_cast<int>(attributes_.size() - 1);
91 }
92 
AddAttribute(const GeometryAttribute & att,bool identity_mapping,AttributeValueIndex::ValueType num_attribute_values)93 int PointCloud::AddAttribute(
94     const GeometryAttribute &att, bool identity_mapping,
95     AttributeValueIndex::ValueType num_attribute_values) {
96   auto pa = CreateAttribute(att, identity_mapping, num_attribute_values);
97   if (!pa) {
98     return -1;
99   }
100   const int32_t att_id = AddAttribute(std::move(pa));
101   return att_id;
102 }
103 
CreateAttribute(const GeometryAttribute & att,bool identity_mapping,AttributeValueIndex::ValueType num_attribute_values) const104 std::unique_ptr<PointAttribute> PointCloud::CreateAttribute(
105     const GeometryAttribute &att, bool identity_mapping,
106     AttributeValueIndex::ValueType num_attribute_values) const {
107   if (att.attribute_type() == GeometryAttribute::INVALID) {
108     return nullptr;
109   }
110   std::unique_ptr<PointAttribute> pa =
111       std::unique_ptr<PointAttribute>(new PointAttribute(att));
112   // Initialize point cloud specific attribute data.
113   if (!identity_mapping) {
114     // First create mapping between indices.
115     pa->SetExplicitMapping(num_points_);
116   } else {
117     pa->SetIdentityMapping();
118     num_attribute_values = std::max(num_points_, num_attribute_values);
119   }
120   if (num_attribute_values > 0) {
121     pa->Reset(num_attribute_values);
122   }
123   return pa;
124 }
125 
SetAttribute(int att_id,std::unique_ptr<PointAttribute> pa)126 void PointCloud::SetAttribute(int att_id, std::unique_ptr<PointAttribute> pa) {
127   DRACO_DCHECK(att_id >= 0);
128   if (static_cast<int>(attributes_.size()) <= att_id) {
129     attributes_.resize(att_id + 1);
130   }
131   if (pa->attribute_type() < GeometryAttribute::NAMED_ATTRIBUTES_COUNT) {
132     named_attribute_index_[pa->attribute_type()].push_back(att_id);
133   }
134   pa->set_unique_id(att_id);
135   attributes_[att_id] = std::move(pa);
136 }
137 
DeleteAttribute(int att_id)138 void PointCloud::DeleteAttribute(int att_id) {
139   if (att_id < 0 || att_id >= attributes_.size()) {
140     return;  // Attribute does not exist.
141   }
142   const GeometryAttribute::Type att_type =
143       attributes_[att_id]->attribute_type();
144   const uint32_t unique_id = attribute(att_id)->unique_id();
145   attributes_.erase(attributes_.begin() + att_id);
146   // Remove metadata if applicable.
147   if (metadata_) {
148     metadata_->DeleteAttributeMetadataByUniqueId(unique_id);
149   }
150 
151   // Remove the attribute from the named attribute list if applicable.
152   if (att_type < GeometryAttribute::NAMED_ATTRIBUTES_COUNT) {
153     const auto it = std::find(named_attribute_index_[att_type].begin(),
154                               named_attribute_index_[att_type].end(), att_id);
155     if (it != named_attribute_index_[att_type].end()) {
156       named_attribute_index_[att_type].erase(it);
157     }
158   }
159 
160   // Update ids of all subsequent named attributes (decrease them by one).
161   for (int i = 0; i < GeometryAttribute::NAMED_ATTRIBUTES_COUNT; ++i) {
162     for (int j = 0; j < named_attribute_index_[i].size(); ++j) {
163       if (named_attribute_index_[i][j] > att_id) {
164         named_attribute_index_[i][j]--;
165       }
166     }
167   }
168 }
169 
170 #ifdef DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED
DeduplicatePointIds()171 void PointCloud::DeduplicatePointIds() {
172   // Hashing function for a single vertex.
173   auto point_hash = [this](PointIndex p) {
174     PointIndex::ValueType hash = 0;
175     for (int32_t i = 0; i < this->num_attributes(); ++i) {
176       const AttributeValueIndex att_id = attribute(i)->mapped_index(p);
177       hash = static_cast<uint32_t>(HashCombine(att_id.value(), hash));
178     }
179     return hash;
180   };
181   // Comparison function between two vertices.
182   auto point_compare = [this](PointIndex p0, PointIndex p1) {
183     for (int32_t i = 0; i < this->num_attributes(); ++i) {
184       const AttributeValueIndex att_id0 = attribute(i)->mapped_index(p0);
185       const AttributeValueIndex att_id1 = attribute(i)->mapped_index(p1);
186       if (att_id0 != att_id1) {
187         return false;
188       }
189     }
190     return true;
191   };
192 
193   std::unordered_map<PointIndex, PointIndex, decltype(point_hash),
194                      decltype(point_compare)>
195       unique_point_map(num_points_, point_hash, point_compare);
196   int32_t num_unique_points = 0;
197   IndexTypeVector<PointIndex, PointIndex> index_map(num_points_);
198   std::vector<PointIndex> unique_points;
199   // Go through all vertices and find their duplicates.
200   for (PointIndex i(0); i < num_points_; ++i) {
201     const auto it = unique_point_map.find(i);
202     if (it != unique_point_map.end()) {
203       index_map[i] = it->second;
204     } else {
205       unique_point_map.insert(std::make_pair(i, PointIndex(num_unique_points)));
206       index_map[i] = num_unique_points++;
207       unique_points.push_back(i);
208     }
209   }
210   if (num_unique_points == num_points_) {
211     return;  // All vertices are already unique.
212   }
213 
214   ApplyPointIdDeduplication(index_map, unique_points);
215   set_num_points(num_unique_points);
216 }
217 
ApplyPointIdDeduplication(const IndexTypeVector<PointIndex,PointIndex> & id_map,const std::vector<PointIndex> & unique_point_ids)218 void PointCloud::ApplyPointIdDeduplication(
219     const IndexTypeVector<PointIndex, PointIndex> &id_map,
220     const std::vector<PointIndex> &unique_point_ids) {
221   int32_t num_unique_points = 0;
222   for (PointIndex i : unique_point_ids) {
223     const PointIndex new_point_id = id_map[i];
224     if (new_point_id >= num_unique_points) {
225       // New unique vertex reached. Copy attribute indices to the proper
226       // position.
227       for (int32_t a = 0; a < num_attributes(); ++a) {
228         attribute(a)->SetPointMapEntry(new_point_id,
229                                        attribute(a)->mapped_index(i));
230       }
231       num_unique_points = new_point_id.value() + 1;
232     }
233   }
234   for (int32_t a = 0; a < num_attributes(); ++a) {
235     attribute(a)->SetExplicitMapping(num_unique_points);
236   }
237 }
238 #endif
239 
240 #ifdef DRACO_ATTRIBUTE_VALUES_DEDUPLICATION_SUPPORTED
DeduplicateAttributeValues()241 bool PointCloud::DeduplicateAttributeValues() {
242   // Go over all attributes and create mapping between duplicate entries.
243   if (num_points() == 0) {
244     return true;  // Nothing to deduplicate.
245   }
246   // Deduplicate all attributes.
247   for (int32_t att_id = 0; att_id < num_attributes(); ++att_id) {
248     if (!attribute(att_id)->DeduplicateValues(*attribute(att_id))) {
249       return false;
250     }
251   }
252   return true;
253 }
254 #endif
255 
256 // TODO(b/199760503): Consider to cache the BBox.
ComputeBoundingBox() const257 BoundingBox PointCloud::ComputeBoundingBox() const {
258   BoundingBox bounding_box;
259   auto pc_att = GetNamedAttribute(GeometryAttribute::POSITION);
260   // TODO(b/199760503): Make the BoundingBox a template type, it may not be easy
261   // because PointCloud is not a template.
262   // Or simply add some preconditioning here to make sure the position attribute
263   // is valid, because the current code works only if the position attribute is
264   // defined with 3 components of DT_FLOAT32.
265   // Consider using pc_att->ConvertValue<float, 3>(i, &p[0]) (Enforced
266   // transformation from Vector with any dimension to Vector3f)
267   Vector3f p;
268   for (AttributeValueIndex i(0); i < static_cast<uint32_t>(pc_att->size());
269        ++i) {
270     pc_att->GetValue(i, &p[0]);
271     bounding_box.Update(p);
272   }
273   return bounding_box;
274 }
275 }  // namespace draco
276