Apollo  6.0
Open source self driving car software
aaboxkdtree2d.h
Go to the documentation of this file.
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 
38 namespace apollo {
39 namespace common {
40 namespace math {
41 
48  int max_depth = -1;
50  int max_leaf_size = -1;
52  double max_leaf_dimension = -1.0;
53 };
54 
59 template <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 
414 template <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
double x() const
Getter for x component.
Definition: vec2d.h:54
AABoxKDTree2dNode(const std::vector< ObjectPtr > &objects, const AABoxKDTreeParams &params, int depth)
Constructor which takes a vector of objects, parameters and depth of the node.
Definition: aaboxkdtree2d.h:70
double max_leaf_dimension
The maximum dimension size of leaf node.
Definition: aaboxkdtree2d.h:52
double y() const
Getter for y component.
Definition: vec2d.h:57
The class of KD-tree of Aligned Axis Bounding Box(AABox).
Definition: aaboxkdtree2d.h:415
const ObjectType * ObjectPtr
Definition: aaboxkdtree2d.h:62
#define ACHECK(cond)
Definition: log.h:80
PlanningContext is the runtime context in planning. It is persistent across multiple frames...
Definition: atomic_hash_map.h:25
AABoxKDTree2d(const std::vector< ObjectType > &objects, const AABoxKDTreeParams &params)
Constructor which takes a vector of objects and parameters.
Definition: aaboxkdtree2d.h:423
int max_depth
The maximum depth of the kdtree.
Definition: aaboxkdtree2d.h:48
The class of KD-tree node of axis-aligned bounding box.
Definition: aaboxkdtree2d.h:60
T bound(const T &x, const T &min, const T &max)
Definition: misc.h:60
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.
Definition: aaboxkdtree2d.h:117
const ObjectType * ObjectPtr
Definition: aaboxkdtree2d.h:417
ObjectPtr GetNearestObject(const Vec2d &point) const
Get the nearest object to a target point.
Definition: aaboxkdtree2d.h:439
AABox2d GetBoundingBox() const
Get the axis-aligned bounding box of the objects.
Definition: aaboxkdtree2d.h:461
std::vector< ObjectPtr > GetObjects(const Vec2d &point, const double distance) const
Get objects within a distance to a point.
Definition: aaboxkdtree2d.h:449
Defines the AABox2d class.
T Square(const T value)
Compute squared value.
Definition: math_utils.h:141
Implements a class of 2-dimensional vectors.
Definition: vec2d.h:42
Math-related util functions.
int max_leaf_size
The maximum number of items in one leaf node.
Definition: aaboxkdtree2d.h:50
ObjectType
Definition: object_types.h:26
Contains parameters of axis-aligned bounding box.
Definition: aaboxkdtree2d.h:46
AABox2d GetBoundingBox() const
Get the axis-aligned bounding box of the objects.
Definition: aaboxkdtree2d.h:128
ObjectPtr GetNearestObject(const Vec2d &point) const
Get the nearest object to a target point by the KD-tree rooted at this node.
Definition: aaboxkdtree2d.h:103
Implements a class of (undirected) axes-aligned bounding boxes in 2-D. This class is referential-agno...
Definition: aabox2d.h:42
constexpr double kMathEpsilon
Definition: vec2d.h:35