Apollo 10.0
自动驾驶开放平台
box2d.cc
浏览该文件的文档.
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
18
19#include <algorithm>
20#include <cmath>
21#include <utility>
22
23#include "absl/strings/str_cat.h"
24
25#include "cyber/common/log.h"
28
29namespace apollo {
30namespace common {
31namespace math {
32namespace {
33
34double PtSegDistance(double query_x, double query_y, double start_x,
35 double start_y, double end_x, double end_y,
36 double length) {
37 const double x0 = query_x - start_x;
38 const double y0 = query_y - start_y;
39 const double dx = end_x - start_x;
40 const double dy = end_y - start_y;
41 const double proj = x0 * dx + y0 * dy;
42 if (proj <= 0.0) {
43 return hypot(x0, y0);
44 }
45 if (proj >= length * length) {
46 return hypot(x0 - dx, y0 - dy);
47 }
48 return std::abs(x0 * dy - y0 * dx) / length;
49}
50
51} // namespace
52
53Box2d::Box2d(const Vec2d &center, const double heading, const double length,
54 const double width)
55 : center_(center),
56 length_(length),
57 width_(width),
58 half_length_(length / 2.0),
59 half_width_(width / 2.0),
60 heading_(heading),
61 cos_heading_(cos(heading)),
62 sin_heading_(sin(heading)) {
63 CHECK_GT(length_, -kMathEpsilon);
64 CHECK_GT(width_, -kMathEpsilon);
66}
67
68Box2d::Box2d(const Vec2d &point, double heading, double front_length,
69 double back_length, double width)
70 : length_(front_length + back_length),
71 width_(width),
72 half_length_(length_ / 2.0),
73 half_width_(width / 2.0),
74 heading_(heading),
75 cos_heading_(cos(heading)),
76 sin_heading_(sin(heading)) {
77 CHECK_GT(length_, -kMathEpsilon);
78 CHECK_GT(width_, -kMathEpsilon);
79 double delta_length = (front_length - back_length) / 2.0;
80 center_ = Vec2d(point.x() + cos_heading_ * delta_length,
81 point.y() + sin_heading_ * delta_length);
83}
84
85Box2d::Box2d(const LineSegment2d &axis, const double width)
86 : center_(axis.center()),
87 length_(axis.length()),
88 width_(width),
89 half_length_(axis.length() / 2.0),
90 half_width_(width / 2.0),
91 heading_(axis.heading()),
92 cos_heading_(axis.cos_heading()),
93 sin_heading_(axis.sin_heading()) {
94 CHECK_GT(length_, -kMathEpsilon);
95 CHECK_GT(width_, -kMathEpsilon);
97}
98
100 const double dx1 = cos_heading_ * half_length_;
101 const double dy1 = sin_heading_ * half_length_;
102 const double dx2 = sin_heading_ * half_width_;
103 const double dy2 = -cos_heading_ * half_width_;
104 corners_.clear();
105 corners_.emplace_back(center_.x() + dx1 + dx2, center_.y() + dy1 + dy2);
106 corners_.emplace_back(center_.x() + dx1 - dx2, center_.y() + dy1 - dy2);
107 corners_.emplace_back(center_.x() - dx1 - dx2, center_.y() - dy1 - dy2);
108 corners_.emplace_back(center_.x() - dx1 + dx2, center_.y() - dy1 + dy2);
109
110 for (auto &corner : corners_) {
111 max_x_ = std::fmax(corner.x(), max_x_);
112 min_x_ = std::fmin(corner.x(), min_x_);
113 max_y_ = std::fmax(corner.y(), max_y_);
114 min_y_ = std::fmin(corner.y(), min_y_);
115 }
116}
117
119 : center_(aabox.center()),
120 length_(aabox.length()),
121 width_(aabox.width()),
122 half_length_(aabox.half_length()),
123 half_width_(aabox.half_width()),
124 heading_(0.0),
125 cos_heading_(1.0),
126 sin_heading_(0.0) {
127 CHECK_GT(length_, -kMathEpsilon);
128 CHECK_GT(width_, -kMathEpsilon);
129}
130
132 const Vec2d &opposite_corner) {
133 const double x1 = std::min(one_corner.x(), opposite_corner.x());
134 const double x2 = std::max(one_corner.x(), opposite_corner.x());
135 const double y1 = std::min(one_corner.y(), opposite_corner.y());
136 const double y2 = std::max(one_corner.y(), opposite_corner.y());
137 return Box2d({(x1 + x2) / 2.0, (y1 + y2) / 2.0}, 0.0, x2 - x1, y2 - y1);
138}
139
140void Box2d::GetAllCorners(std::vector<Vec2d> *const corners) const {
141 if (corners == nullptr) {
142 return;
143 }
144 *corners = corners_;
145}
146
147const std::vector<Vec2d> &Box2d::GetAllCorners() const { return corners_; }
148
149bool Box2d::IsPointIn(const Vec2d &point) const {
150 const double x0 = point.x() - center_.x();
151 const double y0 = point.y() - center_.y();
152 const double dx = std::abs(x0 * cos_heading_ + y0 * sin_heading_);
153 const double dy = std::abs(-x0 * sin_heading_ + y0 * cos_heading_);
154 return dx <= half_length_ + kMathEpsilon && dy <= half_width_ + kMathEpsilon;
155}
156
157bool Box2d::IsPointOnBoundary(const Vec2d &point) const {
158 const double x0 = point.x() - center_.x();
159 const double y0 = point.y() - center_.y();
160 const double dx = std::abs(x0 * cos_heading_ + y0 * sin_heading_);
161 const double dy = std::abs(x0 * sin_heading_ - y0 * cos_heading_);
162 return (std::abs(dx - half_length_) <= kMathEpsilon &&
163 dy <= half_width_ + kMathEpsilon) ||
164 (std::abs(dy - half_width_) <= kMathEpsilon &&
165 dx <= half_length_ + kMathEpsilon);
166}
167
168double Box2d::DistanceTo(const Vec2d &point) const {
169 const double x0 = point.x() - center_.x();
170 const double y0 = point.y() - center_.y();
171 const double dx =
172 std::abs(x0 * cos_heading_ + y0 * sin_heading_) - half_length_;
173 const double dy =
174 std::abs(x0 * sin_heading_ - y0 * cos_heading_) - half_width_;
175 if (dx <= 0.0) {
176 return std::max(0.0, dy);
177 }
178 if (dy <= 0.0) {
179 return dx;
180 }
181 return hypot(dx, dy);
182}
183
184bool Box2d::HasOverlap(const LineSegment2d &line_segment) const {
185 if (line_segment.length() <= kMathEpsilon) {
186 return IsPointIn(line_segment.start());
187 }
188 if (std::fmax(line_segment.start().x(), line_segment.end().x()) < min_x() ||
189 std::fmin(line_segment.start().x(), line_segment.end().x()) > max_x() ||
190 std::fmax(line_segment.start().y(), line_segment.end().y()) < min_y() ||
191 std::fmin(line_segment.start().y(), line_segment.end().y()) > max_y()) {
192 return false;
193 }
194 // Construct coordinate system with origin point as left_bottom corner of
195 // Box2d, y axis along direction of heading.
196 Vec2d x_axis(sin_heading_, -cos_heading_);
197 Vec2d y_axis(cos_heading_, sin_heading_);
198 // corners_[2] is the left bottom point of the box.
199 Vec2d start_v = line_segment.start() - corners_[2];
200 // "start_point" is the start point of "line_segment" mapped in the new
201 // coordinate system.
202 Vec2d start_point(start_v.InnerProd(x_axis), start_v.InnerProd(y_axis));
203 // Check if "start_point" is inside the box.
204 if (is_inside_rectangle(start_point)) {
205 return true;
206 }
207 // Check if "end_point" is inside the box.
208 Vec2d end_v = line_segment.end() - corners_[2];
209 Vec2d end_point(end_v.InnerProd(x_axis), end_v.InnerProd(y_axis));
210 if (is_inside_rectangle(end_point)) {
211 return true;
212 }
213 // Exclude the case when the 2 points of "line_segment" are at the same side
214 // of rectangle.
215 if ((start_point.x() < 0.0) && (end_point.x() < 0.0)) {
216 return false;
217 }
218 if ((start_point.y() < 0.0) && (end_point.y() < 0.0)) {
219 return false;
220 }
221 if ((start_point.x() > width_) && (end_point.x() > width_)) {
222 return false;
223 }
224 if ((start_point.y() > length_) && (end_point.y() > length_)) {
225 return false;
226 }
227 // Check if "line_segment" intersects with box.
228 Vec2d line_direction = line_segment.end() - line_segment.start();
229 Vec2d normal_vec(line_direction.y(), -line_direction.x());
230 Vec2d p1 = center_ - line_segment.start();
231 Vec2d diagonal_vec = center_ - corners_[0];
232 // if project_p1 < projection of diagonal, "line_segment" intersects with box.
233 double project_p1 = fabs(p1.InnerProd(normal_vec));
234 if (fabs(diagonal_vec.InnerProd(normal_vec)) >= project_p1) {
235 return true;
236 }
237 diagonal_vec = center_ - corners_[1];
238 if (fabs(diagonal_vec.InnerProd(normal_vec)) >= project_p1) {
239 return true;
240 }
241 return false;
242}
243
244double Box2d::DistanceTo(const LineSegment2d &line_segment) const {
245 if (line_segment.length() <= kMathEpsilon) {
246 return DistanceTo(line_segment.start());
247 }
248 const double ref_x1 = line_segment.start().x() - center_.x();
249 const double ref_y1 = line_segment.start().y() - center_.y();
250 double x1 = ref_x1 * cos_heading_ + ref_y1 * sin_heading_;
251 double y1 = ref_x1 * sin_heading_ - ref_y1 * cos_heading_;
252 double box_x = half_length_;
253 double box_y = half_width_;
254 int gx1 = (x1 >= box_x ? 1 : (x1 <= -box_x ? -1 : 0));
255 int gy1 = (y1 >= box_y ? 1 : (y1 <= -box_y ? -1 : 0));
256 if (gx1 == 0 && gy1 == 0) {
257 return 0.0;
258 }
259 const double ref_x2 = line_segment.end().x() - center_.x();
260 const double ref_y2 = line_segment.end().y() - center_.y();
261 double x2 = ref_x2 * cos_heading_ + ref_y2 * sin_heading_;
262 double y2 = ref_x2 * sin_heading_ - ref_y2 * cos_heading_;
263 int gx2 = (x2 >= box_x ? 1 : (x2 <= -box_x ? -1 : 0));
264 int gy2 = (y2 >= box_y ? 1 : (y2 <= -box_y ? -1 : 0));
265 if (gx2 == 0 && gy2 == 0) {
266 return 0.0;
267 }
268 if (gx1 < 0 || (gx1 == 0 && gx2 < 0)) {
269 x1 = -x1;
270 gx1 = -gx1;
271 x2 = -x2;
272 gx2 = -gx2;
273 }
274 if (gy1 < 0 || (gy1 == 0 && gy2 < 0)) {
275 y1 = -y1;
276 gy1 = -gy1;
277 y2 = -y2;
278 gy2 = -gy2;
279 }
280 if (gx1 < gy1 || (gx1 == gy1 && gx2 < gy2)) {
281 std::swap(x1, y1);
282 std::swap(gx1, gy1);
283 std::swap(x2, y2);
284 std::swap(gx2, gy2);
285 std::swap(box_x, box_y);
286 }
287 if (gx1 == 1 && gy1 == 1) {
288 switch (gx2 * 3 + gy2) {
289 case 4:
290 return PtSegDistance(box_x, box_y, x1, y1, x2, y2,
291 line_segment.length());
292 case 3:
293 return (x1 > x2) ? (x2 - box_x)
294 : PtSegDistance(box_x, box_y, x1, y1, x2, y2,
295 line_segment.length());
296 case 2:
297 return (x1 > x2) ? PtSegDistance(box_x, -box_y, x1, y1, x2, y2,
298 line_segment.length())
299 : PtSegDistance(box_x, box_y, x1, y1, x2, y2,
300 line_segment.length());
301 case -1:
302 return CrossProd({x1, y1}, {x2, y2}, {box_x, -box_y}) >= 0.0
303 ? 0.0
304 : PtSegDistance(box_x, -box_y, x1, y1, x2, y2,
305 line_segment.length());
306 case -4:
307 return CrossProd({x1, y1}, {x2, y2}, {box_x, -box_y}) <= 0.0
308 ? PtSegDistance(box_x, -box_y, x1, y1, x2, y2,
309 line_segment.length())
310 : (CrossProd({x1, y1}, {x2, y2}, {-box_x, box_y}) <= 0.0
311 ? 0.0
312 : PtSegDistance(-box_x, box_y, x1, y1, x2, y2,
313 line_segment.length()));
314 }
315 } else {
316 switch (gx2 * 3 + gy2) {
317 case 4:
318 return (x1 < x2) ? (x1 - box_x)
319 : PtSegDistance(box_x, box_y, x1, y1, x2, y2,
320 line_segment.length());
321 case 3:
322 return std::min(x1, x2) - box_x;
323 case 1:
324 case -2:
325 return CrossProd({x1, y1}, {x2, y2}, {box_x, box_y}) <= 0.0
326 ? 0.0
327 : PtSegDistance(box_x, box_y, x1, y1, x2, y2,
328 line_segment.length());
329 case -3:
330 return 0.0;
331 }
332 }
333 ACHECK(0) << "unimplemented state: " << gx1 << " " << gy1 << " " << gx2 << " "
334 << gy2;
335 return 0.0;
336}
337
338double Box2d::DistanceTo(const Box2d &box) const {
339 return Polygon2d(box).DistanceTo(*this);
340}
341
342bool Box2d::HasOverlap(const Box2d &box) const {
343 if (box.max_x() < min_x() || box.min_x() > max_x() || box.max_y() < min_y() ||
344 box.min_y() > max_y()) {
345 return false;
346 }
347
348 const double shift_x = box.center_x() - center_.x();
349 const double shift_y = box.center_y() - center_.y();
350
351 const double dx1 = cos_heading_ * half_length_;
352 const double dy1 = sin_heading_ * half_length_;
353 const double dx2 = sin_heading_ * half_width_;
354 const double dy2 = -cos_heading_ * half_width_;
355 const double dx3 = box.cos_heading() * box.half_length();
356 const double dy3 = box.sin_heading() * box.half_length();
357 const double dx4 = box.sin_heading() * box.half_width();
358 const double dy4 = -box.cos_heading() * box.half_width();
359
360 return std::abs(shift_x * cos_heading_ + shift_y * sin_heading_) <=
361 std::abs(dx3 * cos_heading_ + dy3 * sin_heading_) +
362 std::abs(dx4 * cos_heading_ + dy4 * sin_heading_) +
363 half_length_ &&
364 std::abs(shift_x * sin_heading_ - shift_y * cos_heading_) <=
365 std::abs(dx3 * sin_heading_ - dy3 * cos_heading_) +
366 std::abs(dx4 * sin_heading_ - dy4 * cos_heading_) +
367 half_width_ &&
368 std::abs(shift_x * box.cos_heading() + shift_y * box.sin_heading()) <=
369 std::abs(dx1 * box.cos_heading() + dy1 * box.sin_heading()) +
370 std::abs(dx2 * box.cos_heading() + dy2 * box.sin_heading()) +
371 box.half_length() &&
372 std::abs(shift_x * box.sin_heading() - shift_y * box.cos_heading()) <=
373 std::abs(dx1 * box.sin_heading() - dy1 * box.cos_heading()) +
374 std::abs(dx2 * box.sin_heading() - dy2 * box.cos_heading()) +
375 box.half_width();
376}
377
379 const double dx1 = std::abs(cos_heading_ * half_length_);
380 const double dy1 = std::abs(sin_heading_ * half_length_);
381 const double dx2 = std::abs(sin_heading_ * half_width_);
382 const double dy2 = std::abs(cos_heading_ * half_width_);
383 return AABox2d(center_, (dx1 + dx2) * 2.0, (dy1 + dy2) * 2.0);
384}
385
386void Box2d::RotateFromCenter(const double rotate_angle) {
387 heading_ = NormalizeAngle(heading_ + rotate_angle);
388 cos_heading_ = std::cos(heading_);
389 sin_heading_ = std::sin(heading_);
390 InitCorners();
391}
392
393void Box2d::Shift(const Vec2d &shift_vec) {
394 center_ += shift_vec;
395 for (size_t i = 0; i < 4; ++i) {
396 corners_[i] += shift_vec;
397 }
398 for (auto &corner : corners_) {
399 max_x_ = std::fmax(corner.x(), max_x_);
400 min_x_ = std::fmin(corner.x(), min_x_);
401 max_y_ = std::fmax(corner.y(), max_y_);
402 min_y_ = std::fmin(corner.y(), min_y_);
403 }
404}
405
406void Box2d::LongitudinalExtend(const double extension_length) {
407 length_ += extension_length;
408 half_length_ += extension_length / 2.0;
409 InitCorners();
410}
411
412void Box2d::LateralExtend(const double extension_length) {
413 width_ += extension_length;
414 half_width_ += extension_length / 2.0;
415 InitCorners();
416}
417
418std::string Box2d::DebugString() const {
419 return absl::StrCat("box2d ( center = ", center_.DebugString(),
420 " heading = ", heading_, " length = ", length_,
421 " width = ", width_, " )");
422}
423
424} // namespace math
425} // namespace common
426} // namespace apollo
The class of Box2d.
Implements a class of (undirected) axes-aligned bounding boxes in 2-D.
Definition aabox2d.h:42
Rectangular (undirected) bounding box in 2-D.
Definition box2d.h:52
std::string DebugString() const
Gets a human-readable description of the box
Definition box2d.cc:418
void Shift(const Vec2d &shift_vec)
Shifts this box by a given vector
Definition box2d.cc:393
bool IsPointOnBoundary(const Vec2d &point) const
Tests points for membership in the boundary of the box
Definition box2d.cc:157
double min_y() const
Definition box2d.h:272
double half_width() const
Getter of half the width
Definition box2d.h:142
void LateralExtend(const double extension_length)
Definition box2d.cc:412
bool HasOverlap(const LineSegment2d &line_segment) const
Determines whether this box overlaps a given line segment
Definition box2d.cc:184
AABox2d GetAABox() const
Gets the smallest axes-aligned box containing the current one
Definition box2d.cc:378
double center_x() const
Getter of the x-coordinate of the center of the box
Definition box2d.h:112
void RotateFromCenter(const double rotate_angle)
Rotate from center.
Definition box2d.cc:386
bool IsPointIn(const Vec2d &point) const
Tests points for membership in the box
Definition box2d.cc:149
double half_length() const
Getter of half the length
Definition box2d.h:136
double min_x() const
Definition box2d.h:270
double sin_heading() const
Getter of the sine of the heading
Definition box2d.h:160
double DistanceTo(const Vec2d &point) const
Determines the distance between the box and a given point
Definition box2d.cc:168
double cos_heading() const
Getter of the cosine of the heading
Definition box2d.h:154
double max_y() const
Definition box2d.h:271
const std::vector< Vec2d > & GetAllCorners() const
Getter of the corners of the box
Definition box2d.cc:147
static Box2d CreateAABox(const Vec2d &one_corner, const Vec2d &opposite_corner)
Creates an axes-aligned Box2d from two opposite corners
Definition box2d.cc:131
void LongitudinalExtend(const double extension_length)
Extend the box longitudinally
Definition box2d.cc:406
double center_y() const
Getter of the y-coordinate of the center of the box
Definition box2d.h:118
double max_x() const
Definition box2d.h:269
const Vec2d & end() const
Get the end point.
double length() const
Get the length of the line segment.
const Vec2d & start() const
Get the start point.
The class of polygon in 2-D.
Definition polygon2d.h:42
double DistanceTo(const Vec2d &point) const
Compute the distance from a point to the polygon.
Definition polygon2d.cc:45
Implements a class of 2-dimensional vectors.
Definition vec2d.h:42
double InnerProd(const Vec2d &other) const
Returns the inner product between these two Vec2d.
Definition vec2d.cc:61
std::string DebugString() const
Returns a human-readable string representing this object
Definition vec2d.cc:125
double y() const
Getter for y component
Definition vec2d.h:57
double x() const
Getter for x component
Definition vec2d.h:54
#define ACHECK(cond)
Definition log.h:80
Math-related util functions.
float cos(Angle16 a)
Definition angle.cc:42
constexpr double kMathEpsilon
Definition vec2d.h:35
double CrossProd(const Vec2d &start_point, const Vec2d &end_point_1, const Vec2d &end_point_2)
Cross product between two 2-D vectors from the common start point, and end at two other points.
Definition math_utils.cc:28
float sin(Angle16 a)
Definition angle.cc:25
double NormalizeAngle(const double angle)
Normalize angle to [-PI, PI).
Definition math_utils.cc:53
class register implement
Definition arena_queue.h:37
Define the Polygon2d class.