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 }