1 #include <boost/polygon/voronoi.hpp> 17 using type = point_concept;
25 return toVoronoi((orient == HORIZONTAL) ? point.x() : point.y());
36 assert(
false &&
"boost polygon should never call this!");
42 using type = point_concept;
50 return toVoronoi((orient == HORIZONTAL) ? point.x() : point.y());
61 assert(
false &&
"boost polygon should never call this!");
67 using type = point_concept;
75 return toVoronoi((orient == HORIZONTAL) ? point.x() : point.y());
85 assert(
false &&
"boost polygon should never call this!");
95 using IndexedPolygon = std::vector<size_t>;
96 using IndexedPolygons = std::vector<IndexedPolygon>;
99 const double eps = 4 * std::numeric_limits<double>::epsilon()) {
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;
111 return (seg.first == 0 || seg.second == 0) &&
distance + 1 == polySize;
114 using PolygonIterator = IndexedPolygon::const_iterator;
115 using PolygonIteratorPair = std::pair<PolygonIterator, PolygonIterator>;
116 using VoronoiGraph = boost::polygon::voronoi_diagram<double>;
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");
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");
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);
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)}};
141 IndexedSegments getSegmentsExcept(
const IndexedPolygon&
p,
const IndexedSegment& excluded) {
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);
153 IndexedSegments getSegmentIndexesExcept(
const IndexedPolygon& p,
const IndexedSegment& excluded) {
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()});
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);
175 IndexedPolygon merged;
176 IndexedSegments addedSegments;
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());
185 poly.insert(std::next(npIt.second,
std::distance(std::next(mergeIt.second), toMerge.end())), toMerge.begin(),
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());
193 result.merged = poly;
194 insertPolygonExcept(result.merged, toMerge, border);
198 using MetricSegmentPair = std::pair<BasicPoint2d, BasicPoint2d>;
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);
205 metricPolygon.at(newSegments.second.second) - metricPolygon.at(newSegments.second.first);
206 return {newFirst, newSecond};
208 double getOpposingAngle(
const MetricSegmentPair& segmentPair,
const size_t polySize) {
209 return polySize != 3 ? -1. : segmentPair.first.normalized().dot(segmentPair.second.normalized());
211 bool connectionConvex(
const MetricSegmentPair& local,
const MetricSegmentPair& extension) {
214 using AdjacentPolygons = std::map<IndexedSegment, size_t>;
215 using Visited = std::set<size_t>;
217 IndexedSegment border;
220 using Gates = std::vector<Gate>;
221 Gates makeGates(
const IndexedSegments& borders,
const size_t src) {
228 using Heads = std::vector<Head>;
230 IndexedSegment border;
231 double opposingAngle;
232 MetricSegmentPair connectors;
235 using Connections = std::vector<Connection>;
236 struct TraversalResult {
237 std::vector<IndexedPolygon> convex;
241 template <
typename K,
typename V>
246 using ElementType = std::pair<K, V>;
247 using ContainerType = std::vector<ElementType>;
249 void append(
const KeyType& key,
const ValueType& value) {
251 throw std::runtime_error(
"Can't add to frozen vector map");
253 storage_.emplace_back(ElementType{key, value});
255 void append(
const ElementType& elem) {
257 throw std::runtime_error(
"Can't add to frozen vector map");
259 storage_.emplace_back(elem);
263 std::sort(storage_.begin(), storage_.end());
268 const ValueType& at(
const KeyType& key)
const {
270 throw std::runtime_error(
"Can't search in unfrozen vector map");
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");
281 ContainerType storage_;
284 using Adjacencies = VectorMap<IndexedSegment, size_t>;
286 class SegmentationData {
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);
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(); }
300 Adjacencies adjacent_;
301 std::vector<IndexedPolygon> convexParts_;
304 Connections findGates(
const BasicPolygon2d& metricPolygon,
const Gates& candidates,
const Visited& overallVisited,
305 const SegmentationData& segData) {
307 for (
const auto& gate : candidates) {
308 if (isBorder(gate.border, metricPolygon.size())) {
311 const auto opposingPolygonIdx = segData.getDestination(gate);
312 if (overallVisited.find(opposingPolygonIdx) != overallVisited.end()) {
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});
321 std::sort(gates.begin(), gates.end(),
322 [](
const auto& lhs,
const auto& rhs) {
return lhs.opposingAngle < rhs.opposingAngle; });
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);
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()) {
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);
346 closedGates.emplace_back(conn);
350 traversalResult.convex.emplace_back(head.poly);
352 for (
const auto& conn : closedGates) {
353 if (traversalResult.visited.find(conn.newPoly) != traversalResult.visited.end()) {
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);
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);
374 for (
size_t i{0}; i < idxs.size(); ++i) {
375 res[i] = poly.at(idxs.at(i));
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();
388 bool isTriangular(
const IndexedPolygon& poly) {
return poly.size() == 3; }
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();
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);
412 t.addSegment({lastIdx, curIdx}, i);
413 t.addConvex(curPoly, i);
419 if (poly.size() < 3) {
420 throw GeometryError(
"Can't partition a polygon with less than 3 points");
423 auto segData = makeVoronoi(poly);
425 auto indexed = mergeTriangulation(poly, segData);
426 for (
const auto& i : indexed) {
427 res.emplace_back(extractPolygon(poly, i));
433 if (poly.size() < 3) {
434 throw GeometryError(
"Can't triangulate a polygon with less than 3 points");
437 res.reserve(poly.size() - 2);
438 auto segData = makeVoronoi(poly);
439 for (
const auto& i : segData.getConvexPolygons()) {
440 if (isTriangular(i)) {
std::int64_t VoronoiPrecision
auto find(ContainerT &&c, const ValueT &val)
VoronoiPrecision coordinate_type
Thrown when a geometric operation is not valid.
auto crossProd(const BasicPoint3d &p1, const BasicPoint3d &p2)
VoronoiPrecision coordinate_type
VoronoiPrecision coordinate_type
VoronoiPrecision coordinate_type
Primitive 2d polygon with basic points.
std::array< size_t, 3 > IndexedTriangle
Eigen::Matrix< double, 2, 1 > AlignedBasicPoint2d
IndexedTriangles triangulate(const BasicPolygon2d &poly)
VoronoiPrecision coordinate_type
Optional< double > distance
auto transform(Container &&c, Func f)
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > BasicPoint2d
a simple 2d-point
VoronoiPrecision coordinate_type
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