File indexing completed on 2024-12-22 04:27:03
0001 /* 0002 SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "poleofinaccessibilityfinder_p.h" 0008 #include "scenegeometry_p.h" 0009 0010 #include <QDebug> 0011 #include <QLineF> 0012 #include <QPolygonF> 0013 0014 #include <cmath> 0015 #include <limits> 0016 0017 using namespace KOSMIndoorMap; 0018 0019 /** Signed distance from @p point to @p polygon, sign indicates inside/outside distance. */ 0020 static double pointToPolygonDistance(const QPointF &point, const QPolygonF &poly) 0021 { 0022 auto dist = std::numeric_limits<double>::max(); 0023 for (auto i = 0; i < poly.size(); ++i) { 0024 const auto p1 = poly.at(i); 0025 const auto p2 = poly.at((i + 1) % poly.size()); 0026 dist = std::min(dist, SceneGeometry::distanceToLine(QLineF(p1, p2), point)); 0027 } 0028 return poly.containsPoint(point, Qt::OddEvenFill) ? dist : -dist; 0029 } 0030 0031 void PoleOfInaccessibilityFinder::CellPriorityQueue::clear() 0032 { 0033 c.clear(); 0034 } 0035 0036 0037 PoleOfInaccessibilityFinder::Cell::Cell(const QPointF &_center, double _size, const QPolygonF &poly) 0038 : center(_center) 0039 , size(_size) 0040 , distance(pointToPolygonDistance(_center, poly)) 0041 { 0042 } 0043 0044 double PoleOfInaccessibilityFinder::Cell::maximumDistance() const 0045 { 0046 return distance + size * std::sqrt(2.0); 0047 } 0048 0049 bool PoleOfInaccessibilityFinder::Cell::operator<(const PoleOfInaccessibilityFinder::Cell &other) const 0050 { 0051 return maximumDistance() < other.maximumDistance(); 0052 } 0053 0054 0055 PoleOfInaccessibilityFinder::PoleOfInaccessibilityFinder() = default; 0056 PoleOfInaccessibilityFinder::~PoleOfInaccessibilityFinder() = default; 0057 0058 QPointF PoleOfInaccessibilityFinder::find(const QPolygonF &poly) 0059 { 0060 const auto boundingBox = poly.boundingRect(); 0061 const auto cellSize = std::min(boundingBox.width(), boundingBox.height()); 0062 auto h = cellSize / 2.0; 0063 if (cellSize == 0.0) { 0064 return boundingBox.center(); 0065 } 0066 0067 // cover polygon with initial cells 0068 for (auto x = boundingBox.left(); x < boundingBox.right(); x += cellSize) { 0069 for (auto y = boundingBox.top(); y < boundingBox.bottom(); y += cellSize) { 0070 m_queue.push(Cell(QPointF(x + h, y + h), h, poly)); 0071 } 0072 } 0073 0074 // initial guesses 0075 auto bestCell = Cell(SceneGeometry::polygonCentroid(poly), 0, poly); 0076 const auto bboxCell = Cell(boundingBox.center(), 0, poly); 0077 if (bboxCell.distance > bestCell.distance) { 0078 bestCell = bboxCell; 0079 } 0080 0081 while (!m_queue.empty()) { 0082 auto cell = m_queue.top(); 0083 m_queue.pop(); 0084 0085 if (cell.distance > bestCell.distance) { 0086 bestCell = cell; 0087 } 0088 // don't recurse into cells that can't provide a better result 0089 if (cell.maximumDistance() - bestCell.distance <= 0.00002) { 0090 continue; 0091 } 0092 0093 h = cell.size / 2.0; 0094 m_queue.push(Cell(QPointF(cell.center.x() - h, cell.center.y() - h), h, poly)); 0095 m_queue.push(Cell(QPointF(cell.center.x() + h, cell.center.y() - h), h, poly)); 0096 m_queue.push(Cell(QPointF(cell.center.x() - h, cell.center.y() + h), h, poly)); 0097 m_queue.push(Cell(QPointF(cell.center.x() + h, cell.center.y() + h), h, poly)); 0098 } 0099 0100 m_queue.clear(); 0101 return bestCell.center; 0102 }