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