PolygonTriangulationGeometry.cpp
Go to the documentation of this file.
1 #include <boost/polygon/voronoi.hpp>
2 
4 
5 using VoronoiPrecision = std::int64_t;
6 
7 namespace {
8 VoronoiPrecision toVoronoi(double v) { return static_cast<VoronoiPrecision>(v * 100); }
9 } // namespace
10 
11 using AlignedBasicPoint2d = Eigen::Matrix<double, 2, 1>;
12 
13 namespace boost {
14 namespace polygon {
15 template <>
16 struct geometry_concept<lanelet::ConstPoint2d> {
17  using type = point_concept; // NOLINT
18 };
19 
20 // specialize getter
21 template <>
22 struct point_traits<lanelet::ConstPoint2d> {
23  using coordinate_type = VoronoiPrecision; // NOLINT
24  static inline coordinate_type get(const lanelet::ConstPoint2d& point, const orientation_2d& orient) {
25  return toVoronoi((orient == HORIZONTAL) ? point.x() : point.y());
26  }
27 };
28 
29 // specialize setter
30 template <>
31 struct point_mutable_traits<lanelet::ConstPoint2d> {
32  using coordinate_type = VoronoiPrecision; // NOLINT
33 
34  static inline void set(lanelet::ConstPoint2d& /*point*/, const orientation_2d& /*orient*/,
35  VoronoiPrecision /*value*/) {
36  assert(false && "boost polygon should never call this!");
37  }
38 };
39 
40 template <>
41 struct geometry_concept<lanelet::BasicPoint2d> {
42  using type = point_concept; // NOLINT
43 };
44 
45 // specialize getter
46 template <>
47 struct point_traits<lanelet::BasicPoint2d> {
48  using coordinate_type = VoronoiPrecision; // NOLINT
49  static inline coordinate_type get(const lanelet::BasicPoint2d& point, const orientation_2d& orient) {
50  return toVoronoi((orient == HORIZONTAL) ? point.x() : point.y());
51  }
52 };
53 
54 // specialize setter
55 template <>
56 struct point_mutable_traits<lanelet::BasicPoint2d> {
57  using coordinate_type = VoronoiPrecision; // NOLINT
58 
59  static inline void set(lanelet::BasicPoint2d& /*point*/, const orientation_2d& /*orient*/,
60  VoronoiPrecision /*value*/) {
61  assert(false && "boost polygon should never call this!");
62  }
63 };
64 
65 template <>
66 struct geometry_concept<AlignedBasicPoint2d> {
67  using type = point_concept; // NOLINT
68 };
69 
70 // specialize getter
71 template <>
72 struct point_traits<AlignedBasicPoint2d> {
73  using coordinate_type = VoronoiPrecision; // NOLINT
74  static inline coordinate_type get(const AlignedBasicPoint2d& point, const orientation_2d& orient) {
75  return toVoronoi((orient == HORIZONTAL) ? point.x() : point.y());
76  }
77 };
78 
79 // specialize setter
80 template <>
81 struct point_mutable_traits<AlignedBasicPoint2d> {
82  using coordinate_type = VoronoiPrecision; // NOLINT
83 
84  static inline void set(AlignedBasicPoint2d& /*point*/, const orientation_2d& /*orient*/, VoronoiPrecision /*value*/) {
85  assert(false && "boost polygon should never call this!");
86  }
87 };
88 } // namespace polygon
89 } // namespace boost
90 
91 namespace {
92 using namespace lanelet;
93 using namespace lanelet::geometry;
94 
95 using IndexedPolygon = std::vector<size_t>;
96 using IndexedPolygons = std::vector<IndexedPolygon>;
97 
98 bool isConnectionConvex(const BasicPoint2d& seg1, const BasicPoint2d& seg2,
99  const double eps = 4 * std::numeric_limits<double>::epsilon()) {
100  return lanelet::geometry::internal::crossProd(seg1.normalized(), seg2.normalized()).z() <= eps;
101 }
102 using IndexedSegment = std::pair<size_t, size_t>;
103 using IndexedSegments = std::vector<IndexedSegment>;
104 using IndexedSegmentPair = std::pair<IndexedSegment, IndexedSegment>;
105 IndexedSegment flip(const IndexedSegment& seg) { return std::make_pair(seg.second, seg.first); }
106 bool isBorder(const IndexedSegment& seg, const size_t polySize) {
107  const auto distance = seg.first > seg.second ? seg.first - seg.second : seg.second - seg.first;
108  if (distance == 1) {
109  return true;
110  }
111  return (seg.first == 0 || seg.second == 0) && distance + 1 == polySize;
112 }
113 
114 using PolygonIterator = IndexedPolygon::const_iterator;
115 using PolygonIteratorPair = std::pair<PolygonIterator, PolygonIterator>;
116 using VoronoiGraph = boost::polygon::voronoi_diagram<double>;
117 
118 PolygonIteratorPair getAlignedIterators(const IndexedPolygon& poly, const IndexedSegment& segment) {
119  if (poly.size() < 3) {
120  throw InvalidInputError("Can't find segment from polygon with less than 3 vertices");
121  }
122  PolygonIteratorPair itPair = std::make_pair(std::find(poly.begin(), poly.end(), segment.first),
123  std::find(poly.begin(), poly.end(), segment.second));
124  if (itPair.first == poly.end() || itPair.second == poly.end()) {
125  throw InvalidInputError("Index " + std::to_string(segment.first) + "-" + std::to_string(segment.second) +
126  " not found in indices");
127  }
128  const auto dist = std::distance(itPair.first, itPair.second);
129  if (dist == -1 || dist == poly.size() - 1) {
130  std::swap(itPair.first, itPair.second);
131  }
132  return itPair;
133 }
134 
135 IndexedSegmentPair getAdjacent(const IndexedPolygon& poly, const IndexedSegment& seg) {
136  auto itP = getAlignedIterators(poly, seg);
137  return {{*itP.first, itP.first == poly.begin() ? poly.back() : *std::prev(itP.first)},
138  {*itP.second, itP.second == std::prev(poly.end()) ? poly.front() : *std::next(itP.second)}};
139 }
140 
141 IndexedSegments getSegmentsExcept(const IndexedPolygon& p, const IndexedSegment& excluded) {
142  IndexedSegments res;
143  res.reserve(p.size() - 1);
144  for (size_t i = 0; i < p.size(); ++i) {
145  IndexedSegment seg{p.at(i), p.at((i + 1) % p.size())};
146  if (seg != excluded && seg != flip(excluded)) {
147  res.emplace_back(seg);
148  }
149  }
150  return res;
151 }
152 
153 IndexedSegments getSegmentIndexesExcept(const IndexedPolygon& p, const IndexedSegment& excluded) {
154  IndexedSegments res;
155  res.reserve(p.size() - 1);
156  for (size_t i = 0; i < p.size(); ++i) {
157  IndexedSegment seg{p.at(i), p.at((i + 1) % p.size())};
158  if (seg != excluded && seg != flip(excluded)) {
159  res.emplace_back(IndexedSegment{i, (i + 1) % p.size()});
160  }
161  }
162  return res;
163 }
164 
165 IndexedPolygon removeSegment(const IndexedPolygon& poly, const IndexedSegment& excluded) {
166  auto itP = getAlignedIterators(poly, excluded);
167  IndexedPolygon res(std::next(itP.second), std::next(itP.first) == poly.end() ? itP.first : poly.end());
168  if (itP.first != std::prev(poly.end())) {
169  res.insert(res.end(), poly.begin(), itP.first);
170  }
171  return res;
172 }
173 
174 struct MergeResult {
175  IndexedPolygon merged;
176  IndexedSegments addedSegments;
177 };
178 
179 void insertPolygonExcept(IndexedPolygon& poly, const IndexedPolygon& toMerge, const IndexedSegment& border) {
180  auto mergeIt = getAlignedIterators(toMerge, border);
181  auto npIt = getAlignedIterators(poly, border);
182  auto noWrap = std::next(mergeIt.first) == toMerge.end();
183  poly.insert(npIt.second, std::next(mergeIt.second), noWrap ? mergeIt.first : toMerge.end());
184  if (!noWrap) {
185  poly.insert(std::next(npIt.second, std::distance(std::next(mergeIt.second), toMerge.end())), toMerge.begin(),
186  mergeIt.first);
187  }
188 }
189 
190 MergeResult merge(const IndexedPolygon& poly, const IndexedPolygon& toMerge, const IndexedSegment& border) {
191  MergeResult result{{}, getSegmentsExcept(toMerge, border)};
192  result.merged.reserve(poly.size() + toMerge.size()); // das hier ist die einzige Allokation
193  result.merged = poly;
194  insertPolygonExcept(result.merged, toMerge, border);
195  return result;
196 }
197 
198 using MetricSegmentPair = std::pair<BasicPoint2d, BasicPoint2d>;
199 
200 MetricSegmentPair getMetricAdjacentSegmentPair(const BasicPolygon2d& metricPolygon, const IndexedPolygon& poly,
201  const IndexedSegment& segment) {
202  const auto newSegments = getAdjacent(poly, segment);
203  const BasicPoint2d newFirst = metricPolygon.at(newSegments.first.second) - metricPolygon.at(newSegments.first.first);
204  const BasicPoint2d newSecond =
205  metricPolygon.at(newSegments.second.second) - metricPolygon.at(newSegments.second.first);
206  return {newFirst, newSecond};
207 }
208 double getOpposingAngle(const MetricSegmentPair& segmentPair, const size_t polySize) {
209  return polySize != 3 ? -1. : segmentPair.first.normalized().dot(segmentPair.second.normalized());
210 }
211 bool connectionConvex(const MetricSegmentPair& local, const MetricSegmentPair& extension) {
212  return isConnectionConvex(-local.first, extension.second) && isConnectionConvex(-extension.first, local.second);
213 }
214 using AdjacentPolygons = std::map<IndexedSegment, size_t>;
215 using Visited = std::set<size_t>;
216 struct Gate {
217  IndexedSegment border;
218  size_t source;
219 };
220 using Gates = std::vector<Gate>;
221 Gates makeGates(const IndexedSegments& borders, const size_t src) {
222  return utils::transform(borders, [&](auto& b) { return Gate{b, src}; });
223 }
224 struct Head {
225  IndexedPolygon poly;
226  Gates gates; // run ccw
227 };
228 using Heads = std::vector<Head>;
229 struct Connection {
230  IndexedSegment border;
231  double opposingAngle;
232  MetricSegmentPair connectors;
233  size_t newPoly;
234 };
235 using Connections = std::vector<Connection>;
236 struct TraversalResult {
237  std::vector<IndexedPolygon> convex;
238  Visited visited;
239 };
240 
241 template <typename K, typename V>
242 class VectorMap {
243  public:
244  using KeyType = K;
245  using ValueType = V;
246  using ElementType = std::pair<K, V>;
247  using ContainerType = std::vector<ElementType>;
248 
249  void append(const KeyType& key, const ValueType& value) {
250  if (frozen_) {
251  throw std::runtime_error("Can't add to frozen vector map");
252  }
253  storage_.emplace_back(ElementType{key, value});
254  }
255  void append(const ElementType& elem) {
256  if (frozen_) {
257  throw std::runtime_error("Can't add to frozen vector map");
258  }
259  storage_.emplace_back(elem);
260  }
261  void freeze() {
262  if (!frozen_) {
263  std::sort(storage_.begin(), storage_.end());
264  frozen_ = true;
265  }
266  }
267 
268  const ValueType& at(const KeyType& key) const {
269  if (!frozen_) {
270  throw std::runtime_error("Can't search in unfrozen vector map");
271  }
272  auto it = std::lower_bound(storage_.begin(), storage_.end(), key,
273  [](auto& value, const KeyType& k) { return value.first < k; });
274  if (it == storage_.end() || key != it->first) {
275  throw std::runtime_error("Item not found in vector map");
276  }
277  return it->second;
278  }
279 
280  private:
281  ContainerType storage_;
282  bool frozen_{false};
283 };
284 using Adjacencies = VectorMap<IndexedSegment, size_t>;
285 
286 class SegmentationData {
287  public:
288  explicit SegmentationData(const size_t convexPartsSize) { convexParts_.resize(convexPartsSize); }
289  void addConvex(const IndexedPolygon& convexPoly, const int idx) { convexParts_[idx] = convexPoly; }
290  void addSegment(const IndexedSegment& segment, const size_t adjacentPolygonIdx) {
291  adjacent_.append(flip(segment), adjacentPolygonIdx);
292  }
293  const Adjacencies& getAdjacencies() const { return adjacent_; }
294  const IndexedPolygons& getConvexPolygons() const { return convexParts_; }
295  size_t getDestination(const Gate& gate) const { return adjacent_.at(gate.border); }
296  const IndexedPolygon& getPolygonByIdx(const size_t idx) const { return convexParts_.at(idx); }
297  void freeze() { adjacent_.freeze(); }
298 
299  private:
300  Adjacencies adjacent_;
301  std::vector<IndexedPolygon> convexParts_;
302 };
303 
304 Connections findGates(const BasicPolygon2d& metricPolygon, const Gates& candidates, const Visited& overallVisited,
305  const SegmentationData& segData) {
306  Connections gates;
307  for (const auto& gate : candidates) {
308  if (isBorder(gate.border, metricPolygon.size())) {
309  continue;
310  }
311  const auto opposingPolygonIdx = segData.getDestination(gate);
312  if (overallVisited.find(opposingPolygonIdx) != overallVisited.end()) {
313  continue;
314  }
315  const auto& opposingPoly = segData.getPolygonByIdx(opposingPolygonIdx);
316  auto metricSegPair = getMetricAdjacentSegmentPair(metricPolygon, opposingPoly, gate.border);
317  gates.emplace_back(Connection{gate.border, getOpposingAngle(metricSegPair, opposingPoly.size()), metricSegPair,
318  opposingPolygonIdx});
319  }
320 
321  std::sort(gates.begin(), gates.end(),
322  [](const auto& lhs, const auto& rhs) { return lhs.opposingAngle < rhs.opposingAngle; });
323  return gates;
324 }
325 
326 void updateState(Head& head, TraversalResult& traversalResult, const IndexedPolygon& newPoly, const Connection& conn) {
327  traversalResult.visited.insert(conn.newPoly);
328  auto mergeResult = merge(head.poly, newPoly, conn.border);
329  head.poly = std::move(mergeResult.merged);
330  head.gates = makeGates(mergeResult.addedSegments, conn.newPoly);
331 }
332 
333 void traverse(const BasicPolygon2d& metricPolygon, Head& head, TraversalResult& traversalResult,
334  const SegmentationData& segData, const bool isOrigin) {
335  auto gates = findGates(metricPolygon, head.gates, traversalResult.visited, segData);
336  std::vector<Connection> closedGates;
337  for (const auto& conn : gates) {
338  if (traversalResult.visited.find(conn.newPoly) != traversalResult.visited.end()) {
339  continue;
340  }
341  const auto headsideAdjacent = getMetricAdjacentSegmentPair(metricPolygon, head.poly, conn.border);
342  if (connectionConvex(headsideAdjacent, conn.connectors)) {
343  updateState(head, traversalResult, segData.getPolygonByIdx(conn.newPoly), conn);
344  traverse(metricPolygon, head, traversalResult, segData, false);
345  } else {
346  closedGates.emplace_back(conn);
347  }
348  }
349  if (isOrigin) {
350  traversalResult.convex.emplace_back(head.poly);
351  }
352  for (const auto& conn : closedGates) {
353  if (traversalResult.visited.find(conn.newPoly) != traversalResult.visited.end()) {
354  continue;
355  }
356  const auto& newPoly = segData.getPolygonByIdx(conn.newPoly);
357  Head newHead{newPoly, makeGates(getSegmentsExcept(newPoly, conn.border), conn.newPoly)};
358  traverse(metricPolygon, newHead, traversalResult, segData, true);
359  }
360 }
361 
362 std::vector<IndexedPolygon> mergeTriangulation(const BasicPolygon2d& metricPolygon, const SegmentationData& segData) {
363  IndexedSegment init{1, 0};
364  auto firstPolyIdx = segData.getAdjacencies().at(init);
365  const auto& firstPoly = segData.getPolygonByIdx(firstPolyIdx);
366  TraversalResult t{{}, Visited{firstPolyIdx}};
367  Head initHead{firstPoly, makeGates(getSegmentsExcept(firstPoly, init), firstPolyIdx)};
368  traverse(metricPolygon, initHead, t, segData, true);
369  return t.convex;
370 }
371 
372 BasicPolygon2d extractPolygon(const BasicPolygon2d& poly, const std::vector<size_t>& idxs) {
373  BasicPolygon2d res(idxs.size());
374  for (size_t i{0}; i < idxs.size(); ++i) {
375  res[i] = poly.at(idxs.at(i));
376  }
377  return res;
378 }
379 
380 void subdivide(IndexedTriangles& target, const IndexedPolygon& poly) {
381  target.reserve(target.size() + poly.size() - 2);
382  for (auto first = std::next(poly.begin()), second = std::next(poly.begin(), 2); second != poly.end();
383  ++second, ++first) {
384  target.emplace_back(IndexedTriangle{poly.front(), *first, *second});
385  }
386 }
387 
388 bool isTriangular(const IndexedPolygon& poly) { return poly.size() == 3; }
389 
390 SegmentationData makeVoronoi(const BasicPolygon2d& poly) {
391  VoronoiGraph graph;
392  boost::polygon::construct_voronoi(poly.begin(), poly.end(), &graph);
393  SegmentationData t(graph.num_vertices());
394  auto advance = [](const VoronoiGraph::edge_type*& edge, VoronoiGraph::cell_type::source_index_type& lastIndex,
395  VoronoiGraph::cell_type::source_index_type& curIndex) {
396  lastIndex = curIndex;
397  edge = edge->rot_prev();
398  curIndex = edge->cell()->source_index();
399  };
400  for (size_t i{0}; i < graph.num_vertices(); ++i) {
401  const auto* const firstEdge = graph.vertices().at(i).incident_edge();
402  auto startIdx = firstEdge->cell()->source_index();
403  const auto* curEdge = firstEdge->rot_prev();
404  auto curIdx = curEdge->cell()->source_index();
405  auto lastIdx = startIdx;
406  IndexedPolygon curPoly{startIdx};
407  while (curIdx != startIdx) {
408  t.addSegment({lastIdx, curIdx}, i);
409  curPoly.emplace_back(curIdx);
410  advance(curEdge, lastIdx, curIdx);
411  }
412  t.addSegment({lastIdx, curIdx}, i);
413  t.addConvex(curPoly, i);
414  }
415  return t;
416 }
417 
418 BasicPolygons2d convexPartitionImpl(const BasicPolygon2d& poly) {
419  if (poly.size() < 3) {
420  throw GeometryError("Can't partition a polygon with less than 3 points");
421  }
422  BasicPolygons2d res;
423  auto segData = makeVoronoi(poly);
424  segData.freeze();
425  auto indexed = mergeTriangulation(poly, segData);
426  for (const auto& i : indexed) {
427  res.emplace_back(extractPolygon(poly, i));
428  }
429  return res;
430 }
431 
432 IndexedTriangles triangulateImpl(const BasicPolygon2d& poly) {
433  if (poly.size() < 3) {
434  throw GeometryError("Can't triangulate a polygon with less than 3 points");
435  }
436  IndexedTriangles res;
437  res.reserve(poly.size() - 2);
438  auto segData = makeVoronoi(poly);
439  for (const auto& i : segData.getConvexPolygons()) {
440  if (isTriangular(i)) {
441  res.emplace_back(IndexedTriangle{i.at(0), i.at(1), i.at(2)});
442  } else {
443  subdivide(res, i);
444  }
445  }
446  return res;
447 }
448 } // namespace
449 
450 namespace lanelet {
451 namespace geometry {
452 namespace internal {
453 BasicPolygons2d convexPartition(const BasicPolygon2d& poly) { return convexPartitionImpl(poly); }
454 IndexedTriangles triangulate(const BasicPolygon2d& poly) { return triangulateImpl(poly); }
455 } // namespace internal
456 } // namespace geometry
457 } // namespace lanelet
std::int64_t VoronoiPrecision
BasicPoint p
auto find(ContainerT &&c, const ValueT &val)
Definition: Utilities.h:186
Thrown when a geometric operation is not valid.
Definition: Exceptions.h:63
auto crossProd(const BasicPoint3d &p1, const BasicPoint3d &p2)
An immutable 2d point.
Primitive 2d polygon with basic points.
std::array< size_t, 3 > IndexedTriangle
Eigen::Matrix< double, 2, 1 > AlignedBasicPoint2d
IndexedTriangles triangulate(const BasicPolygon2d &poly)
Optional< double > distance
auto transform(Container &&c, Func f)
Definition: Utilities.h:120
Thrown when a function was called with invalid input arguments.
Definition: Exceptions.h:56
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > BasicPoint2d
a simple 2d-point
std::vector< IndexedTriangle > IndexedTriangles
BasicPolygons2d convexPartition(const BasicPolygon2d &poly)
bool isConnectionConvex(const BasicPoint2d &seg1, const BasicPoint2d &seg2, const double eps=4 *std::numeric_limits< double >::epsilon())
std::vector< BasicPolygon2d > BasicPolygons2d
Definition: Forward.h:153


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