Apollo 10.0
自动驾驶开放平台
aaboxkdtree2d.h
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2017 The Apollo Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
16
22#pragma once
23
24#include <algorithm>
25#include <limits>
26#include <memory>
27#include <vector>
28
29#include "cyber/common/log.h"
30
33
38namespace apollo {
39namespace common {
40namespace math {
41
48 int max_depth = -1;
50 int max_leaf_size = -1;
52 double max_leaf_dimension = -1.0;
53};
54
59template <class ObjectType>
61 public:
62 using ObjectPtr = const ObjectType *;
70 AABoxKDTree2dNode(const std::vector<ObjectPtr> &objects,
71 const AABoxKDTreeParams &params, int depth)
72 : depth_(depth) {
73 ACHECK(!objects.empty());
74
75 ComputeBoundary(objects);
76 ComputePartition();
77
78 if (SplitToSubNodes(objects, params)) {
79 std::vector<ObjectPtr> left_subnode_objects;
80 std::vector<ObjectPtr> right_subnode_objects;
81 PartitionObjects(objects, &left_subnode_objects, &right_subnode_objects);
82
83 // Split to sub-nodes.
84 if (!left_subnode_objects.empty()) {
85 left_subnode_.reset(new AABoxKDTree2dNode<ObjectType>(
86 left_subnode_objects, params, depth + 1));
87 }
88 if (!right_subnode_objects.empty()) {
89 right_subnode_.reset(new AABoxKDTree2dNode<ObjectType>(
90 right_subnode_objects, params, depth + 1));
91 }
92 } else {
93 InitObjects(objects);
94 }
95 }
96
103 ObjectPtr GetNearestObject(const Vec2d &point) const {
104 ObjectPtr nearest_object = nullptr;
105 double min_distance_sqr = std::numeric_limits<double>::infinity();
106 GetNearestObjectInternal(point, &min_distance_sqr, &nearest_object);
107 return nearest_object;
108 }
109
117 std::vector<ObjectPtr> GetObjects(const Vec2d &point,
118 const double distance) const {
119 std::vector<ObjectPtr> result_objects;
120 GetObjectsInternal(point, distance, Square(distance), &result_objects);
121 return result_objects;
122 }
123
129 return AABox2d({min_x_, min_y_}, {max_x_, max_y_});
130 }
131
132 private:
133 void InitObjects(const std::vector<ObjectPtr> &objects) {
134 num_objects_ = static_cast<int>(objects.size());
135 objects_sorted_by_min_ = objects;
136 objects_sorted_by_max_ = objects;
137 std::sort(objects_sorted_by_min_.begin(), objects_sorted_by_min_.end(),
138 [&](ObjectPtr obj1, ObjectPtr obj2) {
139 return partition_ == PARTITION_X
140 ? obj1->aabox().min_x() < obj2->aabox().min_x()
141 : obj1->aabox().min_y() < obj2->aabox().min_y();
142 });
143 std::sort(objects_sorted_by_max_.begin(), objects_sorted_by_max_.end(),
144 [&](ObjectPtr obj1, ObjectPtr obj2) {
145 return partition_ == PARTITION_X
146 ? obj1->aabox().max_x() > obj2->aabox().max_x()
147 : obj1->aabox().max_y() > obj2->aabox().max_y();
148 });
149 objects_sorted_by_min_bound_.reserve(num_objects_);
150 for (ObjectPtr object : objects_sorted_by_min_) {
151 objects_sorted_by_min_bound_.push_back(partition_ == PARTITION_X
152 ? object->aabox().min_x()
153 : object->aabox().min_y());
154 }
155 objects_sorted_by_max_bound_.reserve(num_objects_);
156 for (ObjectPtr object : objects_sorted_by_max_) {
157 objects_sorted_by_max_bound_.push_back(partition_ == PARTITION_X
158 ? object->aabox().max_x()
159 : object->aabox().max_y());
160 }
161 }
162
163 bool SplitToSubNodes(const std::vector<ObjectPtr> &objects,
164 const AABoxKDTreeParams &params) {
165 if (params.max_depth >= 0 && depth_ >= params.max_depth) {
166 return false;
167 }
168 if (static_cast<int>(objects.size()) <= std::max(1, params.max_leaf_size)) {
169 return false;
170 }
171 if (params.max_leaf_dimension >= 0.0 &&
172 std::max(max_x_ - min_x_, max_y_ - min_y_) <=
173 params.max_leaf_dimension) {
174 return false;
175 }
176 return true;
177 }
178
179 double LowerDistanceSquareToPoint(const Vec2d &point) const {
180 double dx = 0.0;
181 if (point.x() < min_x_) {
182 dx = min_x_ - point.x();
183 } else if (point.x() > max_x_) {
184 dx = point.x() - max_x_;
185 }
186 double dy = 0.0;
187 if (point.y() < min_y_) {
188 dy = min_y_ - point.y();
189 } else if (point.y() > max_y_) {
190 dy = point.y() - max_y_;
191 }
192 return dx * dx + dy * dy;
193 }
194
195 double UpperDistanceSquareToPoint(const Vec2d &point) const {
196 const double dx =
197 (point.x() > mid_x_ ? (point.x() - min_x_) : (point.x() - max_x_));
198 const double dy =
199 (point.y() > mid_y_ ? (point.y() - min_y_) : (point.y() - max_y_));
200 return dx * dx + dy * dy;
201 }
202
203 void GetAllObjects(std::vector<ObjectPtr> *const result_objects) const {
204 result_objects->insert(result_objects->end(),
205 objects_sorted_by_min_.begin(),
206 objects_sorted_by_min_.end());
207 if (left_subnode_ != nullptr) {
208 left_subnode_->GetAllObjects(result_objects);
209 }
210 if (right_subnode_ != nullptr) {
211 right_subnode_->GetAllObjects(result_objects);
212 }
213 }
214
215 void GetObjectsInternal(const Vec2d &point, const double distance,
216 const double distance_sqr,
217 std::vector<ObjectPtr> *const result_objects) const {
218 if (LowerDistanceSquareToPoint(point) > distance_sqr) {
219 return;
220 }
221 if (UpperDistanceSquareToPoint(point) <= distance_sqr) {
222 GetAllObjects(result_objects);
223 return;
224 }
225 const double pvalue = (partition_ == PARTITION_X ? point.x() : point.y());
226 if (pvalue < partition_position_) {
227 const double limit = pvalue + distance;
228 for (int i = 0; i < num_objects_; ++i) {
229 if (objects_sorted_by_min_bound_[i] > limit) {
230 break;
231 }
232 ObjectPtr object = objects_sorted_by_min_[i];
233 if (object->DistanceSquareTo(point) <= distance_sqr) {
234 result_objects->push_back(object);
235 }
236 }
237 } else {
238 const double limit = pvalue - distance;
239 for (int i = 0; i < num_objects_; ++i) {
240 if (objects_sorted_by_max_bound_[i] < limit) {
241 break;
242 }
243 ObjectPtr object = objects_sorted_by_max_[i];
244 if (object->DistanceSquareTo(point) <= distance_sqr) {
245 result_objects->push_back(object);
246 }
247 }
248 }
249 if (left_subnode_ != nullptr) {
250 left_subnode_->GetObjectsInternal(point, distance, distance_sqr,
251 result_objects);
252 }
253 if (right_subnode_ != nullptr) {
254 right_subnode_->GetObjectsInternal(point, distance, distance_sqr,
255 result_objects);
256 }
257 }
258
259 void GetNearestObjectInternal(const Vec2d &point,
260 double *const min_distance_sqr,
261 ObjectPtr *const nearest_object) const {
262 if (LowerDistanceSquareToPoint(point) >= *min_distance_sqr - kMathEpsilon) {
263 return;
264 }
265 const double pvalue = (partition_ == PARTITION_X ? point.x() : point.y());
266 const bool search_left_first = (pvalue < partition_position_);
267 if (search_left_first) {
268 if (left_subnode_ != nullptr) {
269 left_subnode_->GetNearestObjectInternal(point, min_distance_sqr,
270 nearest_object);
271 }
272 } else {
273 if (right_subnode_ != nullptr) {
274 right_subnode_->GetNearestObjectInternal(point, min_distance_sqr,
275 nearest_object);
276 }
277 }
278 if (*min_distance_sqr <= kMathEpsilon) {
279 return;
280 }
281
282 if (search_left_first) {
283 for (int i = 0; i < num_objects_; ++i) {
284 const double bound = objects_sorted_by_min_bound_[i];
285 if (bound > pvalue && Square(bound - pvalue) > *min_distance_sqr) {
286 break;
287 }
288 ObjectPtr object = objects_sorted_by_min_[i];
289 const double distance_sqr = object->DistanceSquareTo(point);
290 if (distance_sqr < *min_distance_sqr) {
291 *min_distance_sqr = distance_sqr;
292 *nearest_object = object;
293 }
294 }
295 } else {
296 for (int i = 0; i < num_objects_; ++i) {
297 const double bound = objects_sorted_by_max_bound_[i];
298 if (bound < pvalue && Square(bound - pvalue) > *min_distance_sqr) {
299 break;
300 }
301 ObjectPtr object = objects_sorted_by_max_[i];
302 const double distance_sqr = object->DistanceSquareTo(point);
303 if (distance_sqr < *min_distance_sqr) {
304 *min_distance_sqr = distance_sqr;
305 *nearest_object = object;
306 }
307 }
308 }
309 if (*min_distance_sqr <= kMathEpsilon) {
310 return;
311 }
312 if (search_left_first) {
313 if (right_subnode_ != nullptr) {
314 right_subnode_->GetNearestObjectInternal(point, min_distance_sqr,
315 nearest_object);
316 }
317 } else {
318 if (left_subnode_ != nullptr) {
319 left_subnode_->GetNearestObjectInternal(point, min_distance_sqr,
320 nearest_object);
321 }
322 }
323 }
324
325 void ComputeBoundary(const std::vector<ObjectPtr> &objects) {
326 min_x_ = std::numeric_limits<double>::infinity();
327 min_y_ = std::numeric_limits<double>::infinity();
328 max_x_ = -std::numeric_limits<double>::infinity();
329 max_y_ = -std::numeric_limits<double>::infinity();
330 for (ObjectPtr object : objects) {
331 min_x_ = std::fmin(min_x_, object->aabox().min_x());
332 max_x_ = std::fmax(max_x_, object->aabox().max_x());
333 min_y_ = std::fmin(min_y_, object->aabox().min_y());
334 max_y_ = std::fmax(max_y_, object->aabox().max_y());
335 }
336 mid_x_ = (min_x_ + max_x_) / 2.0;
337 mid_y_ = (min_y_ + max_y_) / 2.0;
338 ACHECK(!std::isinf(max_x_) && !std::isinf(max_y_) && !std::isinf(min_x_) &&
339 !std::isinf(min_y_))
340 << "the provided object box size is infinity";
341 }
342
343 void ComputePartition() {
344 if (max_x_ - min_x_ >= max_y_ - min_y_) {
345 partition_ = PARTITION_X;
346 partition_position_ = (min_x_ + max_x_) / 2.0;
347 } else {
348 partition_ = PARTITION_Y;
349 partition_position_ = (min_y_ + max_y_) / 2.0;
350 }
351 }
352
353 void PartitionObjects(const std::vector<ObjectPtr> &objects,
354 std::vector<ObjectPtr> *const left_subnode_objects,
355 std::vector<ObjectPtr> *const right_subnode_objects) {
356 left_subnode_objects->clear();
357 right_subnode_objects->clear();
358 std::vector<ObjectPtr> other_objects;
359 if (partition_ == PARTITION_X) {
360 for (ObjectPtr object : objects) {
361 if (object->aabox().max_x() <= partition_position_) {
362 left_subnode_objects->push_back(object);
363 } else if (object->aabox().min_x() >= partition_position_) {
364 right_subnode_objects->push_back(object);
365 } else {
366 other_objects.push_back(object);
367 }
368 }
369 } else {
370 for (ObjectPtr object : objects) {
371 if (object->aabox().max_y() <= partition_position_) {
372 left_subnode_objects->push_back(object);
373 } else if (object->aabox().min_y() >= partition_position_) {
374 right_subnode_objects->push_back(object);
375 } else {
376 other_objects.push_back(object);
377 }
378 }
379 }
380 InitObjects(other_objects);
381 }
382
383 private:
384 int num_objects_ = 0;
385 std::vector<ObjectPtr> objects_sorted_by_min_;
386 std::vector<ObjectPtr> objects_sorted_by_max_;
387 std::vector<double> objects_sorted_by_min_bound_;
388 std::vector<double> objects_sorted_by_max_bound_;
389 int depth_ = 0;
390
391 // Boundary
392 double min_x_ = 0.0;
393 double max_x_ = 0.0;
394 double min_y_ = 0.0;
395 double max_y_ = 0.0;
396 double mid_x_ = 0.0;
397 double mid_y_ = 0.0;
398
399 enum Partition {
400 PARTITION_X = 1,
401 PARTITION_Y = 2,
402 };
403 Partition partition_ = PARTITION_X;
404 double partition_position_ = 0.0;
405
406 std::unique_ptr<AABoxKDTree2dNode<ObjectType>> left_subnode_ = nullptr;
407 std::unique_ptr<AABoxKDTree2dNode<ObjectType>> right_subnode_ = nullptr;
408};
409
414template <class ObjectType>
416 public:
417 using ObjectPtr = const ObjectType *;
418
423 AABoxKDTree2d(const std::vector<ObjectType> &objects,
424 const AABoxKDTreeParams &params) {
425 if (!objects.empty()) {
426 std::vector<ObjectPtr> object_ptrs;
427 for (const auto &object : objects) {
428 object_ptrs.push_back(&object);
429 }
430 root_.reset(new AABoxKDTree2dNode<ObjectType>(object_ptrs, params, 0));
431 }
432 }
433
439 ObjectPtr GetNearestObject(const Vec2d &point) const {
440 return root_ == nullptr ? nullptr : root_->GetNearestObject(point);
441 }
442
449 std::vector<ObjectPtr> GetObjects(const Vec2d &point,
450 const double distance) const {
451 if (root_ == nullptr) {
452 return {};
453 }
454 return root_->GetObjects(point, distance);
455 }
456
462 return root_ == nullptr ? AABox2d() : root_->GetBoundingBox();
463 }
464
465 private:
466 std::unique_ptr<AABoxKDTree2dNode<ObjectType>> root_ = nullptr;
467};
468
469} // namespace math
470} // namespace common
471} // namespace apollo
Defines the AABox2d class.
Implements a class of (undirected) axes-aligned bounding boxes in 2-D.
Definition aabox2d.h:42
The class of KD-tree node of axis-aligned bounding box.
ObjectPtr GetNearestObject(const Vec2d &point) const
Get the nearest object to a target point by the KD-tree rooted at this node.
std::vector< ObjectPtr > GetObjects(const Vec2d &point, const double distance) const
Get objects within a distance to a point by the KD-tree rooted at this node.
AABox2d GetBoundingBox() const
Get the axis-aligned bounding box of the objects.
AABoxKDTree2dNode(const std::vector< ObjectPtr > &objects, const AABoxKDTreeParams &params, int depth)
Constructor which takes a vector of objects, parameters and depth of the node.
The class of KD-tree of Aligned Axis Bounding Box(AABox).
std::vector< ObjectPtr > GetObjects(const Vec2d &point, const double distance) const
Get objects within a distance to a point.
ObjectPtr GetNearestObject(const Vec2d &point) const
Get the nearest object to a target point.
AABoxKDTree2d(const std::vector< ObjectType > &objects, const AABoxKDTreeParams &params)
Constructor which takes a vector of objects and parameters.
AABox2d GetBoundingBox() const
Get the axis-aligned bounding box of the objects.
Implements a class of 2-dimensional vectors.
Definition vec2d.h:42
#define ACHECK(cond)
Definition log.h:80
Math-related util functions.
constexpr double kMathEpsilon
Definition vec2d.h:35
T Square(const T value)
Compute squared value.
Definition math_utils.h:141
class register implement
Definition arena_queue.h:37
Contains parameters of axis-aligned bounding box.
int max_leaf_size
The maximum number of items in one leaf node.
int max_depth
The maximum depth of the kdtree.
double max_leaf_dimension
The maximum dimension size of leaf node.