test_polygon.cpp
Go to the documentation of this file.
1 #include <gtest/gtest.h>
2 
3 #include <boost/geometry/algorithms/perimeter.hpp>
4 #include <random>
5 
9 
10 using namespace lanelet;
11 template <typename T>
12 T toPolygon(const Polygon3d& p) {
13  return T(p);
14 }
15 template <>
17  return utils::to2D(p);
18 }
19 template <>
21  return utils::to2D(p);
22 }
23 template <>
25  return utils::to2D(p).basicPolygon();
26 }
27 template <>
29  return p.basicPolygon();
30 }
31 
32 class PolygonPoints : public ::testing::Test {
33  public:
35  Id id{1};
36  p11 = Point3d(++id, 0., 0., 0.);
37  p12 = Point3d(++id, 1., 2., 1.);
38  p13 = Point3d(++id, 1., 0., 0.);
39  p21 = Point3d(++id, 2., 0., 0.);
40  p22 = Point3d(++id, 3., 1., 0.);
41  p23 = Point3d(++id, 3., 0., 0.);
42  p31 = Point3d(++id, 1., 0., 2.);
43  p32 = Point3d(++id, 1., 1., 2.);
44  p33 = Point3d(++id, 2., 1., 2.);
45  p34 = Point3d(++id, 2., 0., 2.);
46  /*
47  * p42 X
48  * / \
49  * / X \
50  * / -/ p44 \- \
51  * p41 X/ \X p43
52  */
53  p41 = Point3d(++id, 1.5, 1.5, 2.);
54  p42 = Point3d(++id, 1.5, 0.5, 2.);
55  }
56 
57  Point3d p11, p12, p13;
58  Point3d p21, p22, p23;
59  Point3d p31, p32, p33, p34;
60  Point3d p41, p42;
61 };
62 
63 template <typename T>
65  public:
66  using PolygonT = T;
68  Id id{20};
70  poly1 = toPolygon<T>(Polygon3d{++id, {p11, p12, p13}, poly1Attrs});
71  poly2 = toPolygon<T>(Polygon3d{++id, {p21, p22, p23}});
72  poly3 = toPolygon<T>(Polygon3d{++id, {p31, p32, p33, p34}});
73  Polygon3d temp{++id, {p31, p41, p34, p42}};
74  simpleStar = toPolygon<T>(temp);
75  subdivide(temp, 2, ++id);
76  fancyStar = toPolygon<T>(temp);
77  }
78 
79  PolygonT poly1, poly2, poly3, fancyStar, simpleStar; // NOLINT
80 
81  private:
82  void randomSubdivide(Polygon3d& poly, const size_t after, const int nNew, Id id) const {
83  BasicPoint3d delta =
84  (utils::toBasicPoint(poly[(after + 1) % poly.size()]) - utils::toBasicPoint(poly[after])) / (2 * nNew + 1);
85  std::vector<Point3d> points;
86  points.reserve(size_t(nNew));
87  for (int i = 0; i < nNew; ++i) {
88  points.emplace_back(Point3d(++id, utils::toBasicPoint(poly[after]) + (2 * double(i) + randomNum_() + 1) * delta));
89  }
90  Polygon3d newLs(++id, points);
91  poly.insert(poly.begin() + long((after + 1) % poly.size()), newLs.begin(), newLs.end());
92  }
93  void subdivide(Polygon3d& poly, const int n, Id id) {
94  auto initSize = poly.size();
95  for (size_t i = 0; i < initSize; ++i) {
96  randomSubdivide(poly, (n + 1) * i, n, id);
97  }
98  }
99  std::function<double(void)> randomNum_ = [distr = std::uniform_real_distribution<double>(-0.5, 0.5),
100  gen = std::mt19937(133769420)]() mutable { return distr(gen); };
101 };
102 
103 template <typename PolygonT>
104 PolygonT composePolygon(std::initializer_list<ConstLineString3d> list) {
105  return PolygonT(list);
106 }
107 
108 template <typename T>
110  protected:
111  using PolygonT = T;
113  Id id{10};
114  ConstLineString3d tempLs11{++id, {p11, p12, p13}};
115  ConstLineString3d tempLs21{++id, {p21, p22}};
116  ConstLineString3d tempLs22{++id, {p22, p23}};
117  ConstLineString3d tempLs31{++id, {p31, p32}};
118  ConstLineString3d tempLs32{++id, {p33, p34}};
119  ConstLineString3d tempStar1{++id, {p31, p41, p34}};
120  ConstLineString3d tempStar2{++id, {p34, p42}};
121 
122  poly1 = PolygonT({tempLs11});
123  poly2 = PolygonT({tempLs21, tempLs22});
124  poly3 = PolygonT({tempLs31, tempLs32});
125  simpleStar = PolygonT({tempStar1, tempStar2});
126  fancyStar = simpleStar;
127  }
128 
129  public:
130  PolygonT poly1, poly2, poly3, fancyStar, simpleStar;
131 };
132 
133 template <typename T>
134 auto getZ(const T& p) -> std::enable_if_t<!traits::is2D<T>(), double> {
135  return p.z();
136 }
137 template <typename T>
138 auto getZ(const T & /*p*/) -> std::enable_if_t<traits::is2D<T>(), double> {
139  return 0.;
140 }
141 
142 template <>
143 class PolygonTypeTest<CompoundPolygon2d> : public CompoundPolygonTypeTest<CompoundPolygon2d> {};
144 
145 template <>
146 class PolygonTypeTest<CompoundPolygon3d> : public CompoundPolygonTypeTest<CompoundPolygon3d> {};
147 
148 template <>
149 class PolygonTypeTest<CompoundHybridPolygon2d> : public CompoundPolygonTypeTest<CompoundHybridPolygon2d> {};
150 
151 template <>
152 class PolygonTypeTest<CompoundHybridPolygon3d> : public CompoundPolygonTypeTest<CompoundHybridPolygon3d> {};
153 
154 template <typename T>
155 class AllPolygonsTest : public PolygonTypeTest<T> {};
156 
157 template <typename T>
158 class NormalPolygonsTest : public PolygonTypeTest<T> {};
159 
160 template <typename T>
162 
163 template <typename T>
165 
166 template <typename T>
168 
169 template <typename T>
170 class HybridPolygonsTest : public PolygonTypeTest<T> {};
171 
172 template <typename T>
174 
175 template <typename T>
176 class ThreeDPolygonsTest : public PolygonTypeTest<T> {};
177 
178 template <typename T>
179 class TwoDPolygonsTest : public PolygonTypeTest<T> {};
180 
181 template <typename T>
183 
184 template <typename T>
186 
187 using AllPolygons =
189  CompoundPolygon2d, CompoundPolygon3d, CompoundHybridPolygon2d, CompoundHybridPolygon3d>;
190 using NormalPolygons = testing::Types<Polygon2d, Polygon3d, ConstPolygon2d, ConstPolygon3d>;
191 using ThreeDPolygons =
192  testing::Types<Polygon3d, ConstPolygon3d, ConstHybridPolygon3d, CompoundPolygon3d, CompoundHybridPolygon3d>;
193 using TwoDPolygons =
194  testing::Types<Polygon2d, ConstPolygon2d, ConstHybridPolygon2d, CompoundPolygon2d, CompoundHybridPolygon2d>;
196  CompoundPolygon3d, CompoundHybridPolygon3d>;
198  CompoundPolygon2d, CompoundHybridPolygon2d>;
199 using MutablePolygons = testing::Types<Polygon2d, Polygon3d>;
200 using PrimitivePolygons =
201  testing::Types<Polygon2d, Polygon3d, ConstPolygon2d, ConstPolygon3d, ConstHybridPolygon2d, ConstHybridPolygon3d>;
202 using NonHybridPolygons =
203  testing::Types<Polygon2d, Polygon3d, ConstPolygon2d, ConstPolygon3d, CompoundPolygon2d, CompoundPolygon3d>;
204 using HybridPolygons =
205  testing::Types<ConstHybridPolygon2d, ConstHybridPolygon3d, CompoundHybridPolygon2d, CompoundHybridPolygon3d>;
206 using HybridPolygonsTwoD = testing::Types<ConstHybridPolygon2d, CompoundHybridPolygon2d>;
207 
208 #ifndef TYPED_TEST_SUITE
209 // backwards compability with old gtest versions
210 #define TYPED_TEST_SUITE TYPED_TEST_CASE
211 #endif
223 
225  this->poly1.setId(100);
226  EXPECT_EQ(100, this->poly1.id());
227 }
228 
229 TYPED_TEST(MutablePolygonsTest, readAttributes) { // NOLINT
230  EXPECT_TRUE(this->poly1.hasAttribute(AttributeName::Type));
231  EXPECT_TRUE(this->poly1.hasAttribute(AttributeNamesString::Type));
232  EXPECT_EQ(this->poly1.attribute(AttributeName::Type), AttributeValueString::Curbstone);
233  EXPECT_EQ(this->poly1.attribute(AttributeNamesString::Type), AttributeValueString::Curbstone);
234 }
235 
236 TYPED_TEST(TwoDAndBasicPolygonsTest, bounds2d) { // NOLINT
238  EXPECT_EQ(bbox.min().x(), 2);
239  EXPECT_EQ(bbox.min().y(), 0);
240  EXPECT_EQ(bbox.max().x(), 3);
241  EXPECT_EQ(bbox.max().y(), 1);
242 }
243 
246  EXPECT_EQ(bbox.min().x(), 0);
247  EXPECT_EQ(bbox.min().y(), 0);
248  EXPECT_EQ(bbox.min().z(), 0);
249  EXPECT_EQ(bbox.max().x(), 1);
250  EXPECT_EQ(bbox.max().y(), 2);
251  EXPECT_EQ(bbox.max().z(), 1);
252 }
253 
255  EXPECT_DOUBLE_EQ(geometry::distance(this->poly1, this->poly3), 0.);
256  EXPECT_DOUBLE_EQ(geometry::distance(this->poly1, this->poly2), 1.);
257 }
258 
259 TYPED_TEST(HybridPolygonsTwoDTest, distancePoint2d) { // NOLINT
260  EXPECT_DOUBLE_EQ(geometry::distance(this->poly1, BasicPoint2d(2, 1)), 1.);
261 }
262 
264  auto a = geometry::area(this->poly1);
265  EXPECT_DOUBLE_EQ(a, 1.);
266 }
267 
268 TYPED_TEST(HybridPolygonsTwoDTest, centroid) { // NOLINT
269  BasicPoint2d p{0, 0};
270  boost::geometry::centroid(this->poly3, p);
271  EXPECT_DOUBLE_EQ(p.x(), 1.5);
272  EXPECT_DOUBLE_EQ(p.y(), 0.5);
273 }
274 
275 TYPED_TEST(AllPolygonsTest, perimeter) { // NOLINT
276  auto p = boost::geometry::perimeter(this->poly3);
277  EXPECT_DOUBLE_EQ(p, 4);
278 }
279 
280 TYPED_TEST(TwoDPolygonsTest, toBasicPolygon) { // NOLINT
281  auto pBasic = this->poly3.basicPolygon();
282  EXPECT_EQ(4, boost::geometry::perimeter(pBasic));
283  EXPECT_EQ(4ul, pBasic.size());
284 }
285 
286 inline auto crossProd(const Eigen::Matrix<double, 2, 1>& p1, const Eigen::Matrix<double, 2, 1>& p2) {
287  return BasicPoint3d(p1.x(), p1.y(), 0.).cross(BasicPoint3d(p2.x(), p2.y(), 0.)).eval();
288 }
289 
290 bool isConnectionConvex(const BasicPoint2d& seg1, const BasicPoint2d& seg2,
291  const double eps = 4 * std::numeric_limits<double>::epsilon()) {
292  return crossProd(seg1.normalized(), seg2.normalized()).z() <= eps;
293 }
294 
295 bool isConvex(const BasicPolygon2d& poly) {
296  for (size_t i = 2; i < poly.size(); ++i) {
297  if (!isConnectionConvex(poly.at(i - 1) - poly.at(i - 2), poly.at(i) - poly.at(i - 1))) {
298  return false;
299  }
300  }
301  return (isConnectionConvex(poly.back() - poly.at(poly.size() - 2), poly.front() - poly.back()) &&
302  isConnectionConvex(poly.front() - poly.back(), poly.at(1) - poly.front()));
303 }
304 
306  double areaSum{0.};
307  for (auto i = 0ul; i < parts.size(); ++i) {
308  areaSum += boost::geometry::area(parts.at(i));
309  for (auto j = i + 1; j < parts.size(); ++j) {
310  BasicPolygon2d intersection;
311  boost::geometry::intersection(parts.at(i), parts.at(j), intersection);
312  EXPECT_DOUBLE_EQ(boost::geometry::area(intersection), 0.);
313  }
314  for (const auto& p : parts.at(i)) {
315  Eigen::Matrix<double, 2, 1> pCopy(p.x(), p.y());
316  EXPECT_DOUBLE_EQ(boost::geometry::distance(pCopy, poly), 0.);
317  }
318  }
319  EXPECT_DOUBLE_EQ(areaSum, boost::geometry::area(poly));
320 }
321 
323  auto t1 = lanelet::geometry::convexPartition(this->poly3);
324  EXPECT_EQ(t1.size(), 1ul);
325  EXPECT_TRUE(isConvex(t1.front()));
326  checkPartitionConsistency(BasicPolygon2d(this->poly3.basicBegin(), this->poly3.basicEnd()), t1);
327  auto t2 = lanelet::geometry::convexPartition(this->simpleStar);
328  EXPECT_EQ(t2.size(), 2ul);
329  for (const auto& poly : t2) {
330  EXPECT_TRUE(isConvex(poly));
331  }
332  checkPartitionConsistency(BasicPolygon2d(this->simpleStar.basicBegin(), this->simpleStar.basicEnd()), t2);
333  auto t3 = lanelet::geometry::convexPartition(this->fancyStar);
334  EXPECT_EQ(t3.size(), 2ul);
335  for (const auto& poly : t3) {
336  EXPECT_TRUE(isConvex(poly));
337  }
338  checkPartitionConsistency(BasicPolygon2d(this->fancyStar.basicBegin(), this->fancyStar.basicEnd()), t3);
339 }
340 
342  auto isIn = [](const geometry::IndexedTriangle& p, const size_t idx) {
343  return std::find(p.begin(), p.end(), idx) != p.end();
344  };
345  auto res = lanelet::geometry::triangulate(this->poly3);
346  ASSERT_EQ(res.size(), 2ul);
347  EXPECT_EQ(res.front().size(), 3ul);
348  EXPECT_EQ(res.back().size(), 3ul);
349  const auto& t1 = res.front();
350  const auto& t2 = res.back();
351  EXPECT_TRUE((isIn(t1, 1) && isIn(t1, 3) && isIn(t2, 1) && isIn(t2, 3)) ||
352  (isIn(t1, 0) && isIn(t1, 2) && isIn(t2, 0) && isIn(t2, 2)));
353 }
iterator insert(iterator position, const PointType &point)
Inserts an element at a specific position.
Polygon with access to primitive points.
iterator end()
mutable iterator to the end of the elements of this vector.
BasicPoint p
testing::Types< Polygon2d, Polygon3d, ConstPolygon2d, ConstPolygon3d, ConstHybridPolygon2d, ConstHybridPolygon3d > PrimitivePolygons
auto find(ContainerT &&c, const ValueT &val)
Definition: Utilities.h:186
static constexpr const char Curbstone[]
Definition: Attribute.h:283
An mutable clockwise oriented, open (ie start point != end point) polygon in 2d.
testing::Types< ConstHybridPolygon2d, ConstHybridPolygon3d, CompoundHybridPolygon2d, CompoundHybridPolygon3d > HybridPolygons
An immutable clockwise oriented, open (ie start point != end point) polygon in 2d.
testing::Types< Polygon2d, Polygon3d, ConstPolygon2d, ConstPolygon3d, ConstHybridPolygon2d, ConstHybridPolygon3d, CompoundPolygon2d, CompoundPolygon3d, CompoundHybridPolygon2d, CompoundHybridPolygon3d > AllPolygons
int64_t Id
Definition: Forward.h:198
Eigen::Vector3d BasicPoint3d
a simple 3d-point
BasicPolygon2d toPolygon< BasicPolygon2d >(const Polygon3d &p)
Combines multiple linestrings to one polygon in 3d that returns BasicPoint3d.
#define TYPED_TEST_SUITE
void randomSubdivide(Polygon3d &poly, const size_t after, const int nNew, Id id) const
testing::Types< Polygon2d, Polygon3d > MutablePolygons
ConstPolygon2d toPolygon< ConstPolygon2d >(const Polygon3d &p)
Primitive 2d polygon with basic points.
A normal 3d linestring with immutable data.
BoundingBox2d bbox
const VectorType &() min() const
std::array< size_t, 3 > IndexedTriangle
size_t size() const noexcept
Return the number of points in this linestring.
Combines multiple linestrings to one polygon in 3d.
BasicPoint p2
Eigen::AlignedBox3d BoundingBox3d
Convenience type for an axis aligned bounding box in 3d.
testing::Types< Polygon3d, ConstPolygon3d, ConstHybridPolygon3d, CompoundPolygon3d, CompoundHybridPolygon3d > ThreeDPolygons
static constexpr const char Type[]
Definition: Attribute.h:204
A mutable 3d point.
testing::Types< BasicPolygon2d, Polygon2d, ConstPolygon2d, ConstHybridPolygon2d, CompoundPolygon2d, CompoundHybridPolygon2d > TwoDAndBasicPolygons
const VectorType &() max() const
bool isConvex(const BasicPolygon2d &poly)
IndexedTriangles triangulate(const BasicPolygon2d &poly)
TYPED_TEST(MutablePolygonsTest, id)
Polygon with access to primitive points.
PolygonT composePolygon(std::initializer_list< ConstLineString3d > list)
PolygonT simpleStar
testing::Types< ConstHybridPolygon2d, CompoundHybridPolygon2d > HybridPolygonsTwoD
Polygon2d toPolygon< Polygon2d >(const Polygon3d &p)
IfAr< AreaT, BoundingBox2d > boundingBox2d(const AreaT &area)
calculates an up-right 2d bounding box
Optional< double > distance
testing::Types< BasicPolygon3d, Polygon3d, ConstPolygon3d, ConstHybridPolygon3d, CompoundPolygon3d, CompoundHybridPolygon3d > ThreeDAndBasicPolygons
auto crossProd(const Eigen::Matrix< double, 2, 1 > &p1, const Eigen::Matrix< double, 2, 1 > &p2)
Axis-Aligned bounding box in 2d.
void subdivide(Polygon3d &poly, const int n, Id id)
A mutable clockwise oriented, open (ie start point != end point) polygon in 3d.
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > BasicPoint2d
a simple 2d-point
An immutable clockwise oriented, open (ie start point != end point) polygon in 3d.
BasicPoint p1
Combines multiple linestrings to one polygon in 2d that returns BasicPoint2d.
auto getZ(const T &p) -> std::enable_if_t<!traits::is2D< T >(), double >
Id id
Definition: LaneletMap.cpp:63
auto toBasicPoint(const PointT &point) -> std::enable_if_t< PointTraits< PointT >::IsPrimitive, BasicPointT< PointT >>
Definition: Traits.h:165
BasicPolygons2d convexPartition(const BasicPolygon2d &poly)
IfPoly< Polygon2dT, IndexedTriangles > triangulate(const Polygon2dT &poly)
Split a concave polygon into triangles.
BasicPolygon3d toPolygon< BasicPolygon3d >(const Polygon3d &p)
BoundingBox2d to2D(const BoundingBox3d &primitive)
Primitive 3d polygon with basic points.
Combines multiple linestrings to one polygon in 2d.
testing::Types< Polygon2d, Polygon3d, ConstPolygon2d, ConstPolygon3d > NormalPolygons
IfPoly< Polygon2dT, BasicPolygons2d > convexPartition(const Polygon2dT &poly)
Split a concave polygon into convex parts.
T toPolygon(const Polygon3d &p)
testing::Types< Polygon2d, Polygon3d, ConstPolygon2d, ConstPolygon3d, CompoundPolygon2d, CompoundPolygon3d > NonHybridPolygons
void checkPartitionConsistency(const BasicPolygon2d &poly, const BasicPolygons2d &parts)
bool isConnectionConvex(const BasicPoint2d &seg1, const BasicPoint2d &seg2, const double eps=4 *std::numeric_limits< double >::epsilon())
testing::Types< Polygon2d, ConstPolygon2d, ConstHybridPolygon2d, CompoundPolygon2d, CompoundHybridPolygon2d > TwoDPolygons
double distance2d(const RegulatoryElement &regElem, const BasicPoint2d &p)
iterator begin()
mutable iterator for the elements of this vector.
IfAr< AreaT, BoundingBox3d > boundingBox3d(const AreaT &area)
calculates 3d bounding box
std::vector< BasicPolygon2d > BasicPolygons2d
Definition: Forward.h:153


lanelet2_core
Author(s): Fabian Poggenhans
autogenerated on Tue Jun 6 2023 02:23:32