diff --git a/common/autoware_universe_utils/include/autoware/universe_utils/geometry/alt_geometry.hpp b/common/autoware_universe_utils/include/autoware/universe_utils/geometry/alt_geometry.hpp index f9ee6e1beeefa..bf9e622849377 100644 --- a/common/autoware_universe_utils/include/autoware/universe_utils/geometry/alt_geometry.hpp +++ b/common/autoware_universe_utils/include/autoware/universe_utils/geometry/alt_geometry.hpp @@ -151,20 +151,26 @@ class ConvexPolygon2d : public Polygon2d }; } // namespace alt -double area(const alt::ConvexPolygon2d & poly); +double area(const alt::PointList2d & ring); + +double area(const alt::Polygon2d & poly); std::optional convex_hull(const alt::Points2d & points); void correct(alt::Polygon2d & poly); -bool covered_by(const alt::Point2d & point, const alt::ConvexPolygon2d & poly); +bool covered_by(const alt::Point2d & point, const alt::PointList2d & ring); + +bool covered_by(const alt::Point2d & point, const alt::Polygon2d & poly); + +bool disjoint(const alt::Polygon2d & poly1, const alt::Polygon2d & poly2); bool disjoint(const alt::ConvexPolygon2d & poly1, const alt::ConvexPolygon2d & poly2); double distance( const alt::Point2d & point, const alt::Point2d & seg_start, const alt::Point2d & seg_end); -double distance(const alt::Point2d & point, const alt::ConvexPolygon2d & poly); +double distance(const alt::Point2d & point, const alt::Polygon2d & poly); std::optional envelope(const alt::Polygon2d & poly); @@ -179,12 +185,14 @@ bool intersects( const alt::Point2d & seg1_start, const alt::Point2d & seg1_end, const alt::Point2d & seg2_start, const alt::Point2d & seg2_end); +bool intersects(const alt::Polygon2d & poly1, const alt::Polygon2d & poly2); + bool intersects(const alt::ConvexPolygon2d & poly1, const alt::ConvexPolygon2d & poly2); bool is_above( const alt::Point2d & point, const alt::Point2d & seg_start, const alt::Point2d & seg_end); -bool is_clockwise(const alt::PointList2d & vertices); +bool is_clockwise(const alt::PointList2d & ring); bool is_convex(const alt::Polygon2d & poly); @@ -193,12 +201,15 @@ alt::PointList2d simplify(const alt::PointList2d & line, const double max_distan bool touches( const alt::Point2d & point, const alt::Point2d & seg_start, const alt::Point2d & seg_end); -bool touches(const alt::Point2d & point, const alt::ConvexPolygon2d & poly); +bool touches(const alt::Point2d & point, const alt::PointList2d & line); + +bool touches(const alt::Point2d & point, const alt::Polygon2d & poly); + +bool within(const alt::Point2d & point, const alt::PointList2d & ring); -bool within(const alt::Point2d & point, const alt::ConvexPolygon2d & poly); +bool within(const alt::Point2d & point, const alt::Polygon2d & poly); -bool within( - const alt::ConvexPolygon2d & poly_contained, const alt::ConvexPolygon2d & poly_containing); +bool within(const alt::Polygon2d & poly_contained, const alt::Polygon2d & poly_containing); } // namespace autoware::universe_utils #endif // AUTOWARE__UNIVERSE_UTILS__GEOMETRY__ALT_GEOMETRY_HPP_ diff --git a/common/autoware_universe_utils/src/geometry/alt_geometry.cpp b/common/autoware_universe_utils/src/geometry/alt_geometry.cpp index 6c060b03b4543..65f363996a7c2 100644 --- a/common/autoware_universe_utils/src/geometry/alt_geometry.cpp +++ b/common/autoware_universe_utils/src/geometry/alt_geometry.cpp @@ -140,16 +140,26 @@ std::optional ConvexPolygon2d::create( } } // namespace alt -double area(const alt::ConvexPolygon2d & poly) +double area(const alt::PointList2d & ring) { - const auto & vertices = poly.vertices(); + double area_2 = 0.; + for (auto it = ring.cbegin(); it != std::prev(ring.cend()); ++it) { + area_2 += (*std::next(it)).cross(*it); + } + + return area_2 / 2; +} - double area = 0.; - for (auto it = std::next(vertices.cbegin()); it != std::prev(vertices.cend(), 2); ++it) { - area += (*std::next(it) - vertices.front()).cross(*it - vertices.front()) / 2; +double area(const alt::Polygon2d & poly) +{ + const auto outer_area = area(poly.outer()); + + double inner_area = 0.; + for (const auto & inner : poly.inners()) { + inner_area += area(inner); } - return area; + return outer_area - inner_area; } std::optional convex_hull(const alt::Points2d & points) @@ -241,22 +251,21 @@ void correct(alt::Polygon2d & poly) } } -bool covered_by(const alt::Point2d & point, const alt::ConvexPolygon2d & poly) +bool covered_by(const alt::Point2d & point, const alt::PointList2d & ring) { constexpr double epsilon = 1e-6; - const auto & vertices = poly.vertices(); std::size_t winding_number = 0; const auto [y_min_vertex, y_max_vertex] = std::minmax_element( - vertices.begin(), std::prev(vertices.end()), + ring.begin(), std::prev(ring.end()), [](const auto & a, const auto & b) { return a.y() < b.y(); }); if (point.y() < y_min_vertex->y() || point.y() > y_max_vertex->y()) { return false; } double cross; - for (auto it = vertices.cbegin(); it != std::prev(vertices.cend()); ++it) { + for (auto it = ring.cbegin(); it != std::prev(ring.cend()); ++it) { const auto & p1 = *it; const auto & p2 = *std::next(it); @@ -284,23 +293,25 @@ bool covered_by(const alt::Point2d & point, const alt::ConvexPolygon2d & poly) return winding_number != 0; } -bool disjoint(const alt::ConvexPolygon2d & poly1, const alt::ConvexPolygon2d & poly2) +bool covered_by(const alt::Point2d & point, const alt::Polygon2d & poly) { - if (equals(poly1, poly2)) { - return false; - } - - if (intersects(poly1, poly2)) { - return false; - } - - for (const auto & vertex : poly1.vertices()) { - if (touches(vertex, poly2)) { + for (const auto & inner : poly.inners()) { + if (within(point, inner)) { return false; } } - return true; + return covered_by(point, poly.outer()); +} + +bool disjoint(const alt::Polygon2d & poly1, const alt::Polygon2d & poly2) +{ + return !intersects(poly1, poly2); +} + +bool disjoint(const alt::ConvexPolygon2d & poly1, const alt::ConvexPolygon2d & poly2) +{ + return !intersects(poly1, poly2); } double distance( @@ -323,14 +334,14 @@ double distance( } } -double distance(const alt::Point2d & point, const alt::ConvexPolygon2d & poly) +double distance(const alt::Point2d & point, const alt::Polygon2d & poly) { - if (covered_by(point, poly)) { + if (covered_by(point, poly.outer())) { return 0.0; } // TODO(mitukou1109): Use plane sweep method to improve performance? - const auto & vertices = poly.vertices(); + const auto & vertices = poly.outer(); double min_distance = std::numeric_limits::max(); for (auto it = vertices.cbegin(); it != std::prev(vertices.cend()); ++it) { min_distance = std::min(min_distance, distance(point, *it, *std::next(it))); @@ -414,10 +425,56 @@ bool intersects( return true; } +bool intersects(const alt::Polygon2d & poly1, const alt::Polygon2d & poly2) +{ + // TODO(mitukou1109): Use plane sweep method to improve performance + for (const auto & vertex : poly1.outer()) { + if (within(vertex, poly2)) { + return true; + } + } + + for (const auto & vertex : poly2.outer()) { + if (within(vertex, poly1)) { + return true; + } + } + + for (auto it1 = poly1.outer().cbegin(); it1 != std::prev(poly1.outer().cend()); ++it1) { + for (auto it2 = poly2.outer().cbegin(); it2 != std::prev(poly2.outer().cend()); ++it2) { + if (intersects(*it1, *std::next(it1), *it2, *std::next(it2))) { + return true; + } + } + + for (const auto & inner : poly2.inners()) { + for (auto it2 = inner.cbegin(); it2 != std::prev(inner.cend()); ++it2) { + if (intersects(*it1, *std::next(it1), *it2, *std::next(it2))) { + return true; + } + } + } + } + + for (const auto & inner : poly1.inners()) { + for (auto it1 = inner.cbegin(); it1 != std::prev(inner.cend()); ++it1) { + for (auto it2 = poly2.outer().cbegin(); it2 != std::prev(poly2.outer().cend()); ++it2) { + if (intersects(*it1, *std::next(it1), *it2, *std::next(it2))) { + return true; + } + } + } + } + + return false; +} + bool intersects(const alt::ConvexPolygon2d & poly1, const alt::ConvexPolygon2d & poly2) { - if (equals(poly1, poly2)) { - return true; + for (const auto & vertex : poly1.vertices()) { + if (touches(vertex, poly2)) { + return true; + } } // GJK algorithm @@ -474,10 +531,10 @@ bool is_above( return (seg_end - seg_start).cross(point - seg_start) > 0; } -bool is_clockwise(const alt::PointList2d & vertices) +bool is_clockwise(const alt::PointList2d & ring) { double sum = 0.; - for (auto it = vertices.cbegin(); it != std::prev(vertices.cend()); ++it) { + for (auto it = ring.cbegin(); it != std::prev(ring.cend()); ++it) { sum += (std::next(it)->x() - it->x()) * (std::next(it)->y() + it->y()); } @@ -557,19 +614,17 @@ bool touches( return std::abs(start_vec.cross(end_vec)) < epsilon && start_vec.dot(end_vec) <= 0; } -bool touches(const alt::Point2d & point, const alt::ConvexPolygon2d & poly) +bool touches(const alt::Point2d & point, const alt::PointList2d & line) { - const auto & vertices = poly.vertices(); - const auto [y_min_vertex, y_max_vertex] = std::minmax_element( - vertices.begin(), std::prev(vertices.end()), + line.begin(), std::prev(line.end()), [](const auto & a, const auto & b) { return a.y() < b.y(); }); if (point.y() < y_min_vertex->y() || point.y() > y_max_vertex->y()) { return false; } - for (auto it = vertices.cbegin(); it != std::prev(vertices.cend()); ++it) { - // check if the point is on each edge of the polygon + for (auto it = line.cbegin(); it != std::prev(line.cend()); ++it) { + // check if the point is on each segment if (touches(point, *it, *std::next(it))) { return true; } @@ -578,22 +633,36 @@ bool touches(const alt::Point2d & point, const alt::ConvexPolygon2d & poly) return false; } -bool within(const alt::Point2d & point, const alt::ConvexPolygon2d & poly) +bool touches(const alt::Point2d & point, const alt::Polygon2d & poly) +{ + if (touches(point, poly.outer())) { + return true; + } + + for (const auto & inner : poly.inners()) { + if (touches(point, inner)) { + return true; + } + } + + return false; +} + +bool within(const alt::Point2d & point, const alt::PointList2d & ring) { constexpr double epsilon = 1e-6; - const auto & vertices = poly.vertices(); int64_t winding_number = 0; const auto [y_min_vertex, y_max_vertex] = std::minmax_element( - vertices.begin(), std::prev(vertices.end()), + ring.begin(), std::prev(ring.end()), [](const auto & a, const auto & b) { return a.y() < b.y(); }); if (point.y() <= y_min_vertex->y() || point.y() >= y_max_vertex->y()) { return false; } double cross; - for (auto it = vertices.cbegin(); it != std::prev(vertices.cend()); ++it) { + for (auto it = ring.cbegin(); it != std::prev(ring.cend()); ++it) { const auto & p1 = *it; const auto & p2 = *std::next(it); @@ -618,23 +687,45 @@ bool within(const alt::Point2d & point, const alt::ConvexPolygon2d & poly) } } - return winding_number != 0; + if (winding_number == 0) { + return false; + } + + return true; +} + +bool within(const alt::Point2d & point, const alt::Polygon2d & poly) +{ + for (const auto & inner : poly.inners()) { + if (covered_by(point, inner)) { + return false; + } + } + + return within(point, poly.outer()); } -bool within( - const alt::ConvexPolygon2d & poly_contained, const alt::ConvexPolygon2d & poly_containing) +bool within(const alt::Polygon2d & poly_contained, const alt::Polygon2d & poly_containing) { if (equals(poly_contained, poly_containing)) { return true; } // check if all points of poly_contained are within poly_containing - for (const auto & vertex : poly_contained.vertices()) { + for (const auto & vertex : poly_contained.outer()) { if (!within(vertex, poly_containing)) { return false; } } + for (const auto & inner : poly_contained.inners()) { + for (const auto & vertex : inner) { + if (!within(vertex, poly_containing)) { + return false; + } + } + } + return true; } } // namespace autoware::universe_utils diff --git a/common/autoware_universe_utils/test/src/geometry/test_alt_geometry.cpp b/common/autoware_universe_utils/test/src/geometry/test_alt_geometry.cpp index 90dfb1ede4701..973851d6b6415 100644 --- a/common/autoware_universe_utils/test/src/geometry/test_alt_geometry.cpp +++ b/common/autoware_universe_utils/test/src/geometry/test_alt_geometry.cpp @@ -13,6 +13,7 @@ // limitations under the License. #include "autoware/universe_utils/geometry/alt_geometry.hpp" +#include "autoware/universe_utils/geometry/random_concave_polygon.hpp" #include "autoware/universe_utils/geometry/random_convex_polygon.hpp" #include "autoware/universe_utils/system/stop_watch.hpp" @@ -33,14 +34,15 @@ TEST(alt_geometry, area) { using autoware::universe_utils::area; using autoware::universe_utils::alt::ConvexPolygon2d; - using autoware::universe_utils::alt::Point2d; + using autoware::universe_utils::alt::PointList2d; { - const Point2d p1 = {0.0, 0.0}; - const Point2d p2 = {0.0, 7.0}; - const Point2d p3 = {4.0, 2.0}; - const Point2d p4 = {2.0, 0.0}; - const auto result = area(ConvexPolygon2d::create({p1, p2, p3, p4}).value()); + PointList2d vertices; + vertices.push_back({0.0, 0.0}); + vertices.push_back({0.0, 7.0}); + vertices.push_back({4.0, 2.0}); + vertices.push_back({2.0, 0.0}); + const auto result = area(ConvexPolygon2d::create(vertices).value()); EXPECT_NEAR(result, 16.0, epsilon); } @@ -128,36 +130,103 @@ TEST(alt_geometry, coveredBy) using autoware::universe_utils::covered_by; using autoware::universe_utils::alt::ConvexPolygon2d; using autoware::universe_utils::alt::Point2d; + using autoware::universe_utils::alt::PointList2d; + using autoware::universe_utils::alt::Polygon2d; { // The point is within the polygon const Point2d point = {0.0, 0.0}; - const Point2d p1 = {1.0, 1.0}; - const Point2d p2 = {1.0, -1.0}; - const Point2d p3 = {-1.0, -1.0}; - const Point2d p4 = {-1.0, 1.0}; - const auto result = covered_by(point, ConvexPolygon2d::create({p1, p2, p3, p4}).value()); + + PointList2d vertices; + vertices.push_back({1.0, 1.0}); + vertices.push_back({1.0, -1.0}); + vertices.push_back({-1.0, -1.0}); + vertices.push_back({-1.0, 1.0}); + + const auto result = covered_by(point, ConvexPolygon2d::create(vertices).value()); EXPECT_TRUE(result); } { // The point is outside the polygon const Point2d point = {0.0, 0.0}; - const Point2d p1 = {2.0, 2.0}; - const Point2d p2 = {2.0, 1.0}; - const Point2d p3 = {1.0, 1.0}; - const Point2d p4 = {1.0, 2.0}; - const auto result = covered_by(point, ConvexPolygon2d::create({p1, p2, p3, p4}).value()); + + PointList2d vertices; + vertices.push_back({2.0, 2.0}); + vertices.push_back({2.0, 1.0}); + vertices.push_back({1.0, 1.0}); + vertices.push_back({1.0, 2.0}); + + const auto result = covered_by(point, ConvexPolygon2d::create(vertices).value()); EXPECT_FALSE(result); } { // The point is on the edge of the polygon const Point2d point = {0.0, 0.0}; - const Point2d p1 = {2.0, 1.0}; - const Point2d p2 = {2.0, -1.0}; - const Point2d p3 = {0.0, -1.0}; - const Point2d p4 = {0.0, 1.0}; - const auto result = covered_by(point, ConvexPolygon2d::create({p1, p2, p3, p4}).value()); + + PointList2d vertices; + vertices.push_back({2.0, 1.0}); + vertices.push_back({2.0, -1.0}); + vertices.push_back({0.0, -1.0}); + vertices.push_back({0.0, 1.0}); + + const auto result = covered_by(point, ConvexPolygon2d::create(vertices).value()); + + EXPECT_TRUE(result); + } + + { // The point is within the concave polygon + PointList2d outer; + outer.push_back({1.0, 1.0}); + outer.push_back({1.0, -1.0}); + outer.push_back({0.0, -0.5}); + outer.push_back({-1.0, -1.0}); + outer.push_back({-1.0, 1.0}); + outer.push_back({0.0, 0.5}); + + const Point2d point = {0.0, 0.0}; + + const auto result = covered_by(point, Polygon2d::create(outer, {}).value()); + + EXPECT_TRUE(result); + } + + { // The point is within the hole of the polygon + PointList2d outer; + outer.push_back({0.0, 0.0}); + outer.push_back({0.0, 2.0}); + outer.push_back({2.0, 2.0}); + outer.push_back({2.0, 0.0}); + + PointList2d inner; + inner.push_back({0.5, 0.5}); + inner.push_back({0.5, 1.5}); + inner.push_back({1.5, 1.5}); + inner.push_back({1.5, 0.5}); + + const Point2d point = {1.0, 1.0}; + + const auto result = covered_by(point, Polygon2d::create(outer, {inner}).value()); + + EXPECT_FALSE(result); + } + + { // The point is on the edge of the hole of the polygon + PointList2d outer; + outer.push_back({0.0, 0.0}); + outer.push_back({0.0, 2.0}); + outer.push_back({2.0, 2.0}); + outer.push_back({2.0, 0.0}); + + PointList2d inner; + inner.push_back({0.5, 0.5}); + inner.push_back({0.5, 1.5}); + inner.push_back({1.5, 1.5}); + inner.push_back({1.5, 0.5}); + + const Point2d point = {0.5, 1.0}; + + const auto result = covered_by(point, Polygon2d::create(outer, {inner}).value()); EXPECT_TRUE(result); } @@ -167,36 +236,93 @@ TEST(alt_geometry, disjoint) { using autoware::universe_utils::disjoint; using autoware::universe_utils::alt::ConvexPolygon2d; - using autoware::universe_utils::alt::Point2d; + using autoware::universe_utils::alt::PointList2d; + using autoware::universe_utils::alt::Polygon2d; { // Disjoint - const Point2d p1 = {0.0, 2.0}; - const Point2d p2 = {-2.0, 0.0}; - const Point2d p3 = {-4.0, 2.0}; - const Point2d p4 = {-2.0, 4.0}; - const Point2d p5 = {2.0, 2.0}; - const Point2d p6 = {4.0, 4.0}; - const Point2d p7 = {6.0, 2.0}; - const Point2d p8 = {4.0, 0.0}; + PointList2d vertices1; + vertices1.push_back({0.0, 2.0}); + vertices1.push_back({-2.0, 0.0}); + vertices1.push_back({-4.0, 2.0}); + vertices1.push_back({-2.0, 4.0}); + + PointList2d vertices2; + vertices2.push_back({2.0, 2.0}); + vertices2.push_back({4.0, 4.0}); + vertices2.push_back({6.0, 2.0}); + vertices2.push_back({4.0, 0.0}); + const auto result = disjoint( - ConvexPolygon2d::create({p1, p2, p3, p4}).value(), - ConvexPolygon2d::create({p5, p6, p7, p8}).value()); + ConvexPolygon2d::create(vertices1).value(), ConvexPolygon2d::create(vertices2).value()); EXPECT_TRUE(result); } { // Not disjoint (two polygons share a vertex) - const Point2d p1 = {0.0, 2.0}; - const Point2d p2 = {-2.0, 0.0}; - const Point2d p3 = {-4.0, 2.0}; - const Point2d p4 = {-2.0, 4.0}; - const Point2d p5 = {0.0, 2.0}; - const Point2d p6 = {2.0, 4.0}; - const Point2d p7 = {4.0, 2.0}; - const Point2d p8 = {2.0, 0.0}; + PointList2d vertices1; + vertices1.push_back({0.0, 2.0}); + vertices1.push_back({-2.0, 0.0}); + vertices1.push_back({-4.0, 2.0}); + vertices1.push_back({-2.0, 4.0}); + + PointList2d vertices2; + vertices2.push_back({0.0, 2.0}); + vertices2.push_back({2.0, 4.0}); + vertices2.push_back({4.0, 2.0}); + vertices2.push_back({2.0, 0.0}); + const auto result = disjoint( - ConvexPolygon2d::create({p1, p2, p3, p4}).value(), - ConvexPolygon2d::create({p5, p6, p7, p8}).value()); + ConvexPolygon2d::create(vertices1).value(), ConvexPolygon2d::create(vertices2).value()); + + EXPECT_FALSE(result); + } + + { // Disjoint (one concave polygon is within the hole of the other) + PointList2d outer1; + outer1.push_back({-1.0, -1.0}); + outer1.push_back({-1.0, 3.0}); + outer1.push_back({3.0, 3.0}); + outer1.push_back({3.0, -1.0}); + + PointList2d inner1; + inner1.push_back({0.0, 0.0}); + inner1.push_back({0.0, 2.0}); + inner1.push_back({1.0, 1.0}); + inner1.push_back({2.0, 2.0}); + inner1.push_back({2.0, 0.0}); + + PointList2d outer2; + outer2.push_back({0.5, 0.5}); + outer2.push_back({0.5, 1.0}); + outer2.push_back({1.0, 0.5}); + + const auto result = + disjoint(Polygon2d::create(outer1, {inner1}).value(), Polygon2d::create(outer2, {}).value()); + + EXPECT_TRUE(result); + } + + { // Not disjoint (one concave polygon and the hole of the other share a vertex) + PointList2d outer1; + outer1.push_back({-1.0, -1.0}); + outer1.push_back({-1.0, 3.0}); + outer1.push_back({3.0, 3.0}); + outer1.push_back({3.0, -1.0}); + + PointList2d inner1; + inner1.push_back({0.0, 0.0}); + inner1.push_back({0.0, 2.0}); + inner1.push_back({1.0, 1.0}); + inner1.push_back({2.0, 2.0}); + inner1.push_back({2.0, 0.0}); + + PointList2d outer2; + outer2.push_back({1.0, 1.0}); + outer2.push_back({1.5, 0.5}); + outer2.push_back({0.5, 0.5}); + + const auto result = + disjoint(Polygon2d::create(outer1, {inner1}).value(), Polygon2d::create(outer2, {}).value()); EXPECT_FALSE(result); } @@ -207,6 +333,8 @@ TEST(alt_geometry, distance) using autoware::universe_utils::distance; using autoware::universe_utils::alt::ConvexPolygon2d; using autoware::universe_utils::alt::Point2d; + using autoware::universe_utils::alt::PointList2d; + using autoware::universe_utils::alt::Polygon2d; { // Normal setting const Point2d p = {0.0, 1.0}; @@ -277,23 +405,67 @@ TEST(alt_geometry, distance) } { // The point is outside the polygon - const Point2d p = {0.0, 0.0}; - const Point2d p1 = {3.0, 1.0}; - const Point2d p2 = {3.0, -1.0}; - const Point2d p3 = {1.0, -1.0}; - const Point2d p4 = {1.0, 1.0}; - const auto result = distance(p, ConvexPolygon2d::create({p1, p2, p3, p4}).value()); + const Point2d point = {0.0, 0.0}; + + PointList2d vertices; + vertices.push_back({3.0, 1.0}); + vertices.push_back({3.0, -1.0}); + vertices.push_back({1.0, -1.0}); + vertices.push_back({1.0, 1.0}); + + const auto result = distance(point, ConvexPolygon2d::create(vertices).value()); EXPECT_NEAR(result, 1.0, epsilon); } { // The point is within the polygon - const Point2d p = {0.0, 0.0}; - const Point2d p1 = {2.0, 1.0}; - const Point2d p2 = {2.0, -1.0}; - const Point2d p3 = {-1.0, -1.0}; - const Point2d p4 = {-1.0, 1.0}; - const auto result = distance(p, ConvexPolygon2d::create({p1, p2, p3, p4}).value()); + const Point2d point = {0.0, 0.0}; + + PointList2d vertices; + vertices.push_back({2.0, 1.0}); + vertices.push_back({2.0, -1.0}); + vertices.push_back({-1.0, -1.0}); + vertices.push_back({-1.0, 1.0}); + + const auto result = distance(point, ConvexPolygon2d::create(vertices).value()); + + EXPECT_NEAR(result, 0.0, epsilon); + } + + { // The point is outside the concave polygon + PointList2d outer; + outer.push_back({0.0, 0.0}); + outer.push_back({0.0, 2.0}); + outer.push_back({1.0, 1.0}); + outer.push_back({2.0, 2.0}); + outer.push_back({2.0, 0.0}); + outer.push_back({0.0, 0.0}); + + const Point2d point = {1.0, 2.0}; + + const auto result = distance(point, Polygon2d::create(outer, {}).value()); + + EXPECT_NEAR(result, std::pow(2, -0.5), epsilon); + } + + { // The point is inside the hole of the polygon + PointList2d outer; + outer.push_back({0.0, 0.0}); + outer.push_back({0.0, 2.0}); + outer.push_back({2.0, 2.0}); + outer.push_back({2.0, 0.0}); + outer.push_back({0.0, 0.0}); + + PointList2d inner; + inner.push_back({0.5, 0.5}); + inner.push_back({0.5, 1.5}); + inner.push_back({1.5, 1.5}); + inner.push_back({1.5, 0.5}); + inner.push_back({0.5, 0.5}); + + const Point2d point = {1.0, 1.0}; + + const auto result = distance(point, Polygon2d::create(outer, {inner}).value()); EXPECT_NEAR(result, 0.0, epsilon); } @@ -306,23 +478,28 @@ TEST(geometry, envelope) using autoware::universe_utils::alt::Polygon2d; { - const auto poly = Polygon2d::create( - PointList2d{ - {2.0, 1.3}, - {2.4, 1.7}, - {2.8, 1.8}, - {3.4, 1.2}, - {3.7, 1.6}, - {3.4, 2.0}, - {4.1, 3.0}, - {5.3, 2.6}, - {5.4, 1.2}, - {4.9, 0.8}, - {2.9, 0.7}, - {2.0, 1.3}}, - {PointList2d{{4.0, 2.0}, {4.2, 1.4}, {4.8, 1.9}, {4.4, 2.2}, {4.0, 2.0}}}) - .value(); - const auto result = envelope(poly); + PointList2d outer; + outer.push_back({2.0, 1.3}); + outer.push_back({2.4, 1.7}); + outer.push_back({2.8, 1.8}); + outer.push_back({3.4, 1.2}); + outer.push_back({3.7, 1.6}); + outer.push_back({3.4, 2.0}); + outer.push_back({4.1, 3.0}); + outer.push_back({5.3, 2.6}); + outer.push_back({5.4, 1.2}); + outer.push_back({4.9, 0.8}); + outer.push_back({2.9, 0.7}); + outer.push_back({2.0, 1.3}); + + PointList2d inner; + inner.push_back({4.0, 2.0}); + inner.push_back({4.2, 1.4}); + inner.push_back({4.8, 1.9}); + inner.push_back({4.4, 2.2}); + inner.push_back({4.0, 2.0}); + + const auto result = envelope(Polygon2d::create(outer, {inner}).value()); ASSERT_TRUE(result); @@ -342,6 +519,8 @@ TEST(alt_geometry, intersects) using autoware::universe_utils::intersects; using autoware::universe_utils::alt::ConvexPolygon2d; using autoware::universe_utils::alt::Point2d; + using autoware::universe_utils::alt::PointList2d; + using autoware::universe_utils::alt::Polygon2d; { // Normally crossing const Point2d p1 = {0.0, -1.0}; @@ -434,52 +613,215 @@ TEST(alt_geometry, intersects) } { // One polygon intersects the other - const Point2d p1 = {1.0, 1.0}; - const Point2d p2 = {1.0, -1.0}; - const Point2d p3 = {-1.0, -1.0}; - const Point2d p4 = {-1.0, 1.0}; - const Point2d p5 = {2.0, 2.0}; - const Point2d p6 = {2.0, 0.0}; - const Point2d p7 = {0.0, 0.0}; - const Point2d p8 = {0.0, 2.0}; + PointList2d vertices1; + vertices1.push_back({1.0, 1.0}); + vertices1.push_back({1.0, -1.0}); + vertices1.push_back({-1.0, -1.0}); + vertices1.push_back({-1.0, 1.0}); + + PointList2d vertices2; + vertices2.push_back({2.0, 2.0}); + vertices2.push_back({2.0, 0.0}); + vertices2.push_back({0.0, 0.0}); + vertices2.push_back({0.0, 2.0}); + const auto result = intersects( - ConvexPolygon2d::create({p1, p2, p3, p4}).value(), - ConvexPolygon2d::create({p5, p6, p7, p8}).value()); + ConvexPolygon2d::create(vertices1).value(), ConvexPolygon2d::create(vertices2).value()); EXPECT_TRUE(result); } { // Two polygons do not intersect each other - const Point2d p1 = {1.0, 1.0}; - const Point2d p2 = {1.0, -1.0}; - const Point2d p3 = {-1.0, -1.0}; - const Point2d p4 = {-1.0, 1.0}; - const Point2d p5 = {3.0, 3.0}; - const Point2d p6 = {3.0, 2.0}; - const Point2d p7 = {2.0, 2.0}; - const Point2d p8 = {2.0, 3.0}; + PointList2d vertices1; + vertices1.push_back({1.0, 1.0}); + vertices1.push_back({1.0, -1.0}); + vertices1.push_back({-1.0, -1.0}); + vertices1.push_back({-1.0, 1.0}); + + PointList2d vertices2; + vertices2.push_back({3.0, 3.0}); + vertices2.push_back({3.0, 2.0}); + vertices2.push_back({2.0, 2.0}); + vertices2.push_back({2.0, 3.0}); + const auto result = intersects( - ConvexPolygon2d::create({p1, p2, p3, p4}).value(), - ConvexPolygon2d::create({p5, p6, p7, p8}).value()); + ConvexPolygon2d::create(vertices1).value(), ConvexPolygon2d::create(vertices2).value()); EXPECT_FALSE(result); } { // Two polygons share a vertex - const Point2d p1 = {1.0, 1.0}; - const Point2d p2 = {1.0, -1.0}; - const Point2d p3 = {-1.0, -1.0}; - const Point2d p4 = {-1.0, 1.0}; - const Point2d p5 = {2.0, 2.0}; - const Point2d p6 = {2.0, 1.0}; - const Point2d p7 = {1.0, 1.0}; - const Point2d p8 = {1.0, 2.0}; + PointList2d vertices1; + vertices1.push_back({1.0, 1.0}); + vertices1.push_back({1.0, -1.0}); + vertices1.push_back({-1.0, -1.0}); + vertices1.push_back({-1.0, 1.0}); + + PointList2d vertices2; + vertices2.push_back({2.0, 2.0}); + vertices2.push_back({2.0, 1.0}); + vertices2.push_back({1.0, 1.0}); + vertices2.push_back({1.0, 2.0}); + + const auto result = intersects( + ConvexPolygon2d::create(vertices1).value(), ConvexPolygon2d::create(vertices2).value()); + + EXPECT_TRUE(result); + } + + { // One concave polygon intersects the other + PointList2d outer1; + outer1.push_back({0.0, 0.0}); + outer1.push_back({0.0, 2.0}); + outer1.push_back({1.0, 1.0}); + outer1.push_back({2.0, 2.0}); + outer1.push_back({2.0, 0.0}); + + PointList2d outer2; + outer2.push_back({-0.5, 1.5}); + outer2.push_back({2.5, 1.5}); + outer2.push_back({2.5, -0.5}); + outer2.push_back({-0.5, -0.5}); + + const auto result = + intersects(Polygon2d::create(outer1, {}).value(), Polygon2d::create(outer2, {}).value()); + + EXPECT_TRUE(result); + } + + { // Two concave polygon does not intersect each other + PointList2d outer1; + outer1.push_back({0.0, 0.0}); + outer1.push_back({0.0, 2.0}); + outer1.push_back({1.0, 1.0}); + outer1.push_back({2.0, 2.0}); + outer1.push_back({2.0, 0.0}); + + PointList2d outer2; + outer2.push_back({0.0, 3.0}); + outer2.push_back({0.0, 4.0}); + outer2.push_back({2.0, 4.0}); + outer2.push_back({2.0, 3.0}); + + const auto result = + intersects(Polygon2d::create(outer1, {}).value(), Polygon2d::create(outer2, {}).value()); + + EXPECT_FALSE(result); + } + + { // Two concave polygons share a vertex + PointList2d outer1; + outer1.push_back({0.0, 0.0}); + outer1.push_back({0.0, 2.0}); + outer1.push_back({1.0, 1.0}); + outer1.push_back({2.0, 2.0}); + outer1.push_back({2.0, 0.0}); + + PointList2d outer2; + outer2.push_back({1.0, 1.0}); + outer2.push_back({0.0, 3.0}); + outer2.push_back({2.0, 3.0}); + + const auto result = + intersects(Polygon2d::create(outer1, {}).value(), Polygon2d::create(outer2, {}).value()); + + EXPECT_TRUE(result); + } + + { // One concave polygon is within the other + PointList2d outer1; + outer1.push_back({0.0, 0.0}); + outer1.push_back({0.0, 2.0}); + outer1.push_back({1.0, 1.0}); + outer1.push_back({2.0, 2.0}); + outer1.push_back({2.0, 0.0}); + + PointList2d outer2; + outer2.push_back({0.5, 0.5}); + outer2.push_back({0.5, 1.0}); + outer2.push_back({1.0, 0.5}); + + const auto result = + intersects(Polygon2d::create(outer1, {}).value(), Polygon2d::create(outer2, {}).value()); + + EXPECT_TRUE(result); + } + + { // One concave polygon intersects the hole of the other + PointList2d outer1; + outer1.push_back({-1.0, -1.0}); + outer1.push_back({-1.0, 3.0}); + outer1.push_back({3.0, 3.0}); + outer1.push_back({3.0, -1.0}); + + PointList2d inner1; + inner1.push_back({0.0, 0.0}); + inner1.push_back({0.0, 2.0}); + inner1.push_back({1.0, 1.0}); + inner1.push_back({2.0, 2.0}); + inner1.push_back({2.0, 0.0}); + + PointList2d outer2; + outer2.push_back({0.5, 1.5}); + outer2.push_back({1.5, 1.5}); + outer2.push_back({1.5, 0.5}); + outer2.push_back({0.5, 0.5}); + + const auto result = intersects( + Polygon2d::create(outer1, {inner1}).value(), Polygon2d::create(outer2, {}).value()); + + EXPECT_TRUE(result); + } + + { // One concave polygon is within the hole of the other + PointList2d outer1; + outer1.push_back({-1.0, -1.0}); + outer1.push_back({-1.0, 3.0}); + outer1.push_back({3.0, 3.0}); + outer1.push_back({3.0, -1.0}); + + PointList2d inner1; + inner1.push_back({0.0, 0.0}); + inner1.push_back({0.0, 2.0}); + inner1.push_back({1.0, 1.0}); + inner1.push_back({2.0, 2.0}); + inner1.push_back({2.0, 0.0}); + + PointList2d outer2; + outer2.push_back({0.5, 0.5}); + outer2.push_back({0.5, 1.0}); + outer2.push_back({1.0, 0.5}); + const auto result = intersects( - ConvexPolygon2d::create({p1, p2, p3, p4}).value(), - ConvexPolygon2d::create({p5, p6, p7, p8}).value()); + Polygon2d::create(outer1, {inner1}).value(), Polygon2d::create(outer2, {}).value()); EXPECT_FALSE(result); } + + { // One concave polygon and the hole of the other share a vertex + PointList2d outer1; + outer1.push_back({-1.0, -1.0}); + outer1.push_back({-1.0, 3.0}); + outer1.push_back({3.0, 3.0}); + outer1.push_back({3.0, -1.0}); + + PointList2d inner1; + inner1.push_back({0.0, 0.0}); + inner1.push_back({0.0, 2.0}); + inner1.push_back({1.0, 1.0}); + inner1.push_back({2.0, 2.0}); + inner1.push_back({2.0, 0.0}); + + PointList2d outer2; + outer2.push_back({1.0, 1.0}); + outer2.push_back({1.5, 0.5}); + outer2.push_back({0.5, 0.5}); + + const auto result = intersects( + Polygon2d::create(outer1, {inner1}).value(), Polygon2d::create(outer2, {}).value()); + + EXPECT_TRUE(result); + } } TEST(alt_geometry, isAbove) @@ -569,6 +911,7 @@ TEST(alt_geometry, touches) using autoware::universe_utils::touches; using autoware::universe_utils::alt::ConvexPolygon2d; using autoware::universe_utils::alt::Point2d; + using autoware::universe_utils::alt::PointList2d; { // The point is on the segment @@ -611,22 +954,28 @@ TEST(alt_geometry, touches) { // The point is on the edge of the polygon const Point2d point = {0.0, 0.0}; - const Point2d p1 = {2.0, 1.0}; - const Point2d p2 = {2.0, -1.0}; - const Point2d p3 = {0.0, -1.0}; - const Point2d p4 = {0.0, 1.0}; - const auto result = touches(point, ConvexPolygon2d::create({p1, p2, p3, p4}).value()); + + PointList2d vertices; + vertices.push_back({2.0, 1.0}); + vertices.push_back({2.0, -1.0}); + vertices.push_back({0.0, -1.0}); + vertices.push_back({0.0, 1.0}); + + const auto result = touches(point, ConvexPolygon2d::create(vertices).value()); EXPECT_TRUE(result); } { // The point is outside the polygon const Point2d point = {0.0, 0.0}; - const Point2d p1 = {2.0, 2.0}; - const Point2d p2 = {2.0, 1.0}; - const Point2d p3 = {1.0, 1.0}; - const Point2d p4 = {1.0, 2.0}; - const auto result = touches(point, ConvexPolygon2d::create({p1, p2, p3, p4}).value()); + + PointList2d vertices; + vertices.push_back({2.0, 2.0}); + vertices.push_back({2.0, 1.0}); + vertices.push_back({1.0, 1.0}); + vertices.push_back({1.0, 2.0}); + + const auto result = touches(point, ConvexPolygon2d::create(vertices).value()); EXPECT_FALSE(result); } @@ -637,87 +986,285 @@ TEST(alt_geometry, within) using autoware::universe_utils::within; using autoware::universe_utils::alt::ConvexPolygon2d; using autoware::universe_utils::alt::Point2d; + using autoware::universe_utils::alt::PointList2d; + using autoware::universe_utils::alt::Polygon2d; { // The point is within the polygon const Point2d point = {0.0, 0.0}; - const Point2d p1 = {1.0, 1.0}; - const Point2d p2 = {1.0, -1.0}; - const Point2d p3 = {-1.0, -1.0}; - const Point2d p4 = {-1.0, 1.0}; - const auto result = within(point, ConvexPolygon2d::create({p1, p2, p3, p4}).value()); + + PointList2d vertices; + vertices.push_back({1.0, 1.0}); + vertices.push_back({1.0, -1.0}); + vertices.push_back({-1.0, -1.0}); + vertices.push_back({-1.0, 1.0}); + + const auto result = within(point, ConvexPolygon2d::create(vertices).value()); EXPECT_TRUE(result); } { // The point is outside the polygon const Point2d point = {0.0, 0.0}; - const Point2d p1 = {2.0, 2.0}; - const Point2d p2 = {2.0, 1.0}; - const Point2d p3 = {1.0, 1.0}; - const Point2d p4 = {1.0, 2.0}; - const auto result = within(point, ConvexPolygon2d::create({p1, p2, p3, p4}).value()); + + PointList2d vertices; + vertices.push_back({2.0, 2.0}); + vertices.push_back({2.0, 1.0}); + vertices.push_back({1.0, 1.0}); + vertices.push_back({1.0, 2.0}); + + const auto result = within(point, ConvexPolygon2d::create(vertices).value()); EXPECT_FALSE(result); } { // The point is on the edge of the polygon const Point2d point = {0.0, 0.0}; - const Point2d p1 = {2.0, 1.0}; - const Point2d p2 = {2.0, -1.0}; - const Point2d p3 = {0.0, -1.0}; - const Point2d p4 = {0.0, 1.0}; - const auto result = within(point, ConvexPolygon2d::create({p1, p2, p3, p4}).value()); + + PointList2d vertices; + vertices.push_back({2.0, 1.0}); + vertices.push_back({2.0, -1.0}); + vertices.push_back({0.0, -1.0}); + vertices.push_back({0.0, 1.0}); + + const auto result = within(point, ConvexPolygon2d::create(vertices).value()); + + EXPECT_FALSE(result); + } + + { // The point is within the concave polygon + PointList2d outer; + outer.push_back({1.0, 1.0}); + outer.push_back({1.0, -1.0}); + outer.push_back({0.0, -0.5}); + outer.push_back({-1.0, -1.0}); + outer.push_back({-1.0, 1.0}); + outer.push_back({0.0, 0.5}); + + const Point2d point = {0.0, 0.0}; + + const auto result = within(point, Polygon2d::create(outer, {}).value()); + + EXPECT_TRUE(result); + } + + { // The point is within the hole of the polygon + PointList2d outer; + outer.push_back({0.0, 0.0}); + outer.push_back({0.0, 2.0}); + outer.push_back({2.0, 2.0}); + outer.push_back({2.0, 0.0}); + + PointList2d inner; + inner.push_back({0.5, 0.5}); + inner.push_back({0.5, 1.5}); + inner.push_back({1.5, 1.5}); + inner.push_back({1.5, 0.5}); + + const Point2d point = {1.0, 1.0}; + + const auto result = within(point, Polygon2d::create(outer, {inner}).value()); + + EXPECT_FALSE(result); + } + + { // The point is on the edge of the hole of the polygon + PointList2d outer; + outer.push_back({0.0, 0.0}); + outer.push_back({0.0, 2.0}); + outer.push_back({2.0, 2.0}); + outer.push_back({2.0, 0.0}); + + PointList2d inner; + inner.push_back({0.5, 0.5}); + inner.push_back({0.5, 1.5}); + inner.push_back({1.5, 1.5}); + inner.push_back({1.5, 0.5}); + + const Point2d point = {0.5, 1.0}; + + const auto result = within(point, Polygon2d::create(outer, {inner}).value()); EXPECT_FALSE(result); } { // One polygon is within the other - const Point2d p1 = {1.0, 1.0}; - const Point2d p2 = {1.0, -1.0}; - const Point2d p3 = {-1.0, -1.0}; - const Point2d p4 = {-1.0, 1.0}; - const Point2d p5 = {2.0, 2.0}; - const Point2d p6 = {2.0, -2.0}; - const Point2d p7 = {-2.0, -2.0}; - const Point2d p8 = {-2.0, 2.0}; + PointList2d outer_contained; + outer_contained.push_back({0.5, 0.5}); + outer_contained.push_back({0.5, 1.0}); + outer_contained.push_back({1.0, 0.5}); + + PointList2d outer_containing; + outer_containing.push_back({0.0, 0.0}); + outer_containing.push_back({0.0, 2.0}); + outer_containing.push_back({1.0, 1.0}); + outer_containing.push_back({2.0, 2.0}); + outer_containing.push_back({2.0, 0.0}); + const auto result = within( - ConvexPolygon2d::create({p1, p2, p3, p4}).value(), - ConvexPolygon2d::create({p5, p6, p7, p8}).value()); + Polygon2d::create(outer_contained, {}).value(), + Polygon2d::create(outer_containing, {}).value()); EXPECT_TRUE(result); } { // One polygon is outside the other - const Point2d p1 = {1.0, 1.0}; - const Point2d p2 = {1.0, -1.0}; - const Point2d p3 = {-1.0, -1.0}; - const Point2d p4 = {-1.0, 1.0}; - const Point2d p5 = {3.0, 3.0}; - const Point2d p6 = {3.0, 2.0}; - const Point2d p7 = {2.0, 2.0}; - const Point2d p8 = {2.0, 3.0}; + PointList2d outer_contained; + outer_contained.push_back({1.0, 1.0}); + outer_contained.push_back({1.0, -1.0}); + outer_contained.push_back({-1.0, -1.0}); + outer_contained.push_back({-1.0, 1.0}); + + PointList2d outer_containing; + outer_containing.push_back({3.0, 3.0}); + outer_containing.push_back({3.0, 2.0}); + outer_containing.push_back({2.0, 2.0}); + outer_containing.push_back({2.0, 3.0}); + const auto result = within( - ConvexPolygon2d::create({p1, p2, p3, p4}).value(), - ConvexPolygon2d::create({p5, p6, p7, p8}).value()); + Polygon2d::create(outer_contained, {}).value(), + Polygon2d::create(outer_containing, {}).value()); EXPECT_FALSE(result); } { // Both polygons are the same - const Point2d p1 = {1.0, 1.0}; - const Point2d p2 = {1.0, -1.0}; - const Point2d p3 = {-1.0, -1.0}; - const Point2d p4 = {-1.0, 1.0}; - const Point2d p5 = {1.0, 1.0}; - const Point2d p6 = {1.0, -1.0}; - const Point2d p7 = {-1.0, -1.0}; - const Point2d p8 = {-1.0, 1.0}; + PointList2d outer_contained; + outer_contained.push_back({1.0, 1.0}); + outer_contained.push_back({1.0, -1.0}); + outer_contained.push_back({-1.0, -1.0}); + outer_contained.push_back({-1.0, 1.0}); + + PointList2d outer_containing; + outer_containing.push_back({1.0, 1.0}); + outer_containing.push_back({1.0, -1.0}); + outer_containing.push_back({-1.0, -1.0}); + outer_containing.push_back({-1.0, 1.0}); + + const auto result = within( + Polygon2d::create(outer_contained, {}).value(), + Polygon2d::create(outer_containing, {}).value()); + + EXPECT_TRUE(result); + } + + { // One polygon is within the hole of the other + PointList2d outer_contained; + outer_contained.push_back({0.5, 0.5}); + outer_contained.push_back({0.5, 1.0}); + outer_contained.push_back({1.0, 0.5}); + + PointList2d outer_containing; + outer_containing.push_back({-1.0, -1.0}); + outer_containing.push_back({-1.0, 3.0}); + outer_containing.push_back({3.0, 3.0}); + outer_containing.push_back({3.0, -1.0}); + + PointList2d inner_containing; + inner_containing.push_back({0.0, 0.0}); + inner_containing.push_back({0.0, 2.0}); + inner_containing.push_back({1.0, 1.0}); + inner_containing.push_back({2.0, 2.0}); + inner_containing.push_back({2.0, 0.0}); + + const auto result = within( + Polygon2d::create(outer_contained, {}).value(), + Polygon2d::create(outer_containing, {inner_containing}).value()); + + EXPECT_FALSE(result); + } + + { // One polygon intersects the hole of the other + PointList2d outer_contained; + outer_contained.push_back({0.5, 1.5}); + outer_contained.push_back({1.5, 1.5}); + outer_contained.push_back({1.5, 0.5}); + outer_contained.push_back({0.5, 0.5}); + + PointList2d outer_containing; + outer_containing.push_back({-1.0, -1.0}); + outer_containing.push_back({-1.0, 3.0}); + outer_containing.push_back({3.0, 3.0}); + outer_containing.push_back({3.0, -1.0}); + + PointList2d inner_containing; + inner_containing.push_back({0.0, 0.0}); + inner_containing.push_back({0.0, 2.0}); + inner_containing.push_back({1.0, 1.0}); + inner_containing.push_back({2.0, 2.0}); + inner_containing.push_back({2.0, 0.0}); + + const auto result = within( + Polygon2d::create(outer_contained, {}).value(), + Polygon2d::create(outer_containing, {inner_containing}).value()); + + EXPECT_FALSE(result); + } + + { + // One polygon with a hole is within the other + PointList2d outer_contained; + outer_contained.push_back({-1.5, -1.5}); + outer_contained.push_back({-1.5, 1.5}); + outer_contained.push_back({1.5, 1.5}); + outer_contained.push_back({1.5, -1.5}); + + PointList2d inner_contained; + inner_contained.push_back({-1.0, -1.0}); + inner_contained.push_back({-1.0, 1.0}); + inner_contained.push_back({1.0, 1.0}); + inner_contained.push_back({1.0, -1.0}); + + PointList2d outer_containing; + outer_containing.push_back({-2.0, -2.0}); + outer_containing.push_back({-2.0, 2.0}); + outer_containing.push_back({2.0, 2.0}); + outer_containing.push_back({2.0, -2.0}); + + PointList2d inner_containing; + inner_containing.push_back({-0.5, -0.5}); + inner_containing.push_back({-0.5, 0.5}); + inner_containing.push_back({0.5, 0.5}); + inner_containing.push_back({0.5, -0.5}); + const auto result = within( - ConvexPolygon2d::create({p1, p2, p3, p4}).value(), - ConvexPolygon2d::create({p5, p6, p7, p8}).value()); + Polygon2d::create(outer_contained, {inner_contained}).value(), + Polygon2d::create(outer_containing, {inner_containing}).value()); EXPECT_TRUE(result); } + + { + // One polygon with a hole is not within the other + PointList2d outer_contained; + outer_contained.push_back({-1.5, -1.5}); + outer_contained.push_back({-1.5, 1.5}); + outer_contained.push_back({1.5, 1.5}); + outer_contained.push_back({1.5, -1.5}); + + PointList2d inner_contained; + inner_contained.push_back({-0.5, -0.5}); + inner_contained.push_back({-0.5, 0.5}); + inner_contained.push_back({0.5, 0.5}); + inner_contained.push_back({0.5, -0.5}); + + PointList2d outer_containing; + outer_containing.push_back({-2.0, -2.0}); + outer_containing.push_back({-2.0, 2.0}); + outer_containing.push_back({2.0, 2.0}); + outer_containing.push_back({2.0, -2.0}); + + PointList2d inner_containing; + inner_containing.push_back({-1.0, -1.0}); + inner_containing.push_back({-1.0, 1.0}); + inner_containing.push_back({1.0, 1.0}); + inner_containing.push_back({1.0, -1.0}); + + const auto result = within( + Polygon2d::create(outer_contained, {inner_contained}).value(), + Polygon2d::create(outer_containing, {inner_containing}).value()); + + EXPECT_FALSE(result); + } } TEST(alt_geometry, areaRand) @@ -760,6 +1307,45 @@ TEST(alt_geometry, areaRand) } } +TEST(alt_geometry, areaConcaveRand) +{ + std::vector polygons; + constexpr auto polygons_nb = 100; + constexpr auto max_vertices = 10; + constexpr auto max_values = 1000; + + autoware::universe_utils::StopWatch sw; + for (auto vertices = 4UL; vertices < max_vertices; ++vertices) { + double ground_truth_area_ns = 0.0; + double alt_area_ns = 0.0; + + polygons.clear(); + for (auto i = 0; i < polygons_nb; ++i) { + polygons.push_back(autoware::universe_utils::random_concave_polygon(vertices, max_values)); + } + for (auto i = 0UL; i < polygons.size(); ++i) { + sw.tic(); + const auto ground_truth = boost::geometry::area(polygons[i]); + ground_truth_area_ns += sw.toc(); + + const auto alt_poly = autoware::universe_utils::alt::Polygon2d::create(polygons[i]).value(); + sw.tic(); + const auto alt = autoware::universe_utils::area(alt_poly); + alt_area_ns += sw.toc(); + + if (std::abs(alt - ground_truth) > epsilon) { + std::cout << "Alt failed for the polygon: "; + std::cout << boost::geometry::wkt(polygons[i]) << std::endl; + } + EXPECT_NEAR(ground_truth, alt, epsilon); + } + std::printf("polygons_nb = %d, vertices = %ld\n", polygons_nb, vertices); + std::printf( + "\tArea:\n\t\tBoost::geometry = %2.2f ms\n\t\tAlt = %2.2f ms\n", ground_truth_area_ns / 1e6, + alt_area_ns / 1e6); + } +} + TEST(alt_geometry, convexHullRand) { std::vector polygons; @@ -875,7 +1461,7 @@ TEST(alt_geometry, coveredByRand) } } -TEST(alt_geometry, disjointRand) +TEST(alt_geometry, coveredByConcaveRand) { std::vector polygons; constexpr auto polygons_nb = 100; @@ -883,7 +1469,74 @@ TEST(alt_geometry, disjointRand) constexpr auto max_values = 1000; autoware::universe_utils::StopWatch sw; - for (auto vertices = 3UL; vertices < max_vertices; ++vertices) { + for (auto vertices = 4UL; vertices < max_vertices; ++vertices) { + double ground_truth_covered_ns = 0.0; + double ground_truth_not_covered_ns = 0.0; + double alt_covered_ns = 0.0; + double alt_not_covered_ns = 0.0; + int covered_count = 0; + + polygons.clear(); + for (auto i = 0; i < polygons_nb; ++i) { + polygons.push_back(autoware::universe_utils::random_concave_polygon(vertices, max_values)); + } + for (auto i = 0UL; i < polygons.size(); ++i) { + for (const auto & point : polygons[i].outer()) { + for (auto j = 0UL; j < polygons.size(); ++j) { + sw.tic(); + const auto ground_truth = boost::geometry::covered_by(point, polygons[j]); + if (ground_truth) { + ++covered_count; + ground_truth_covered_ns += sw.toc(); + } else { + ground_truth_not_covered_ns += sw.toc(); + } + + const auto alt_point = autoware::universe_utils::alt::Point2d(point); + const auto alt_poly = + autoware::universe_utils::alt::Polygon2d::create(polygons[j]).value(); + sw.tic(); + const auto alt = autoware::universe_utils::covered_by(alt_point, alt_poly); + if (alt) { + alt_covered_ns += sw.toc(); + } else { + alt_not_covered_ns += sw.toc(); + } + + if (ground_truth != alt) { + std::cout << "Alt failed for the point and polygon: "; + std::cout << boost::geometry::wkt(point) << boost::geometry::wkt(polygons[j]) + << std::endl; + } + EXPECT_EQ(ground_truth, alt); + } + } + } + std::printf( + "polygons_nb = %d, vertices = %ld, %d / %ld covered pairs\n", polygons_nb, vertices, + covered_count, polygons_nb * vertices * polygons_nb); + std::printf( + "\tCovered:\n\t\tBoost::geometry = %2.2f ms\n\t\tAlt = %2.2f ms\n", + ground_truth_covered_ns / 1e6, alt_covered_ns / 1e6); + std::printf( + "\tNot covered:\n\t\tBoost::geometry = %2.2f ms\n\t\tAlt = %2.2f ms\n", + ground_truth_not_covered_ns / 1e6, alt_not_covered_ns / 1e6); + std::printf( + "\tTotal:\n\t\tBoost::geometry = %2.2f ms\n\t\tAlt = %2.2f ms\n", + (ground_truth_not_covered_ns + ground_truth_covered_ns) / 1e6, + (alt_not_covered_ns + alt_covered_ns) / 1e6); + } +} + +TEST(alt_geometry, disjointRand) +{ + std::vector polygons; + constexpr auto polygons_nb = 100; + constexpr auto max_vertices = 10; + constexpr auto max_values = 1000; + + autoware::universe_utils::StopWatch sw; + for (auto vertices = 3UL; vertices < max_vertices; ++vertices) { double ground_truth_disjoint_ns = 0.0; double ground_truth_not_disjoint_ns = 0.0; double alt_disjoint_ns = 0.0; @@ -941,6 +1594,72 @@ TEST(alt_geometry, disjointRand) } } +TEST(alt_geometry, disjointConcaveRand) +{ + std::vector polygons; + constexpr auto polygons_nb = 100; + constexpr auto max_vertices = 10; + constexpr auto max_values = 1000; + + autoware::universe_utils::StopWatch sw; + for (auto vertices = 4UL; vertices < max_vertices; ++vertices) { + double ground_truth_disjoint_ns = 0.0; + double ground_truth_not_disjoint_ns = 0.0; + double alt_disjoint_ns = 0.0; + double alt_not_disjoint_ns = 0.0; + int disjoint_count = 0; + + polygons.clear(); + for (auto i = 0; i < polygons_nb; ++i) { + polygons.push_back(autoware::universe_utils::random_concave_polygon(vertices, max_values)); + } + for (auto i = 0UL; i < polygons.size(); ++i) { + for (auto j = 0UL; j < polygons.size(); ++j) { + sw.tic(); + const auto ground_truth = boost::geometry::disjoint(polygons[i], polygons[j]); + if (ground_truth) { + ++disjoint_count; + ground_truth_disjoint_ns += sw.toc(); + } else { + ground_truth_not_disjoint_ns += sw.toc(); + } + + const auto alt_poly1 = + autoware::universe_utils::alt::Polygon2d::create(polygons[i]).value(); + const auto alt_poly2 = + autoware::universe_utils::alt::Polygon2d::create(polygons[j]).value(); + sw.tic(); + const auto alt = autoware::universe_utils::disjoint(alt_poly1, alt_poly2); + if (alt) { + alt_disjoint_ns += sw.toc(); + } else { + alt_not_disjoint_ns += sw.toc(); + } + + if (ground_truth != alt) { + std::cout << "Failed for the 2 polygons: "; + std::cout << boost::geometry::wkt(polygons[i]) << boost::geometry::wkt(polygons[j]) + << std::endl; + } + EXPECT_EQ(ground_truth, alt); + } + } + std::printf( + "polygons_nb = %d, vertices = %ld, %d / %d disjoint pairs\n", polygons_nb, vertices, + disjoint_count, polygons_nb * polygons_nb); + std::printf( + "\tDisjoint:\n\t\tBoost::geometry = %2.2f ms\n\t\tAlt = %2.2f ms\n", + ground_truth_disjoint_ns / 1e6, alt_disjoint_ns / 1e6); + std::printf( + "\tNot disjoint:\n\t\tBoost::geometry = %2.2f ms\n\t\tAlt = %2.2f ms\n", + ground_truth_not_disjoint_ns / 1e6, alt_not_disjoint_ns / 1e6); + std::printf( + "\tTotal:\n\t\tBoost::geometry = %2.2f ms\n\t\tAlt = %2.2f ms\n", + (ground_truth_not_disjoint_ns + ground_truth_disjoint_ns) / 1e6, + (alt_not_disjoint_ns + alt_disjoint_ns) / 1e6); + } +} + TEST(alt_geometry, intersectsRand) { std::vector polygons; @@ -1007,6 +1726,72 @@ TEST(alt_geometry, intersectsRand) } } +TEST(alt_geometry, intersectsConcaveRand) +{ + std::vector polygons; + constexpr auto polygons_nb = 100; + constexpr auto max_vertices = 10; + constexpr auto max_values = 1000; + + autoware::universe_utils::StopWatch sw; + for (auto vertices = 4UL; vertices < max_vertices; ++vertices) { + double ground_truth_intersect_ns = 0.0; + double ground_truth_no_intersect_ns = 0.0; + double alt_intersect_ns = 0.0; + double alt_no_intersect_ns = 0.0; + int intersect_count = 0; + + polygons.clear(); + for (auto i = 0; i < polygons_nb; ++i) { + polygons.push_back(autoware::universe_utils::random_concave_polygon(vertices, max_values)); + } + for (auto i = 0UL; i < polygons.size(); ++i) { + for (auto j = 0UL; j < polygons.size(); ++j) { + sw.tic(); + const auto ground_truth = boost::geometry::intersects(polygons[i], polygons[j]); + if (ground_truth) { + ++intersect_count; + ground_truth_intersect_ns += sw.toc(); + } else { + ground_truth_no_intersect_ns += sw.toc(); + } + + const auto alt_poly1 = + autoware::universe_utils::alt::Polygon2d::create(polygons[i]).value(); + const auto alt_poly2 = + autoware::universe_utils::alt::Polygon2d::create(polygons[j]).value(); + sw.tic(); + const auto alt = autoware::universe_utils::intersects(alt_poly1, alt_poly2); + if (alt) { + alt_intersect_ns += sw.toc(); + } else { + alt_no_intersect_ns += sw.toc(); + } + + if (ground_truth != alt) { + std::cout << "Failed for the 2 polygons: "; + std::cout << boost::geometry::wkt(polygons[i]) << boost::geometry::wkt(polygons[j]) + << std::endl; + } + EXPECT_EQ(ground_truth, alt); + } + } + std::printf( + "polygons_nb = %d, vertices = %ld, %d / %d pairs with intersects\n", polygons_nb, vertices, + intersect_count, polygons_nb * polygons_nb); + std::printf( + "\tIntersect:\n\t\tBoost::geometry = %2.2f ms\n\t\tAlt = %2.2f ms\n", + ground_truth_intersect_ns / 1e6, alt_intersect_ns / 1e6); + std::printf( + "\tNo intersect:\n\t\tBoost::geometry = %2.2f ms\n\t\tAlt = %2.2f ms\n", + ground_truth_no_intersect_ns / 1e6, alt_no_intersect_ns / 1e6); + std::printf( + "\tTotal:\n\t\tBoost::geometry = %2.2f ms\n\t\tAlt = %2.2f ms\n", + (ground_truth_no_intersect_ns + ground_truth_intersect_ns) / 1e6, + (alt_no_intersect_ns + alt_intersect_ns) / 1e6); + } +} + TEST(alt_geometry, touchesRand) { std::vector polygons; @@ -1074,6 +1859,73 @@ TEST(alt_geometry, touchesRand) } } +TEST(alt_geometry, touchesConcaveRand) +{ + std::vector polygons; + constexpr auto polygons_nb = 100; + constexpr auto max_vertices = 10; + constexpr auto max_values = 1000; + + autoware::universe_utils::StopWatch sw; + for (auto vertices = 4UL; vertices < max_vertices; ++vertices) { + double ground_truth_touching_ns = 0.0; + double ground_truth_not_touching_ns = 0.0; + double alt_touching_ns = 0.0; + double alt_not_touching_ns = 0.0; + int touching_count = 0; + + polygons.clear(); + for (auto i = 0; i < polygons_nb; ++i) { + polygons.push_back(autoware::universe_utils::random_concave_polygon(vertices, max_values)); + } + for (auto i = 0UL; i < polygons.size(); ++i) { + for (const auto & point : polygons[i].outer()) { + for (auto j = 0UL; j < polygons.size(); ++j) { + sw.tic(); + const auto ground_truth = boost::geometry::touches(point, polygons[j]); + if (ground_truth) { + ++touching_count; + ground_truth_touching_ns += sw.toc(); + } else { + ground_truth_not_touching_ns += sw.toc(); + } + + const auto alt_point = autoware::universe_utils::alt::Point2d(point); + const auto alt_poly = + autoware::universe_utils::alt::Polygon2d::create(polygons[j]).value(); + sw.tic(); + const auto alt = autoware::universe_utils::touches(alt_point, alt_poly); + if (alt) { + alt_touching_ns += sw.toc(); + } else { + alt_not_touching_ns += sw.toc(); + } + + if (ground_truth != alt) { + std::cout << "Alt failed for the point and polygon: "; + std::cout << boost::geometry::wkt(point) << boost::geometry::wkt(polygons[j]) + << std::endl; + } + EXPECT_EQ(ground_truth, alt); + } + } + } + std::printf( + "polygons_nb = %d, vertices = %ld, %d / %ld touching pairs\n", polygons_nb, vertices, + touching_count, polygons_nb * vertices * polygons_nb); + std::printf( + "\tTouching:\n\t\tBoost::geometry = %2.2f ms\n\t\tAlt = %2.2f ms\n", + ground_truth_touching_ns / 1e6, alt_touching_ns / 1e6); + std::printf( + "\tNot touching:\n\t\tBoost::geometry = %2.2f ms\n\t\tAlt = %2.2f ms\n", + ground_truth_not_touching_ns / 1e6, alt_not_touching_ns / 1e6); + std::printf( + "\tTotal:\n\t\tBoost::geometry = %2.2f ms\n\t\tAlt = %2.2f ms\n", + (ground_truth_not_touching_ns + ground_truth_touching_ns) / 1e6, + (alt_not_touching_ns + alt_touching_ns) / 1e6); + } +} + TEST(alt_geometry, withinPolygonRand) { std::vector polygons; @@ -1139,3 +1991,69 @@ TEST(alt_geometry, withinPolygonRand) (alt_not_within_ns + alt_within_ns) / 1e6); } } + +TEST(alt_geometry, withinPolygonConcaveRand) +{ + std::vector polygons; + constexpr auto polygons_nb = 100; + constexpr auto max_vertices = 10; + constexpr auto max_values = 1000; + + autoware::universe_utils::StopWatch sw; + for (auto vertices = 4UL; vertices < max_vertices; ++vertices) { + double ground_truth_within_ns = 0.0; + double ground_truth_not_within_ns = 0.0; + double alt_within_ns = 0.0; + double alt_not_within_ns = 0.0; + int within_count = 0; + + polygons.clear(); + for (auto i = 0; i < polygons_nb; ++i) { + polygons.push_back(autoware::universe_utils::random_concave_polygon(vertices, max_values)); + } + for (auto i = 0UL; i < polygons.size(); ++i) { + for (auto j = 0UL; j < polygons.size(); ++j) { + sw.tic(); + const auto ground_truth = boost::geometry::within(polygons[i], polygons[j]); + if (ground_truth) { + ++within_count; + ground_truth_within_ns += sw.toc(); + } else { + ground_truth_not_within_ns += sw.toc(); + } + + const auto alt_poly1 = + autoware::universe_utils::alt::Polygon2d::create(polygons[i]).value(); + const auto alt_poly2 = + autoware::universe_utils::alt::Polygon2d::create(polygons[j]).value(); + sw.tic(); + const auto alt = autoware::universe_utils::within(alt_poly1, alt_poly2); + if (alt) { + alt_within_ns += sw.toc(); + } else { + alt_not_within_ns += sw.toc(); + } + + if (ground_truth != alt) { + std::cout << "Alt failed for the 2 polygons: "; + std::cout << boost::geometry::wkt(polygons[i]) << boost::geometry::wkt(polygons[j]) + << std::endl; + } + EXPECT_EQ(ground_truth, alt); + } + } + std::printf( + "polygons_nb = %d, vertices = %ld, %d / %d pairs either of which is within the other\n", + polygons_nb, vertices, within_count, polygons_nb * polygons_nb); + std::printf( + "\tWithin:\n\t\tBoost::geometry = %2.2f ms\n\t\tAlt = %2.2f ms\n", + ground_truth_within_ns / 1e6, alt_within_ns / 1e6); + std::printf( + "\tNot within:\n\t\tBoost::geometry = %2.2f ms\n\t\tAlt = %2.2f ms\n", + ground_truth_not_within_ns / 1e6, alt_not_within_ns / 1e6); + std::printf( + "\tTotal:\n\t\tBoost::geometry = %2.2f ms\n\t\tAlt = %2.2f ms\n", + (ground_truth_not_within_ns + ground_truth_within_ns) / 1e6, + (alt_not_within_ns + alt_within_ns) / 1e6); + } +}