Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
+557
@@ -0,0 +1,557 @@
|
||||
/**
|
||||
* Namespace for calculations, needed by CollateralDamageSimulation
|
||||
* @RDE_HEADER@
|
||||
* $Id: ppm_msdl_edit.h 52239 2020-10-05 11:33:12Z thoden $
|
||||
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/ParticipantManagement/Gui/MSDL/ppm_msdl_edit.h $
|
||||
* $Author: thoden $
|
||||
* Created by Hannes Fritsche, ESZS, 2024-09-03
|
||||
*/
|
||||
|
||||
#include "cds_calculations.h"
|
||||
|
||||
#include <InfrastructureManagement/ism_dtc.h>
|
||||
#include <ParticipantManagement/ppm_dtc.h>
|
||||
|
||||
#include <LtsCoordTrans/lts_coord_trans.h>
|
||||
|
||||
#include <QPolygonF>
|
||||
#include <QLineF>
|
||||
|
||||
#include <MathUtils/simple_helper.h>
|
||||
|
||||
#include <AreaEffectSimulation/Utils/polygons.h>
|
||||
|
||||
namespace CollateralDamageSimulation
|
||||
{
|
||||
namespace Calculations
|
||||
{
|
||||
|
||||
QHash<int, QPolygonF> getBuildingFootprints(const QVector<int> &buildingIds, const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc, const QPointF &impactPosition)
|
||||
{
|
||||
QHash<int, QPolygonF> footprints;
|
||||
|
||||
if (buildingIds.isEmpty())
|
||||
return footprints;
|
||||
|
||||
footprints.reserve(buildingIds.size());
|
||||
|
||||
for (int buildingId : buildingIds)
|
||||
{
|
||||
const QPolygonF &buildingFootprint = getBuildingFootprint(buildingId, ismDtc);
|
||||
|
||||
// if the impact position is within a building footprint, the building has to be ignored
|
||||
if (buildingFootprint.containsPoint(impactPosition, Qt::OddEvenFill))
|
||||
continue;
|
||||
|
||||
footprints.insert(buildingId, buildingFootprint);
|
||||
}
|
||||
|
||||
return footprints;
|
||||
}
|
||||
|
||||
QPolygonF getBuildingFootprint(int buildingId, const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc)
|
||||
{
|
||||
if (!ismDtc->isMainFootprintEnabled())
|
||||
{
|
||||
ismDtc->enableMainFootprint();
|
||||
ismDtc->waitForHistoricalDataMainFootprint(3000);
|
||||
}
|
||||
|
||||
if (!ismDtc->isMainStructureElementEnabled())
|
||||
{
|
||||
ismDtc->enableMainStructureElement();
|
||||
ismDtc->waitForHistoricalDataMainStructureElement(3000);
|
||||
}
|
||||
|
||||
QPolygonF footprint; // = absolute positions
|
||||
QVector<QPointF> relPositions;
|
||||
const InfrastructureManagement::MainFootprint &mainFootprint = ismDtc->mainFootprint(buildingId);
|
||||
|
||||
relPositions.reserve(mainFootprint.elements().size());
|
||||
|
||||
for (const InfrastructureManagement::FootprintElement &element : mainFootprint.elements())
|
||||
{
|
||||
const Core::RelativePosition &relPos = element.relPosition();
|
||||
|
||||
relPositions.append(QPointF(relPos.offsetX(), relPos.offsetY()));
|
||||
}
|
||||
|
||||
const InfrastructureManagement::MainStructureElement &mainStructureElement = ismDtc->mainStructureElement(buildingId);
|
||||
const Core::GeoPosition &geoPosition = mainStructureElement.geoPosition();
|
||||
const double angleRad = mainStructureElement.angle() * LtsCoordTrans::deg2rad();
|
||||
const QPointF referencePointGeo(geoPosition.lon(), geoPosition.lat());
|
||||
const LtsCoordTrans::UtmCoordinate referencePointUTM = LtsCoordTrans::UtmCoordinate::fromGeographic(referencePointGeo);
|
||||
const QPointF center;
|
||||
|
||||
footprint.reserve(relPositions.size());
|
||||
|
||||
for (const auto &relPosition : qAsConst(relPositions))
|
||||
{
|
||||
const QPointF relPositionRotate = SituationMap::MathUtils::rotate(relPosition, center, angleRad);
|
||||
const QPointF geoPositionRotate = referencePointUTM.geographic(relPositionRotate);
|
||||
|
||||
footprint.append(geoPositionRotate);
|
||||
}
|
||||
|
||||
return footprint;
|
||||
}
|
||||
|
||||
QVector<QPolygonF> getProtectionAreas(const QHash<int, QPolygonF> &buildingFootprints, double radius, const QPointF &impactPosition)
|
||||
{
|
||||
if (isPointInAnyPolygon(buildingFootprints, impactPosition))
|
||||
return {AreaEffectSimulation::Polygons::circle(impactPosition, radius)};
|
||||
|
||||
QPolygonF currentProtectionArea;
|
||||
QVector<QPolygonF> protectionAreas;
|
||||
|
||||
protectionAreas.reserve(buildingFootprints.size());
|
||||
|
||||
for (const QPolygonF ¤tFootprint : buildingFootprints)
|
||||
{
|
||||
currentProtectionArea.clear();
|
||||
currentProtectionArea.reserve(currentFootprint.size() + 360);
|
||||
|
||||
// take the visible points from the center
|
||||
for (const QPointF currentPoint : currentFootprint)
|
||||
{
|
||||
if (isPointVisible(impactPosition, currentPoint, currentFootprint))
|
||||
currentProtectionArea.append(currentPoint);
|
||||
}
|
||||
|
||||
// order the points by angle
|
||||
// each of the two sorting algorithms has a weakness if a point is present which is either directly left or right
|
||||
// of the impact position, so we have to choose one
|
||||
if (currentProtectionArea.boundingRect().center().x() < impactPosition.x())
|
||||
{
|
||||
// does not sort properly if there is a point directly right to center point
|
||||
std::sort(currentProtectionArea.begin(), currentProtectionArea.end(),
|
||||
[&impactPosition](const QPointF &a, const QPointF &b)
|
||||
{
|
||||
double angleA = QLineF(impactPosition, a).angle();
|
||||
double angleB = QLineF(impactPosition, b).angle();
|
||||
|
||||
return angleA > angleB;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// does not sort properly if there is a point directly left to center point
|
||||
std::sort(currentProtectionArea.begin(), currentProtectionArea.end(), [impactPosition](const QPointF &a, const QPointF &b)
|
||||
{
|
||||
double angleA = std::atan2(a.y() - impactPosition.y(), a.x() - impactPosition.x());
|
||||
double angleB = std::atan2(b.y() - impactPosition.y(), b.x() - impactPosition.x());
|
||||
|
||||
return angleA < angleB;
|
||||
});
|
||||
}
|
||||
|
||||
addOuterBounds(currentProtectionArea, impactPosition, radius);
|
||||
|
||||
protectionAreas.append(currentProtectionArea);
|
||||
}
|
||||
|
||||
return mergePolygons(protectionAreas);
|
||||
}
|
||||
|
||||
bool isPointVisible(const QPointF ¢er, const QPointF &point, const QPolygonF &polygon)
|
||||
{
|
||||
for (int i = 0; i < polygon.size(); ++i)
|
||||
{
|
||||
const QPointF ¤tPoint = polygon.at(i);
|
||||
const QPointF &nextPoint = polygon.at((i + 1) % polygon.size());
|
||||
|
||||
if (point == currentPoint || point == nextPoint)
|
||||
continue;
|
||||
|
||||
if (doLinesIntersect(center, point, currentPoint, nextPoint))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool doLinesIntersect(const QPointF &a1, const QPointF &a2, const QPointF &b1, const QPointF &b2)
|
||||
{
|
||||
auto ccw = [](const QPointF &A, const QPointF &B, const QPointF &C)
|
||||
{
|
||||
return ((C.y() - A.y()) * (B.x() - A.x())) > ((B.y() - A.y()) * (C.x() - A.x()));
|
||||
};
|
||||
|
||||
return ccw(a1, b1, b2) != ccw(a2, b1, b2) && ccw(a1, a2, b1) != ccw(a1, a2, b2);
|
||||
}
|
||||
|
||||
void addOuterBounds(QPolygonF &polygon, const QPointF ¢er, double radius)
|
||||
{
|
||||
QPolygonF circle = AreaEffectSimulation::Polygons::circle(center, radius);
|
||||
|
||||
LtsCoordTrans::UtmCoordinate startUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(getStartPosForOuterBounds(polygon, circle));
|
||||
LtsCoordTrans::UtmCoordinate endUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(getEndPosForOuterBounds(polygon, circle));
|
||||
LtsCoordTrans::UtmCoordinate centerUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(center);
|
||||
|
||||
double startRad = std::atan2(startUtm.y - centerUtm.y, startUtm.x - centerUtm.x);
|
||||
double endRad = std::atan2(endUtm.y - centerUtm.y, endUtm.x - centerUtm.x);
|
||||
double angleStep = 5.0 * M_PI / 180.0;
|
||||
|
||||
if (startRad > endRad)
|
||||
endRad += 2 * M_PI;
|
||||
|
||||
for (double angle = startRad; angle < endRad; angle += angleStep)
|
||||
{
|
||||
double x = centerUtm.x + radius * cos(angle);
|
||||
double y = centerUtm.y + radius * sin(angle);
|
||||
|
||||
polygon.prepend(LtsCoordTrans::UtmCoordinate(centerUtm.zone, x, y).geographic());
|
||||
}
|
||||
|
||||
if (endRad > startRad && fmod(endRad - startRad, angleStep) != 0)
|
||||
{
|
||||
double x = centerUtm.x + radius * cos(endRad);
|
||||
double y = centerUtm.y + radius * sin(endRad);
|
||||
|
||||
polygon.prepend(LtsCoordTrans::UtmCoordinate(centerUtm.zone, x, y).geographic());
|
||||
}
|
||||
}
|
||||
|
||||
ParticipantProtectionInfo getParticipantProtectionInfo(const QVector<int> &participantIds, const QVector<QPolygonF> &protectionAreas, const QVector<int> &undergroundBuildingIds, const QVector<int> &directlyHitBuildingIds, const QHash<int, QPolygonF> &buildingFootprints, const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc, const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc)
|
||||
{
|
||||
ParticipantProtectionInfo participantProtectionInfo;
|
||||
|
||||
QHash<int, QPolygonF> directlyHitBuildingFootprints;
|
||||
directlyHitBuildingFootprints.reserve(directlyHitBuildingIds.size());
|
||||
|
||||
for (int buildingId : directlyHitBuildingIds)
|
||||
directlyHitBuildingFootprints.insert(buildingId, buildingFootprints.value(buildingId));
|
||||
|
||||
participantProtectionInfo.withoutProtection.reserve(participantIds.size());
|
||||
participantProtectionInfo.withProtection.reserve(participantIds.size());
|
||||
participantProtectionInfo.participantsInUnderground.reserve(participantIds.size());
|
||||
|
||||
if (!ppmDtc->isParticipantPositionEnabled())
|
||||
{
|
||||
ppmDtc->enableParticipantPosition();
|
||||
ppmDtc->waitForHistoricalDataParticipantPosition(3000);
|
||||
}
|
||||
|
||||
if (!ismDtc->isMainStructureElementEnabled())
|
||||
{
|
||||
ismDtc->enableMainStructureElement();
|
||||
ismDtc->waitForHistoricalDataMainStructureElement(3000);
|
||||
}
|
||||
|
||||
for (int currentParticipantId : participantIds)
|
||||
{
|
||||
const ParticipantManagement::ParticipantPosition ¤tPosition = ppmDtc->participantPosition(currentParticipantId);
|
||||
|
||||
if (undergroundBuildingIds.contains(currentPosition.buildingId()))
|
||||
{
|
||||
participantProtectionInfo.participantsInUnderground.append(currentParticipantId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (directlyHitBuildingIds.contains(currentPosition.buildingId()))
|
||||
{
|
||||
participantProtectionInfo.withoutProtection.append(currentParticipantId);
|
||||
continue;
|
||||
}
|
||||
|
||||
const QPointF geoPosition(currentPosition.pos().lon(), currentPosition.pos().lat());
|
||||
|
||||
if (isPointInAnyPolygon(directlyHitBuildingFootprints, geoPosition))
|
||||
{
|
||||
participantProtectionInfo.withoutProtection.append(currentParticipantId);
|
||||
continue;
|
||||
}
|
||||
|
||||
// participants in buildings are always protected
|
||||
if (currentPosition.roomId() != 0)
|
||||
{
|
||||
participantProtectionInfo.withProtection.append(currentParticipantId);
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if a participant is in a protection area
|
||||
|
||||
for (const QPolygonF ¤tProtectionArea : protectionAreas)
|
||||
{
|
||||
if (currentProtectionArea.containsPoint(geoPosition, Qt::OddEvenFill))
|
||||
{
|
||||
participantProtectionInfo.withProtection.append(currentParticipantId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (participantProtectionInfo.withProtection.contains(currentParticipantId))
|
||||
continue;
|
||||
|
||||
// no protection
|
||||
participantProtectionInfo.withoutProtection.append(currentParticipantId);
|
||||
}
|
||||
|
||||
participantProtectionInfo.withoutProtection.squeeze();
|
||||
participantProtectionInfo.withProtection.squeeze();
|
||||
participantProtectionInfo.participantsInUnderground.squeeze();
|
||||
|
||||
return participantProtectionInfo;
|
||||
}
|
||||
|
||||
QPointF getStartPosForOuterBounds(const QPolygonF &polygon, const QPolygonF &circle)
|
||||
{
|
||||
if (circle.containsPoint(polygon.first(), Qt::OddEvenFill))
|
||||
return polygon.first();
|
||||
|
||||
QPointF firstInnerPoint = polygon.first();
|
||||
|
||||
int index = 0;
|
||||
|
||||
while (!circle.containsPoint(firstInnerPoint, Qt::OddEvenFill))
|
||||
{
|
||||
if (index >= polygon.size() - 1)
|
||||
return QPointF();
|
||||
|
||||
firstInnerPoint = polygon.at(++index);
|
||||
}
|
||||
|
||||
QPointF lastOuterPoint = polygon.at(index - 1);
|
||||
QLineF intersectionLine(firstInnerPoint, lastOuterPoint);
|
||||
|
||||
return firstIntersection(intersectionLine, circle);
|
||||
}
|
||||
|
||||
QPointF getEndPosForOuterBounds(const QPolygonF &polygon, const QPolygonF &circle)
|
||||
{
|
||||
if (circle.containsPoint(polygon.last(), Qt::OddEvenFill))
|
||||
return polygon.last();
|
||||
|
||||
QPointF firstInnerPoint = polygon.last();
|
||||
|
||||
int index = polygon.size();
|
||||
|
||||
while (!circle.containsPoint(firstInnerPoint, Qt::OddEvenFill))
|
||||
{
|
||||
if (index <= 0)
|
||||
return QPointF();
|
||||
|
||||
firstInnerPoint = polygon.at(--index);
|
||||
}
|
||||
|
||||
QPointF lastOuterPoint = polygon.at(index + 1);
|
||||
QLineF intersectionLine(firstInnerPoint, lastOuterPoint);
|
||||
|
||||
return firstIntersection(intersectionLine, circle);
|
||||
}
|
||||
|
||||
QPointF firstIntersection(const QLineF &line, const QPolygonF &circle)
|
||||
{
|
||||
QPointF prevPoint = circle.last();
|
||||
QPointF intersectionPoint;
|
||||
|
||||
for (int index = 0; index < circle.size(); ++index)
|
||||
{
|
||||
const QLineF edge(prevPoint, circle.at(index));
|
||||
|
||||
if (edge.intersects(line, &intersectionPoint) == QLineF::BoundedIntersection)
|
||||
return intersectionPoint;
|
||||
|
||||
prevPoint = circle.at(index);
|
||||
}
|
||||
|
||||
return QPointF();
|
||||
}
|
||||
|
||||
bool isPointInAnyPolygon(const QHash<int, QPolygonF> &polygons, const QPointF &point)
|
||||
{
|
||||
for (auto it = polygons.constBegin(); it != polygons.constEnd(); ++it)
|
||||
{
|
||||
const QPolygonF ¤tPolygon = it.value();
|
||||
|
||||
if (currentPolygon.containsPoint(point, Qt::OddEvenFill))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QVector<QPolygonF> mergePolygons(const QVector<QPolygonF> &geoPolygons)
|
||||
{
|
||||
if (geoPolygons.size() <= 1)
|
||||
return geoPolygons;
|
||||
|
||||
const double scaleFactor = 100000.0;
|
||||
// since geocoordinates are very small the result of QPolygonF::united is very imprecise.
|
||||
// the polygons therefore must be scaled up and later be scaled down again
|
||||
QVector<QPolygonF> mergedPolygons = getScaledPolygons(geoPolygons,scaleFactor);
|
||||
bool hasIntersection = true;
|
||||
|
||||
while (hasIntersection)
|
||||
{
|
||||
hasIntersection = false;
|
||||
|
||||
for (int outerIndex = 0; outerIndex < mergedPolygons.size(); ++outerIndex)
|
||||
{
|
||||
for (int innerIndex = outerIndex + 1; innerIndex < mergedPolygons.size(); ++innerIndex)
|
||||
{
|
||||
if (mergedPolygons.at(outerIndex).intersects(mergedPolygons.at(innerIndex)))
|
||||
{
|
||||
const QPolygonF mergedPolygon = mergedPolygons.at(outerIndex).united(mergedPolygons.at(innerIndex));
|
||||
|
||||
mergedPolygons[outerIndex] = mergedPolygon;
|
||||
mergedPolygons.removeAt(innerIndex);
|
||||
|
||||
hasIntersection = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasIntersection)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getScaledPolygons(mergedPolygons, 1.0 / scaleFactor);
|
||||
}
|
||||
|
||||
QVector<QPolygonF> getScaledPolygons(const QVector<QPolygonF> &polygons, double factor)
|
||||
{
|
||||
QVector<QPolygonF> scaledPolygons;
|
||||
QPolygonF scaledPolygon;
|
||||
|
||||
scaledPolygons.reserve(polygons.size());
|
||||
|
||||
for (const QPolygonF ¤tPolygon : polygons)
|
||||
{
|
||||
scaledPolygon.reserve(currentPolygon.size());
|
||||
|
||||
for (const QPointF ¤tPoint : currentPolygon)
|
||||
scaledPolygon.append(currentPoint * factor);
|
||||
|
||||
scaledPolygons.append(scaledPolygon);
|
||||
|
||||
scaledPolygon.clear();
|
||||
}
|
||||
|
||||
return scaledPolygons;
|
||||
}
|
||||
|
||||
QVector<int> getUndergroundBuildingIds(const QVector<int> &buildingIds, const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc)
|
||||
{
|
||||
if (!ismDtc->isMainStructureElementEnabled())
|
||||
{
|
||||
ismDtc->enableMainStructureElement();
|
||||
ismDtc->waitForHistoricalDataMainStructureElement(3000);
|
||||
}
|
||||
|
||||
QVector<int> undergroundBuildingIds;
|
||||
undergroundBuildingIds.reserve(buildingIds.size());
|
||||
|
||||
// since there is no indicator for underground buildings, we have to parse the buildings name and description
|
||||
// --> known underground buildings area Kanalisation and U-Bahn Tunnel
|
||||
const QString kanal = QStringLiteral("kanaele");
|
||||
const QString uBahn = QStringLiteral("bahn");
|
||||
|
||||
for (int currentBuildingId : buildingIds)
|
||||
{
|
||||
const InfrastructureManagement::MainStructureElement &building = ismDtc->mainStructureElement(currentBuildingId);
|
||||
const QString description = QString::fromStdString(building.description());
|
||||
const QString name = QString::fromStdString(building.name());
|
||||
|
||||
if (!(description.contains(kanal, Qt::CaseInsensitive) ||
|
||||
description.contains(uBahn, Qt::CaseInsensitive) ||
|
||||
name.contains(kanal, Qt::CaseInsensitive) ||
|
||||
name.contains(uBahn, Qt::CaseInsensitive)))
|
||||
continue;
|
||||
|
||||
undergroundBuildingIds.append(currentBuildingId);
|
||||
}
|
||||
|
||||
return undergroundBuildingIds;
|
||||
}
|
||||
|
||||
QList<QPointF> getGeoRect(const QPointF ¢er, double edgeLength)
|
||||
{
|
||||
QList<QPointF> geoRect;
|
||||
const LtsCoordTrans::UtmCoordinate utmCenter = LtsCoordTrans::UtmCoordinate::fromGeographic(center.x(), center.y());
|
||||
const QPointF topLeft(utmCenter.geographic(-(edgeLength / 2.0), edgeLength / 2.0));
|
||||
const QPointF topRight(utmCenter.geographic(edgeLength / 2.0, edgeLength / 2.0));
|
||||
const QPointF bottomRight(utmCenter.geographic(edgeLength / 2.0, -(edgeLength / 2.0)));
|
||||
const QPointF bottomLeft(utmCenter.geographic(-(edgeLength / 2.0), -(edgeLength / 2.0)));
|
||||
|
||||
geoRect.reserve(4);
|
||||
geoRect.append(topLeft);
|
||||
geoRect.append(topRight);
|
||||
geoRect.append(bottomRight);
|
||||
geoRect.append(bottomLeft);
|
||||
|
||||
return geoRect;
|
||||
}
|
||||
|
||||
QVector<int> getDirectlyHitBuildings(const QHash<int, QPolygonF> &footprints, const QPointF &impactPosition)
|
||||
{
|
||||
const double angleStep = 0.2;
|
||||
const int minimumHitCount = 12;
|
||||
QVector<int> directlyHitBuildingIds;
|
||||
|
||||
QHash<int, int> hitBuildingsCount; // <buildingId, hit count>
|
||||
|
||||
directlyHitBuildingIds.reserve(footprints.size());
|
||||
|
||||
QLineF ray(impactPosition, {impactPosition.x() + 1.0, impactPosition.y()});
|
||||
|
||||
for (double currentAngle = 0.0; currentAngle < 360; currentAngle += angleStep)
|
||||
{
|
||||
ray.setAngle(currentAngle);
|
||||
|
||||
int buildingId = getNearestBlockedBuildingId(ray, footprints);
|
||||
|
||||
if (buildingId == -1)
|
||||
continue;
|
||||
|
||||
if (!hitBuildingsCount.contains(buildingId))
|
||||
hitBuildingsCount.insert(buildingId, 0);
|
||||
|
||||
++hitBuildingsCount[buildingId];
|
||||
}
|
||||
|
||||
for (auto it = hitBuildingsCount.constBegin(); it != hitBuildingsCount.constEnd(); ++it)
|
||||
{
|
||||
if (it.value() >= minimumHitCount)
|
||||
directlyHitBuildingIds.append(it.key());
|
||||
}
|
||||
|
||||
directlyHitBuildingIds.squeeze();
|
||||
|
||||
return directlyHitBuildingIds;
|
||||
}
|
||||
|
||||
int getNearestBlockedBuildingId(const QLineF &ray, const QHash<int, QPolygonF> &buildings)
|
||||
{
|
||||
QMap<double, int> distances; // <distance, buildingId>
|
||||
|
||||
for (auto it = buildings.constBegin(); it != buildings.constEnd(); ++it)
|
||||
{
|
||||
const QPolygonF ¤tPolygon = it.value();
|
||||
|
||||
for (int i = 0; i < currentPolygon.count(); ++i)
|
||||
{
|
||||
const QPointF p1 = currentPolygon.at(i);
|
||||
const QPointF p2 = currentPolygon.at((i + 1) % currentPolygon.count());
|
||||
const QLineF currentEdge(p1, p2);
|
||||
|
||||
QPointF intersectionPoint;
|
||||
|
||||
if (ray.intersects(currentEdge, &intersectionPoint) != QLineF::BoundedIntersection)
|
||||
continue;
|
||||
|
||||
QLineF distanceLine(ray.p1(), intersectionPoint);
|
||||
|
||||
distances.insert(distanceLine.length(), it.key());
|
||||
}
|
||||
}
|
||||
|
||||
if (distances.isEmpty())
|
||||
return -1;
|
||||
|
||||
return distances.first();
|
||||
}
|
||||
|
||||
} // namespace Calculations
|
||||
} // namespace CollateralDamageSimulation
|
||||
Reference in new issue
Block a user