| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- #include "math/box2d.h"
- #include "math/math_utils.h"
- #include "math/polygon2d.h"
- #include <cmath>
- #include <sstream>
- using namespace decision::math;
- namespace {
- double outer_prod(double x0, double y0, double x1, double y1, double x2, double y2)
- {
- return (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0);
- }
- double pt_seg_distance(double query_x, double query_y, double start_x, double start_y,
- double end_x, double end_y, double length)
- {
- const double x0 = query_x - start_x;
- const double y0 = query_y - start_y;
- const double dx = end_x - start_x;
- const double dy = end_y - start_y;
- const double proj = x0 * dx + y0 * dy;
- if (proj <= 0.0) {
- return hypot(x0, y0);
- }
- if (proj >= length * length) {
- return hypot(x0 - dx, y0 - dy);
- }
- return std::abs(x0 * dy - y0 * dx) / length;
- }
- } // namespace
- Box2d::Box2d(const Vec2d& center, const double heading,
- const double length, const double width)
- : _center(center)
- , _length(length)
- , _width(width)
- , _half_length(length / 2.0)
- , _half_width(width / 2.0)
- , _heading(heading)
- , _cos_heading(cos(heading))
- , _sin_heading(sin(heading))
- {
- CHECK_GT(_length, -kMathEpsilon);
- CHECK_GT(_width, -kMathEpsilon);
- }
- Box2d::Box2d(const Segment2d& axis, const double width)
- : _center(axis.center())
- , _length(axis.length())
- , _width(width)
- , _half_length(axis.length() / 2.0)
- , _half_width(width / 2.0)
- , _heading(axis.heading())
- , _cos_heading(axis.cos_heading())
- , _sin_heading(axis.sin_heading())
- {
- CHECK_GT(_length, -kMathEpsilon);
- CHECK_GT(_width, -kMathEpsilon);
- }
- Box2d::Box2d(const AABox2d& aabox)
- : _center(aabox.center())
- , _length(aabox.length())
- , _width(aabox.width())
- , _half_length(aabox.half_length())
- , _half_width(aabox.half_width())
- , _heading(0.0)
- , _cos_heading(1.0)
- , _sin_heading(0.0)
- {
- CHECK_GT(_length, -kMathEpsilon);
- CHECK_GT(_width, -kMathEpsilon);
- }
- Box2d Box2d::create_aa_box(const Vec2d& one_corner, const Vec2d& opponent_corner)
- {
- const double x1 = std::min(one_corner.x(), opponent_corner.x());
- const double x2 = std::max(one_corner.x(), opponent_corner.x());
- const double y1 = std::min(one_corner.y(), opponent_corner.y());
- const double y2 = std::max(one_corner.y(), opponent_corner.y());
- return Box2d({ (x1 + x2) / 2.0, (y1 + y2) / 2.0 }, 0.0, x2 - x1, y2 - y1);
- }
- void Box2d::get_all_corners(std::vector<Vec2d>* const corners) const
- {
- if (corners == nullptr) {
- return;
- }
- const double dx1 = _cos_heading * _half_length;
- const double dy1 = _sin_heading * _half_length;
- const double dx2 = _sin_heading * _half_width;
- const double dy2 = -_cos_heading * _half_width;
- corners->clear();
- corners->reserve(4);
- corners->emplace_back(_center.x() + dx1 + dx2, _center.y() + dy1 + dy2);
- corners->emplace_back(_center.x() + dx1 - dx2, _center.y() + dy1 - dy2);
- corners->emplace_back(_center.x() - dx1 - dx2, _center.y() - dy1 - dy2);
- corners->emplace_back(_center.x() - dx1 + dx2, _center.y() - dy1 + dy2);
- }
- bool Box2d::is_point_in(const Vec2d& point) const
- {
- const double x0 = point.x() - _center.x();
- const double y0 = point.y() - _center.y();
- const double dx = std::abs(x0 * _cos_heading + y0 * _sin_heading);
- const double dy = std::abs(x0 * _sin_heading - y0 * _cos_heading);
- return dx <= _half_length + kMathEpsilon && dy <= _half_width + kMathEpsilon;
- }
- bool Box2d::is_point_on_boundary(const Vec2d& point) const
- {
- const double x0 = point.x() - _center.x();
- const double y0 = point.y() - _center.y();
- const double dx = std::abs(x0 * _cos_heading + y0 * _sin_heading);
- const double dy = std::abs(x0 * _sin_heading - y0 * _cos_heading);
- return (std::abs(dx - _half_length) <= kMathEpsilon && dy <= _half_width + kMathEpsilon) || (std::abs(dy - _half_width) <= kMathEpsilon && dx <= _half_length + kMathEpsilon);
- }
- double Box2d::distance_to(const Vec2d& point) const
- {
- const double x0 = point.x() - _center.x();
- const double y0 = point.y() - _center.y();
- const double dx = std::abs(x0 * _cos_heading + y0 * _sin_heading) - _half_length;
- const double dy = std::abs(x0 * _sin_heading - y0 * _cos_heading) - _half_width;
- if (dx <= 0.0) {
- return std::max(0.0, dy);
- }
- if (dy <= 0.0) {
- return dx;
- }
- return hypot(dx, dy);
- }
- bool Box2d::has_overlap(const Segment2d& segment) const
- {
- if (segment.length() <= kMathEpsilon) {
- return is_point_in(segment.start());
- }
- return distance_to(segment) <= kMathEpsilon;
- }
- double Box2d::distance_to(const Segment2d& segment) const
- {
- if (segment.length() <= kMathEpsilon) {
- return distance_to(segment.start());
- }
- const double ref_x1 = segment.start().x() - _center.x();
- const double ref_y1 = segment.start().y() - _center.y();
- double x1 = ref_x1 * _cos_heading + ref_y1 * _sin_heading;
- double y1 = ref_x1 * _sin_heading - ref_y1 * _cos_heading;
- double box_x = _half_length;
- double box_y = _half_width;
- int gx1 = (x1 >= box_x ? 1 : (x1 <= -box_x ? -1 : 0));
- int gy1 = (y1 >= box_y ? 1 : (y1 <= -box_y ? -1 : 0));
- if (gx1 == 0 && gy1 == 0) {
- return 0.0;
- }
- const double ref_x2 = segment.end().x() - _center.x();
- const double ref_y2 = segment.end().y() - _center.y();
- double x2 = ref_x2 * _cos_heading + ref_y2 * _sin_heading;
- double y2 = ref_x2 * _sin_heading - ref_y2 * _cos_heading;
- int gx2 = (x2 >= box_x ? 1 : (x2 <= -box_x ? -1 : 0));
- int gy2 = (y2 >= box_y ? 1 : (y2 <= -box_y ? -1 : 0));
- if (gx2 == 0 && gy2 == 0) {
- return 0.0;
- }
- if (gx1 < 0 || (gx1 == 0 && gx2 < 0)) {
- x1 = -x1;
- gx1 = -gx1;
- x2 = -x2;
- gx2 = -gx2;
- }
- if (gy1 < 0 || (gy1 == 0 && gy2 < 0)) {
- y1 = -y1;
- gy1 = -gy1;
- y2 = -y2;
- gy2 = -gy2;
- }
- if (gx1 < gy1 || (gx1 == gy1 && gx2 < gy2)) {
- std::swap(x1, y1);
- std::swap(gx1, gy1);
- std::swap(x2, y2);
- std::swap(gx2, gy2);
- std::swap(box_x, box_y);
- }
- if (gx1 == 1 && gy1 == 1) {
- switch (gx2 * 3 + gy2) {
- case 4:
- return pt_seg_distance(box_x, box_y, x1, y1, x2, y2, segment.length());
- case 3:
- return (x1 > x2) ? (x2 - box_x)
- : pt_seg_distance(box_x, box_y, x1, y1, x2, y2, segment.length());
- case 2:
- return (x1 > x2) ? pt_seg_distance(box_x, -box_y, x1, y1, x2, y2, segment.length())
- : pt_seg_distance(box_x, box_y, x1, y1, x2, y2, segment.length());
- case -1:
- return outer_prod(x1, y1, x2, y2, box_x, -box_y) >= 0.0 ? 0.0
- : pt_seg_distance(box_x, -box_y, x1, y1, x2, y2, segment.length());
- case -4:
- return outer_prod(x1, y1, x2, y2, box_x, -box_y) <= 0.0 ? pt_seg_distance(box_x, -box_y, x1, y1, x2, y2, segment.length())
- : (outer_prod(x1, y1, x2, y2, -box_x, box_y) <= 0.0 ? 0.0
- : pt_seg_distance(-box_x, box_y, x1, y1, x2, y2, segment.length()));
- }
- } else {
- switch (gx2 * 3 + gy2) {
- case 4:
- return (x1 < x2) ? (x1 - box_x)
- : pt_seg_distance(box_x, box_y, x1, y1, x2, y2, segment.length());
- case 3:
- return std::min(x1, x2) - box_x;
- case 1:
- case -2:
- return outer_prod(x1, y1, x2, y2, box_x, box_y) <= 0.0 ? 0.0
- : pt_seg_distance(box_x, box_y, x1, y1, x2, y2, segment.length());
- case -3:
- return 0.0;
- }
- }
- // CHECK(0) << "unimplemented state: " << gx1 << " " << gy1 << " " << gx2 << " " << gy2;
- return 0.0;
- }
- double Box2d::distance_to(const Box2d& box) const
- {
- return Polygon2d(box).distance_to(*this);
- }
- bool Box2d::has_overlap(const Box2d& box) const
- {
- const double shift_x = box.center_x() - _center.x();
- const double shift_y = box.center_y() - _center.y();
- const double dx1 = _cos_heading * _half_length;
- const double dy1 = _sin_heading * _half_length;
- const double dx2 = _sin_heading * _half_width;
- const double dy2 = -_cos_heading * _half_width;
- const double dx3 = box.cos_heading() * box.half_length();
- const double dy3 = box.sin_heading() * box.half_length();
- const double dx4 = box.sin_heading() * box.half_width();
- const double dy4 = -box.cos_heading() * box.half_width();
- return std::abs(shift_x * _cos_heading + shift_y * _sin_heading) <= std::abs(dx3 * _cos_heading + dy3 * _sin_heading) + std::abs(dx4 * _cos_heading + dy4 * _sin_heading) + _half_length && std::abs(shift_x * _sin_heading - shift_y * _cos_heading) <= std::abs(dx3 * _sin_heading - dy3 * _cos_heading) + std::abs(dx4 * _sin_heading - dy4 * _cos_heading) + _half_width && std::abs(shift_x * box.cos_heading() + shift_y * box.sin_heading()) <= std::abs(dx1 * box.cos_heading() + dy1 * box.sin_heading()) + std::abs(dx2 * box.cos_heading() + dy2 * box.sin_heading()) + box.half_length() && std::abs(shift_x * box.sin_heading() - shift_y * box.cos_heading()) <= std::abs(dx1 * box.sin_heading() - dy1 * box.cos_heading()) + std::abs(dx2 * box.sin_heading() - dy2 * box.cos_heading()) + box.half_width();
- }
- AABox2d Box2d::get_aa_box() const
- {
- const double dx1 = std::abs(_cos_heading * _half_length);
- const double dy1 = std::abs(_sin_heading * _half_length);
- const double dx2 = std::abs(_sin_heading * _half_width);
- const double dy2 = std::abs(_cos_heading * _half_width);
- return AABox2d(_center, (dx1 + dx2) * 2.0, (dy1 + dy2) * 2.0);
- }
- void Box2d::rotate_from_center(double rotate_angle)
- {
- _heading = normalize_angle(_heading + rotate_angle);
- _cos_heading = cos(_heading);
- _sin_heading = sin(_heading);
- }
- void Box2d::shift(const Vec2d& shift_vec)
- {
- _center += shift_vec;
- }
- std::string Box2d::debug_string() const
- {
- std::ostringstream sout;
- sout << "box2d ( center = " << _center.debug_string()
- << " heading = " << _heading
- << " length = " << _length
- << " width = " << _width << " )";
- sout.flush();
- return sout.str();
- }
|