24#include "absl/strings/str_cat.h"
25#include "absl/strings/str_join.h"
41 : points_(
std::move(points)) {
50 double distance = std::numeric_limits<double>::infinity();
62 double distance_sqr = std::numeric_limits<double>::infinity();
80 return poly_seg.HasIntersect(line_segment);
108 double distance = std::numeric_limits<double>::infinity();
116 double distance = std::numeric_limits<double>::infinity();
127 [&](
const LineSegment2d &poly_seg) { return poly_seg.IsPointIn(point); });
165 for (
int i = 0; i < polygon.
num_points(); ++i) {
185 std::vector<LineSegment2d> overlaps =
GetAllOverlaps(line_segment);
186 double total_length = 0;
187 for (
const auto &overlap_seg : overlaps) {
188 total_length += overlap_seg.length();
206 return Contains(line_segment);
253 for (
const auto &point :
points_) {
262 Polygon2d *
const polygon,
bool check_area) {
263 CHECK_NOTNULL(polygon);
264 const int n =
static_cast<int>(
points.size());
268 std::vector<int> sorted_indices(n);
269 for (
int i = 0; i < n; ++i) {
270 sorted_indices[i] = i;
272 std::sort(sorted_indices.begin(), sorted_indices.end(),
273 [&](
const int idx1,
const int idx2) {
274 const Vec2d &pt1 = points[idx1];
275 const Vec2d &pt2 = points[idx2];
276 const double dx = pt1.x() - pt2.x();
277 if (std::abs(dx) > kMathEpsilon) {
280 return pt1.y() < pt2.y();
283 std::vector<int> results;
286 for (
int i = 0; i < n + n; ++i) {
290 const int idx = sorted_indices[(i < n) ? i : (n + n - 1 - i)];
291 const Vec2d &pt = points[idx];
292 while (count > last_count &&
293 CrossProd(points[results[count - 2]], points[results[count - 1]],
298 results.push_back(idx);
305 std::vector<Vec2d> result_points;
306 result_points.reserve(count);
307 for (
int i = 0; i < count; ++i) {
308 result_points.push_back(points[results[i]]);
310 *polygon = Polygon2d(result_points, check_area);
315 std::vector<Vec2d> *
const points) {
319 CHECK_NOTNULL(points);
320 const size_t n = points->size();
324 std::vector<double> prod(n);
325 std::vector<int> side(n);
326 for (
size_t i = 0; i < n; ++i) {
331 side[i] = ((prod[i] < 0) ? -1 : 1);
335 std::vector<Vec2d> new_points;
336 for (
size_t i = 0; i < n; ++i) {
338 new_points.push_back((*points)[i]);
340 const size_t j = ((i == n - 1) ? 0 : (i + 1));
341 if (side[i] * side[j] < 0) {
342 const double ratio = prod[j] / (prod[j] - prod[i]);
343 new_points.emplace_back(
344 (*points)[i].x() * ratio + (*points)[j].x() * (1.0 - ratio),
345 (*points)[i].y() * ratio + (*points)[j].y() * (1.0 - ratio));
349 points->swap(new_points);
350 return points->size() >= 3U;
353bool Polygon2d::ComputeOverlap(
const Polygon2d &other_polygon,
354 Polygon2d *
const overlap_polygon)
const {
355 CHECK_GE(points_.size(), 3U);
356 CHECK_NOTNULL(overlap_polygon);
358 std::vector<Vec2d> points = other_polygon.
points();
359 for (
int i = 0; i < num_points_; ++i) {
360 if (!ClipConvexHull(line_segments_[i], &points)) {
364 return ComputeConvexHull(points, overlap_polygon,
false);
367double Polygon2d::ComputeIoU(
const Polygon2d &other_polygon)
const {
369 if (!ComputeOverlap(other_polygon, &overlap_polygon)) {
372 double intersection_area = overlap_polygon.
area();
373 double union_area = area_ + other_polygon.
area() - overlap_polygon.
area();
374 return intersection_area / union_area;
378 CHECK_GE(points_.size(), 3U);
379 if ((line_segment.
start().
x() < min_x_ && line_segment.
end().
x() < min_x_) ||
380 (line_segment.
start().
x() > max_x_ && line_segment.
end().
x() > max_x_) ||
381 (line_segment.
start().
y() < min_y_ && line_segment.
end().
y() < min_y_) ||
382 (line_segment.
start().
y() > max_y_ && line_segment.
end().
y() > max_y_)) {
386 if (std::any_of(line_segments_.begin(), line_segments_.end(),
388 return poly_seg.HasIntersect(line_segment);
396 Vec2d *
const first,
Vec2d *
const last)
const {
397 CHECK_GE(points_.size(), 3U);
398 CHECK_NOTNULL(first);
402 if (!IsPointIn(line_segment.
start())) {
405 *first = line_segment.
start();
406 *last = line_segment.
start();
410 double min_proj = line_segment.
length();
412 if (IsPointIn(line_segment.
start())) {
413 *first = line_segment.
start();
416 if (IsPointIn(line_segment.
end())) {
417 *last = line_segment.
end();
418 max_proj = line_segment.
length();
420 for (
const auto &poly_seg : line_segments_) {
422 if (poly_seg.GetIntersect(line_segment, &pt)) {
424 if (proj < min_proj) {
428 if (proj > max_proj) {
437void Polygon2d::GetAllVertices(std::vector<Vec2d> *
const vertices)
const {
438 if (vertices ==
nullptr) {
444std::vector<Vec2d> Polygon2d::GetAllVertices()
const {
return points_; }
446std::vector<LineSegment2d> Polygon2d::GetAllOverlaps(
448 CHECK_GE(points_.size(), 3U);
451 std::vector<LineSegment2d> overlaps;
452 if (IsPointIn(line_segment.
start())) {
453 overlaps.push_back(line_segment);
457 std::vector<double> projections;
458 if (IsPointIn(line_segment.
start())) {
459 projections.push_back(0.0);
461 if (IsPointIn(line_segment.
end())) {
462 projections.push_back(line_segment.
length());
464 for (
const auto &poly_seg : line_segments_) {
466 if (poly_seg.GetIntersect(line_segment, &pt)) {
470 std::sort(projections.begin(), projections.end());
471 std::vector<std::pair<double, double>> overlaps;
472 for (
size_t i = 0; i + 1 < projections.size(); ++i) {
473 const double start_proj = projections[i];
474 const double end_proj = projections[i + 1];
478 const Vec2d reference_point =
479 line_segment.
start() +
481 if (!IsPointIn(reference_point)) {
484 if (overlaps.empty() ||
486 overlaps.emplace_back(start_proj, end_proj);
488 overlaps.back().second = end_proj;
491 std::vector<LineSegment2d> overlap_line_segments;
492 for (
const auto &overlap : overlaps) {
493 overlap_line_segments.emplace_back(
497 return overlap_line_segments;
500void Polygon2d::ExtremePoints(
const double heading,
Vec2d *
const first,
501 Vec2d *
const last)
const {
502 CHECK_GE(points_.size(), 3U);
503 CHECK_NOTNULL(first);
506 const Vec2d direction_vec = Vec2d::CreateUnitVec2d(heading);
507 double min_proj = std::numeric_limits<double>::infinity();
508 double max_proj = -std::numeric_limits<double>::infinity();
509 for (
const auto &pt : points_) {
510 const double proj = pt.InnerProd(direction_vec);
511 if (proj < min_proj) {
515 if (proj > max_proj) {
523 return AABox2d({min_x_, min_y_}, {max_x_, max_y_});
526Box2d Polygon2d::BoundingBoxWithHeading(
const double heading)
const {
527 CHECK_GE(points_.size(), 3U);
528 const Vec2d direction_vec = Vec2d::CreateUnitVec2d(heading);
533 ExtremePoints(heading, &px1, &px2);
534 ExtremePoints(heading - M_PI_2, &py1, &py2);
535 const double x1 = px1.
InnerProd(direction_vec);
536 const double x2 = px2.
InnerProd(direction_vec);
537 const double y1 = py1.
CrossProd(direction_vec);
538 const double y2 = py2.
CrossProd(direction_vec);
540 (x1 + x2) / 2.0 * direction_vec +
541 (y1 + y2) / 2.0 *
Vec2d(direction_vec.
y(), -direction_vec.
x()),
542 heading, x2 - x1, y2 - y1);
545Box2d Polygon2d::MinAreaBoundingBox()
const {
546 CHECK_GE(points_.size(), 3U);
549 ComputeConvexHull(points_, &convex_polygon);
553 double min_area = std::numeric_limits<double>::infinity();
554 double min_area_at_heading = 0.0;
558 for (
int i = 0; i < num_points_; ++i) {
559 const auto &line_segment = line_segments_[i];
561 double min_proj = line_segment.ProjectOntoUnit(points_[left_most]);
562 while ((proj = line_segment.ProjectOntoUnit(points_[Prev(left_most)])) <
565 left_most = Prev(left_most);
567 while ((proj = line_segment.ProjectOntoUnit(points_[Next(left_most)])) <
570 left_most = Next(left_most);
572 double max_proj = line_segment.ProjectOntoUnit(points_[right_most]);
573 while ((proj = line_segment.ProjectOntoUnit(points_[Prev(right_most)])) >
576 right_most = Prev(right_most);
578 while ((proj = line_segment.ProjectOntoUnit(points_[Next(right_most)])) >
581 right_most = Next(right_most);
584 double max_prod = line_segment.ProductOntoUnit(points_[top_most]);
585 while ((prod = line_segment.ProductOntoUnit(points_[Prev(top_most)])) >
588 top_most = Prev(top_most);
590 while ((prod = line_segment.ProductOntoUnit(points_[Next(top_most)])) >
593 top_most = Next(top_most);
595 const double area = max_prod * (max_proj - min_proj);
596 if (area < min_area) {
598 min_area_at_heading = line_segment.heading();
601 return BoundingBoxWithHeading(min_area_at_heading);
604Polygon2d Polygon2d::ExpandByDistance(
const double distance)
const {
607 ComputeConvexHull(points_, &convex_polygon);
611 const double kMinAngle = 0.1;
612 std::vector<Vec2d> points;
613 for (
int i = 0; i < num_points_; ++i) {
614 const double start_angle = line_segments_[Prev(i)].heading() - M_PI_2;
615 const double end_angle = line_segments_[i].heading() - M_PI_2;
616 const double diff =
WrapAngle(end_angle - start_angle);
618 points.push_back(points_[i] +
619 Vec2d::CreateUnitVec2d(start_angle) * distance);
621 const int count =
static_cast<int>(diff / kMinAngle) + 1;
622 for (
int k = 0; k <= count; ++k) {
623 const double angle = start_angle + diff *
static_cast<double>(k) /
624 static_cast<double>(count);
625 points.push_back(points_[i] + Vec2d::CreateUnitVec2d(angle) * distance);
630 ACHECK(ComputeConvexHull(points, &new_polygon));
634Polygon2d Polygon2d::PolygonExpandByDistance(
const double distance)
const {
637 ComputeConvexHull(points_, &convex_polygon);
641 std::vector<Vec2d> points;
642 for (
int i = 0; i < num_points_; ++i) {
643 double v1x = points_[Prev(i)].x() - points_[i].x();
644 double v1y = points_[Prev(i)].y() - points_[i].y();
645 double n1 = std::sqrt(v1x * v1x + v1y * v1y);
649 double v2x = points_[Next(i)].x() - points_[i].x();
650 double v2y = points_[Next(i)].y() - points_[i].y();
651 double n2 = std::sqrt(v2x * v2x + v2y * v2y);
655 double l = distance / sqrt((1 - (v1x * v2x + v1y * v2y)) / 2);
657 double vx = v1x + v2x;
658 double vy = v1y + v2y;
659 double n = l / sqrt(vx * vx + vy * vy);
664 Vec2d point(vx + points_[i].x(), vy + points_[i].y());
665 points.push_back(point);
668 ACHECK(ComputeConvexHull(points, &new_polygon));
672void Polygon2d::CalculateVertices(
const Vec2d &shift_vec) {
673 for (
size_t i = 0; i < num_points_; ++i) {
674 points_[i] += shift_vec;
676 for (
auto &point : points_) {
677 max_x_ = std::fmax(point.x(), max_x_);
678 min_x_ = std::fmin(point.x(), min_x_);
679 max_y_ = std::fmax(point.y(), max_y_);
680 min_y_ = std::fmin(point.y(), min_y_);
685 double min_length_of_line_segment = 10e3;
687 for (
auto &line_segment_ : line_segments_) {
688 if (line_segment_.length() < min_length_of_line_segment) {
689 line_segment = line_segment_;
695std::string Polygon2d::DebugString()
const {
696 return absl::StrCat(
"polygon2d ( num_points = ", num_points_,
" points = (",
698 " ) ", is_convex_ ?
"convex" :
"non-convex",
699 " area = ", area_,
" )");
Implements a class of (undirected) axes-aligned bounding boxes in 2-D.
Rectangular (undirected) bounding box in 2-D.
void GetAllCorners(std::vector< Vec2d > *const corners) const
Getter of the corners of the box
const Vec2d & end() const
Get the end point.
const Vec2d & unit_direction() const
Get the unit direction from the start point to the end point.
double ProjectOntoUnit(const Vec2d &point) const
Compute the projection of a vector onto the line segment.
double length() const
Get the length of the line segment.
double DistanceTo(const Vec2d &point) const
Compute the shortest distance from a point on the line segment to a point in 2-D.
const Vec2d & start() const
Get the start point.
Vec2d center() const
Get the center of the line segment.
The class of polygon in 2-D.
std::vector< LineSegment2d > GetAllOverlaps(const LineSegment2d &line_segment) const
Get all overlapped line segments of a line segment and this polygon.
bool HasOverlap(const LineSegment2d &line_segment) const
Check if a line segment has overlap with this polygon.
std::vector< Vec2d > points_
bool IsPointIn(const Vec2d &point) const
Check if a point is within the polygon.
static bool ComputeConvexHull(const std::vector< Vec2d > &points, Polygon2d *const polygon, bool check_area=true)
Compute the convex hull of a group of points.
bool Contains(const LineSegment2d &line_segment) const
Check if the polygon contains a line segment.
Polygon2d()=default
Empty constructor.
Polygon2d ExpandByDistance(const double distance) const
Expand this polygon by a distance.
Box2d MinAreaBoundingBox() const
Get the bounding box with the minimal area.
double DistanceTo(const Vec2d &point) const
Compute the distance from a point to the polygon.
const std::vector< LineSegment2d > & line_segments() const
Get the edges of the polygon.
const std::vector< Vec2d > & points() const
Get the vertices of the polygon.
double DistanceSquareTo(const Vec2d &point) const
Compute the square of distance from a point to the polygon.
void BuildFromPoints(bool check_area=true)
std::vector< LineSegment2d > line_segments_
bool IsPointOnBoundary(const Vec2d &point) const
Check if a point is on the boundary of the polygon.
int num_points() const
Get the number of vertices of the polygon.
Polygon2d PolygonExpandByDistance(const double distance) const
bool is_convex() const
Check if the polygon is convex.
double area() const
Get the area of the polygon.
double DistanceToBoundary(const Vec2d &point) const
Compute the distance from a point to the boundary of the polygon.
Implements a class of 2-dimensional vectors.
double InnerProd(const Vec2d &other) const
Returns the inner product between these two Vec2d.
double y() const
Getter for y component
double x() const
Getter for x component
double CrossProd(const Vec2d &other) const
Returns the "cross" product between these two Vec2d (non-standard).
Math-related util functions.
constexpr double kMathEpsilon
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.
double WrapAngle(const double angle)
Wrap angle to [0, 2 * PI).
Define the Polygon2d class.
Some string util functions.