/** * @RDE_HEADER@ * Building VWM info * $Id$ * $HeadURL$ * $Author$ * Created by Gerlinde Rudolphus, SLSA, 2017-01-15 */ #include "vwm_building.h" #include "vwm_math.h" #include #include #include #include #include namespace BuildingControlUnit { namespace VWM { static NGL::INGLLogger &logger() { return BuildingControlUnit::getLogger("VWM.Building"); } static inline bool isEffectNegligible(double effect) { return (effect < Settings::minimumRemainEffectThreshold()); } static inline bool isEffectNegligible(double RW, double ZF, double TF) { return isEffectNegligible(ZF * RW * TF); } // effect on walls (inside the room hit when TF = 1.0, otherwise neighbour room), see chapter 3.4/3.5 static inline double calcProtectionOfWallAfterHit(double SG_alt, double RW, double ZF, double TF) { const double SG_diff = ZF * RW * TF; if (isEffectNegligible(SG_diff)) return SG_alt; return std::max(0.0, SG_alt - SG_diff); } // // WallExclusionZoneBase // void WallExclusionZoneBase::setInitalProtection(double initialProtection, bool bResetProtection) { m_initialProtection = initialProtection; if (bResetProtection) m_protection = initialProtection; } void WallExclusionZoneBase::updateProtection(const double RW, const double ZF, const double TF) { const double SG_alt = m_protection; const double SG_neu = calcProtectionOfWallAfterHit(SG_alt, RW, ZF, TF); m_protection = SG_neu; } // // Room // void Room::setFortifyFactor(double factorNew, bool bRecalcValues) { const double factorOld = m_fortifyFactor; if (qFuzzyIsNull(factorOld) || qFuzzyIsNull(factorNew)) return; m_fortifyFactor = factorNew; if (bRecalcValues) { for (OuterWall &wall : m_outerWalls) { wall.setProtection(wall.protection() / factorOld * factorNew); wall.setInitalProtection(wall.initialProtection() / factorOld * factorNew, false); for (ExclusionZone &exZone : wall.exclusionZones()) { exZone.setProtection(exZone.protection() / factorOld * factorNew); exZone.setInitalProtection(exZone.initialProtection() / factorOld * factorNew, false); } } } } double Room::damagePercent() const { if (m_outerWalls.isEmpty()) return 0; // should never happen - see Building::init() double currentProtection = 0.0; double initialProtection = 0.0; for (const OuterWall &wall : qAsConst(m_outerWalls)) { currentProtection += wall.protection(); initialProtection += wall.initialProtection(); for (const ExclusionZone &exZone : qAsConst(wall.exclusionZones())) { currentProtection += exZone.protection(); initialProtection += exZone.initialProtection(); } } if (qFuzzyIsNull(initialProtection)) return 100; return (1.0 - currentProtection / initialProtection) * 100.0; } double Room::calcRemainingEffect(const HitPosition &hitPos, const Charge &ammu) const { const double SG_alt = protectionForHitPosition(hitPos); const double ZF = ammu.destructiveFactor; const double WF = ammu.penetrationEffect; const double RW = calcRemainingEffect(SG_alt, ZF, WF); return RW; } void Room::updateProtectionDirectHit(const HitPosition &hitPos, const Charge &ammu) { Q_ASSERT(hitPos.state != HitPosition::State::NoHit); const double SG_alt = protectionForHitPosition(hitPos); const double ZF = ammu.destructiveFactor; // da RW und TF = 1.0 ist es die gleiche Formel für direkten treffer und sekundärwirkung auf andere elemente const double SG_neu = calcProtectionOfWallAfterHit(SG_alt, 1.0, ZF, 1.0); setProtectionForHitPosition(hitPos, SG_neu); } static bool isDirectHitOuterWall(const HitPosition &hitPos, int roomId, int idxOw) { if (hitPos.state != HitPosition::State::OuterWall) return false; return hitPos.roomId == roomId && hitPos.outerWallIdx == idxOw; } static bool isDirectHitExlusionZone(const HitPosition &hitPos, int roomId, int idxOw, int idxEz) { if (hitPos.state != HitPosition::State::ExclusionZone) return false; return hitPos.roomId == roomId && hitPos.outerWallIdx == idxOw && hitPos.exclusionZoneIdx == idxEz; } void Room::updateProtectionIndirectHit(const HitPosition &hitPos, const Charge &ammu, double remainingEffect) { // same calculation as for neighbours when transitionFactor is set to 1.0 updateProtectionNeighbourHit(hitPos, ammu, remainingEffect, 1.0); } bool Room::updateProtectionNeighbourHit(const HitPosition &hitPos, const Charge &ammu, double remainingEffect, double transitionFactor) { const double ZF = ammu.destructiveFactor; const double RW = remainingEffect; const double TF = transitionFactor; DEBUG_LOG(logger()) << "Effect for room " << m_id << " (RW="<< RW << ", ZF=" << ZF << " , TF=" << TF << ") ; effect=" << RW*ZF*TF << " -> damagePercent VOR= " << this->damagePercent(); if (isEffectNegligible(RW, ZF, TF)) { DEBUG_LOG(logger()) << "Effect is Negligible for room " << m_id; for (int idxOw = 0; idxOw < m_outerWalls.size(); ++idxOw) { OuterWall &ow = m_outerWalls[idxOw]; DEBUG_LOG(logger()) << "room " << m_id << "[ow=" << idxOw << "] new protection = " << ow.protection() << " (" << ow.protectionPercent() << "%)"; } return false; } const char *hitReason = (hitPos.roomId == m_id ? "indirect hit" : "neighbour hit"); for (int idxOw = 0; idxOw < m_outerWalls.size(); ++idxOw) { OuterWall &ow = m_outerWalls[idxOw]; if (!isDirectHitOuterWall(hitPos, m_id, idxOw)) { ow.updateProtection(RW, ZF, TF); DEBUG_LOG(logger()) << "room " << m_id << "[ow=" << idxOw << "] new protection = " << ow.protection() << " (" << ow.protectionPercent() << "%) - " << hitReason; } for (int idxEz = 0; idxEz < ow.exclusionZones().size(); ++idxEz) { if (!isDirectHitExlusionZone(hitPos, m_id, idxOw, idxEz)) { ExclusionZone &ez = ow.exclusionZones()[idxEz]; ez.updateProtection(RW, ZF, TF); DEBUG_LOG(logger()) << "room " << m_id << "[ow=" << idxOw << "][ez=" << idxEz << "] new protection = " << ez.protection() << " (" << ez.protectionPercent() << "%) - " << hitReason; } } } DEBUG_LOG(logger()) << "Effect for room " << m_id << " -> damagePercent NACH = " << this->damagePercent(); return true; } double Room::protectionForHitPosition(const HitPosition &hitPos) const { const OuterWall &ow = m_outerWalls.at(hitPos.outerWallIdx); if (hitPos.state == HitPosition::State::OuterWall) return ow.protection(); return ow.exclusionZones().at(hitPos.exclusionZoneIdx).protection(); } void Room::setProtectionForHitPosition(const HitPosition &hitPos, double SG_neu) { Q_ASSERT(hitPos.state != HitPosition::State::NoHit); OuterWall &ow = m_outerWalls[hitPos.outerWallIdx]; if (hitPos.state == HitPosition::State::OuterWall) { ow.setProtection(SG_neu); // // m_directHit = true; // 'moved' to Building::hit() DEBUG_LOG(logger()) << "room " << m_id << "[ow=" << hitPos.outerWallIdx << "] new protection = " << ow.protection() << " (" << ow.protectionPercent() << "%)" << " - direct hit"; } else { ExclusionZone &ez = ow.exclusionZones()[hitPos.exclusionZoneIdx]; ez.setProtection(SG_neu); DEBUG_LOG(logger()) << "room " << m_id << "[ow=" << hitPos.outerWallIdx << "][ez=" << hitPos.exclusionZoneIdx << "] new protection = " << ez.protection() << " (" << ez.protectionPercent() << "%)" << " - direct hit"; } } // effect on the wall/exclusion zone which was hit, see chapter 3.2 vulnerability model document double Room::calcRemainingEffect(double SG_alt, double ZF, double WF) { // keine Schutzwirkung -> remainingEffect = 100% if (SG_alt < 0.0001) return 1.0; if (ZF < 0.0001) return 0; double D_Wand = 0.0; if (SG_alt < ZF * 0.95) D_Wand = ZF * 0.95 - SG_alt; const double D_Zufall = ZF * WF * 0.05 / SG_alt; double RW = 100.0 * (D_Wand + D_Zufall) / ZF; if (RW > 1.0) RW = 1.0; return RW; } // // Building // Building::Building() : m_lastHitPosition(HitPosition()) , m_logger(BuildingControlUnit::getLogger("VWM.Building")) {} int Building::checkPolygons() { int numberOfDuplicates = 0; for (const Room &room : qAsConst(m_rooms)) { for (int owIdx = 0; owIdx < room.outerWalls().size(); ++owIdx) { const OuterWall &ow = room.outerWalls().at(owIdx); for (int elemIdx = 0; elemIdx < ow.elements().size(); ++elemIdx) { const WallElement &wallElement = ow.elements().at(elemIdx); numberOfDuplicates += checkDublicatePointsInPolygon(room.id(), owIdx, elemIdx, "QuterWall", wallElement); /* const auto &points = wallElement; for (int i = 0; i < points.size() - 1; ++i) { const QLineF l1 = QLineF(points.at(i), points.at(i+1)); for (int j = i + 1; j < points.size() - 1; ++j) { const QLineF l2 = QLineF(points.at(j), points.at(j+1)); if (l1.intersect(l2, nullptr)) { WARNING_LOG(m_logger) << "Polygon lines " << i << "," << i+1 << " (" << l1 << ") and " << j << "," << j+1 << " (" << l2 << ") in room " << roomId << ", " << type << "[" << idx << "], element[" << subIdx << "] are interesectiong each other!"; } } }*/ } /* for (int exIdx = 0; exIdx < ow.exclusionZones().size(); ++exIdx) { const ExclusionZone &ez = ow.exclusionZones().at(exIdx); checkDublicatePointsInPolygon(room.id(), owIdx, exIdx, "exclusion zone", ez); }*/ } } return numberOfDuplicates; } int Building::cleanupPolygons() { int numberOfRemoved = 0; for (Room &room : m_rooms) { for (int owIdx = 0; owIdx < room.outerWalls().size(); ++owIdx) { OuterWall &ow = room.outerWalls()[owIdx]; for (int elemIdx = 0; elemIdx < ow.elements().size(); ++elemIdx) { WallElement &wallElement = ow.elements()[elemIdx]; numberOfRemoved += cleanupDublicatePointsInPolygon(room.id(), owIdx, elemIdx, "room", wallElement); bool bMakePlanarDone = false; // make planes planar MakePlanar makePlanar(wallElement); // just a reference //try (keep bldg_3280 in mind -> final poly was reduced to 2 coordinate (cleanupDublicatePointsInPolygon , make planar, getCoordsForPlane/removeLinearDependency) QVector candidate = makePlanar.calculate(&bMakePlanarDone); cleanupDublicatePointsInPolygon(room.id(), owIdx, elemIdx, "room", candidate); if (candidate.size() < 4) { NOTICE_LOG(m_logger) << "wall was made planar: Building '" << m_name << "' -> " << " in room " << room.id() << " OuterWall[" << owIdx << "], element[" << elemIdx << "] " << " makePlanar and cleanupDublicatePointsInPolygon would generate short polygon, size=" << candidate.size(); for (auto point : qAsConst(candidate)) NOTICE_LOG(m_logger) << " point= " << point; bMakePlanarDone = false; } else { wallElement = candidate; } if (bMakePlanarDone) NOTICE_LOG(m_logger) << "wall was made planar: Building '" << m_name << "' -> " << " in room " << room.id() << " OuterWall[" << owIdx << "], element[" << elemIdx << "] " << " smallestDim=" << (int)makePlanar.getSmallestDim() << " smallestRange=" << makePlanar.getSmallestRange(); // // Bei Bldg 1193 (rev. 5032.55466) // ((( if (ratioOfSmallestAndBiggestDimension() >= 10.0 // && getRange(m_smallestDim).range() < 0.10 )) ) // OHNE makePlanar -> 7 HitPosition::State::OuterWall (vwm_math.cpp:539) // MIT makePlanar -> 8 HitPosition::State::OuterWall (vwm_math.cpp:539) // Neu: HitPosition::State::OuterWall, roomId = 2103, outerWallIndex = 1 } } } return numberOfRemoved; } int Building::cleanupDublicatePointsInPolygon(int roomId, int idx, int subIdx, const char *type, QVector &points) const { QVector idxToRemove; idxToRemove.reserve(points.size()); for (int i = 0; i < points.size(); ++i) { const auto &p1 = points.at(i); for (int j = i + 1; j < points.size(); ++j) { auto p2 = points.at(j); if (qFuzzyCompare(p1, p2)) { DEBUG_LOG(m_logger) << "Building '" << m_name << "' -> " << "Polygon point " << i << " (" << p1 << ") and " << j << " (" << p2 << ") in room " << roomId << ", " << type << "[" << idx << "], element[" << subIdx << "] are the same! -> REMOVE"; if (!idxToRemove.contains(j)) idxToRemove.push_back(j); } } } // std::sort works with QVector :-) std::sort(idxToRemove.begin(), idxToRemove.end(), std::greater()); int numberOfRemoved = idxToRemove.size(); // remove from end/high to beginning/low :-) for (auto idxRemove : idxToRemove) points.removeAt(idxRemove); return numberOfRemoved; } Room Building::room(int roomId) { auto it = m_rooms.find(roomId); if (it == m_rooms.end()) return {}; return *it; } // calculated values int Building::damagePercent() const { double roomDamagePercent = 0.0; for (const Room &room : m_rooms) roomDamagePercent += room.damagePercent(); return qRound(roomDamagePercent / m_rooms.size()); } QVector Building::roomStates() const { QVector ret; ret.reserve(m_rooms.size() / 2); for (const Room &room : m_rooms) { const int dmgPercent = qRound(room.damagePercent()); // if ((dmgPercent > 0 && damageState() != DamageState::Destroyed) || !qFuzzyCompare(room.fortifyFactor(), 1.0)) // { HitRoom hr; hr.roomId = room.id(); hr.damagePercent = dmgPercent; hr.remainingEffectPercent = qRound(room.remainingEffect() * 100); hr.fortifyFactor = room.fortifyFactor(); hr.directHit = room.isDirectHit(); ret += hr; DEBUG_LOG(m_logger) << "HitRoom " << hr.roomId << " damage: " << hr.damagePercent << "%, remainEffect: " << hr.remainingEffectPercent << "%" << ", remainingEffectChargeTypeId: " << hr.remainingEffectChargeTypeId << "%" << ", fortifyFactor: " << hr.fortifyFactor << ", isDirectHit:" << room.isDirectHit(); // } } return ret; } DamageState Building::damageState() const { if (Building::damagePercent() >= Settings::killThreshold()) return DamageState::Destroyed; return DamageState::NoDamage; } // effects on building void Building::init() { for (Room &room : m_rooms) initRoom(room); } void Building::destroyAllRooms() { for (Room &room : m_rooms) destroyRoom(room); } void Building::setRoomState(DamageState damageState, const QVector &afffectedRooms) { for (const int roomId : afffectedRooms) { auto it = m_rooms.find(roomId); if (it == m_rooms.end()) { WARNING_LOG(m_logger) << "Could not find room with id " << roomId; continue; } if (damageState == DamageState::Destroyed) destroyRoom(it.value()); else initRoom(it.value()); } } void Building::setFortifyInfo(const QVector &foritfyInfos) { for (const FortifyInfo &info : foritfyInfos) { auto it = m_rooms.find(info.roomId); if (it == m_rooms.end()) { WARNING_LOG(m_logger) << "Could not find room with id " << info.roomId; continue; } Room &room = it.value(); room.setFortifyFactor(info.fortifyFactor); } } void Building::initRoom(Room &room) const { const double BGPL = m_buildingProtectionLevel; const double OWPL = m_outerWallProtectionLevel; const double EZPL = m_exclusionZoneProtectionLevel; room.setRemainingEffect(0); room.setDirectHit(false); const double fortifyFactor = room.fortifyFactor(); if (room.outerWalls().isEmpty()) { // create a dummy wall for internal rooms room.outerWalls() += OuterWall(); } for (OuterWall &ow : room.outerWalls()) { if (ow.isDummyWall()) { // see chapter 3.5 ow.setInitalProtection(250 * fortifyFactor, true); } else { const double OWPF = ow.protectiveFactor(); const double SG_0_Wall = BGPL * OWPL * OWPF * fortifyFactor; ow.setInitalProtection(SG_0_Wall, true); for (ExclusionZone &ez : ow.exclusionZones()) { const double EZPF = ez.protectiveFactor(); const double SG_0_EZ = BGPL * EZPL * EZPF * fortifyFactor; ez.setInitalProtection(SG_0_EZ, true); } } } } void Building::destroyRoom(Room &room) { room.setRemainingEffect(1); // 0..1 for (OuterWall &ow : room.outerWalls()) { ow.setProtection(0); for (ExclusionZone &ez : ow.exclusionZones()) ez.setProtection(0); } } bool Building::hit(const LtsUtils::QPoint3D &pos, const LtsUtils::QPoint3D &dir, Charge charge, HitPosition *preCalculatedHitPosition) { for (Room &room : m_rooms) room.setDirectHit(false); if (preCalculatedHitPosition) { m_lastHitPosition = *preCalculatedHitPosition; // 3DWirksim } else { // hit position berechnen m_lastHitPosition = Math::getHitPosition(pos, dir, m_rooms); } // Hack Unterirdisch: const bool bBelowGround = isLastHitPositionBelowGroundHack(); if (bBelowGround) { INFO_LOG(m_logger) << " XXXX CES belowGround, reduce charge: " << charge; charge.destructiveFactor *= 0.25; charge.penetrationEffect *= 0.25; INFO_LOG(m_logger) << " XXXX CES belowGround, reduce charge: " << charge; } INFO_LOG(m_logger) << "XXXXX HitPosition: state=" << m_lastHitPosition.state << ", m_name=" << m_name << ", m_modelId=" << m_modelId << ", roomId=" << m_lastHitPosition.roomId << ", outerWallIndex=" << m_lastHitPosition.outerWallIdx << ", exclusionZoneIndex=" << m_lastHitPosition.exclusionZoneIdx << ", bBelowGround=" << bBelowGround; // nicht getroffen -> return if (m_lastHitPosition.state == HitPosition::State::NoHit) return false; // zugehörigen Raum suchen Room &hitRoom = m_rooms[m_lastHitPosition.roomId]; if (!hitRoom.isValid()) { ERROR_LOG(m_logger) << "Internal error in hit calculation - could not find room with id " << m_lastHitPosition.roomId; return false; } // GUEZE-1013 Kundenrückläufer Bldg 1158 // 3 REF_MODs für Raum 101 @ Bldg 1158: 11001, 11022, 11029 // Bei allen 3 wird die Trefferlinie in der 2D-Lage dargestellt. // Bei 11022, 11029 wird die BuildingHitLineInfo im Bldg-Viewer angezeigt. // Die BuildingHitLineInfo für 11001 fehlt allerdings!! // Grund: Bei 11022, 29 wird eine OuterWall vom VWM detektiert. // Bei 11001 hingegen ist es eine ExclusionZone. // Bei der Weiterleitung vom VWM gibt es in der Raumliste ein Flag 'm_directHit', // welches eingeführt wurde mit r43258 // Wir setzen dieses Flag nun unabhängig davon, ob der Raum via // Outerwall oder ExclusionZone getroffen wurde: hitRoom.setDirectHit(true); // Implementation laut Dokument: // wir zerstören hier die Nachbarräume mit dem remainingEffect. Es werden alle // Nachbarschaftsbeziehungen berückichtigt, Abbruch ist wenn isEffectNegligible() true zurück liefert // vorher werden alle Räume dem 'Verwandschaftsgrad' nach durchlaufen. D.h. erst alle direkt angrenzenden // Räume, dann alle, die an diesen angegrenzt sind usw. Der Effekt geht immer nur von Vater zu Kind, nie // von Kind zu Vater oder Kind zu Kind. // alten Remaining Effekt entfernen for (Room &room : m_rooms) room.setRemainingEffect(0); // Remaining Effect auf andere Gebäudeteile berechnen (dafür ist die alte protection (SG_alt) nötig) const double remainingEffect = hitRoom.calcRemainingEffect(m_lastHitPosition, charge); hitRoom.setRemainingEffect(remainingEffect); // neue protection für getroffenen Raum berechnen und setzen hitRoom.updateProtectionDirectHit(m_lastHitPosition, charge); // neue protection für die anderen Elemente des getroffenen Raumes berechnen hitRoom.updateProtectionIndirectHit(m_lastHitPosition, charge, remainingEffect); // neue protection für die Nachbarräume berechnen const QMap &neighbours = hitRoom.neighbours(); QMap neighboursToCheck; QSet currentGradeNeighbours; for (const NeighbourRoom &neighbour : neighbours) { neighboursToCheck.insert(neighbour.id(), NeighboursToCheck(neighbour, hitRoom.remainingEffect())); currentGradeNeighbours.insert(neighbour.id()); } QSet lowerGradeNeighbours; lowerGradeNeighbours.insert(hitRoom.id()); // DEBUG_LOG(m_logger) << " XXXXX start recursive ; neighboursToCheck= " << toString(neighboursToCheck); auto ret = checkHitNeighbours(neighboursToCheck, charge, currentGradeNeighbours, lowerGradeNeighbours); while (!ret.isEmpty()) { // DEBUG_LOG(m_logger) << " XXXXX recursive ; ret= " << toString(ret); ret = checkHitNeighbours(ret, charge, currentGradeNeighbours, lowerGradeNeighbours); // currentGradeNeighbours will contain the new neighbours for the next grade // lowerGradeNeighbours will contain all currently lower grade neighbours now } return true; } bool Building::isLastHitPositionBelowGroundHack() const { // Hack: return (m_name.contains(QStringLiteral("BN_Kanaele"))) || (m_name.contains(QStringLiteral("U_Bahn")) && m_lastHitPosition.roomId < 2300) ; } QMap Building::checkHitNeighbours(const QMap &neighbours, const Charge &charge, QSet ¤tGradeNeighbours, QSet &lowerGradeNeighbours) { // DEBUG_LOG(m_logger) << " neighbours " << toString(neighbours); // DEBUG_LOG(m_logger) << " currentGradeNeighbours " << toString(currentGradeNeighbours); // DEBUG_LOG(m_logger) << " lowerGradeNeighbours " << toString(currentGradeNeighbours); QMap neighboursToCheck; QSet newCurrentGradeNeighbours; for (const NeighboursToCheck &neighbourRoom : neighbours) { const int neighbourRoomId = neighbourRoom.room.id(); if (lowerGradeNeighbours.contains(neighbourRoomId)) // this should not happen continue; auto itRoom = m_rooms.find(neighbourRoomId); if (itRoom == m_rooms.end()) { WARNING_LOG(m_logger) << "Could not find room with id " << neighbourRoomId; continue; } Room &neighbour = itRoom.value(); // REGG-5736: "Die Wirkung von der KPZ-Kanone muss ein Gebäude nahezu komplett zerstören. Diese Wirkung wird zur Zeit nicht erreicht." double TF = Settings::transitionMultiplier() * neighbourRoom.room.transitionFactor(); // GUEZE-605 / GUEZE-707: ..aber begrenzen auf 1. Ein WEITER-leitungswert größer ist unlogisch if (TF > 1.0) TF = 1.0; // hitPos is not needed here but internally we use the same calculation for updateProtectionIndirectHit() ... if (!neighbour.updateProtectionNeighbourHit(m_lastHitPosition, charge, neighbourRoom.remainEffect, TF)) continue; // there was an effect on this room -> set remainEffect and add all next grade neighbours to the list for the next check const double oldRe = neighbour.remainingEffect(); neighbour.setRemainingEffect(neighbourRoom.remainEffect * TF); DEBUG_LOG(m_logger) << "room " << neighbourRoomId << " remaining effect old: " << oldRe << " -> new: " << neighbour.remainingEffect(); // check grade2 neighbours, remove all lower grade neighbours for (const NeighbourRoom &grade2Neighbour : qAsConst(neighbour.neighbours())) { if (lowerGradeNeighbours.contains(grade2Neighbour.id()) || currentGradeNeighbours.contains(grade2Neighbour.id())) continue; // [GUEZE-605] / [GUEZE-707] // Problem: 701 hat in Etage 6 diverse Nachbarräume: 601, 602,... 610. // 603 hat aber auch 602 und 604 als Nachbarn. // Bei solch einer Konstellation wurden Räume mehrfach (bis zu 12 mal) eingefügt. // Und dementsprechend die Zerstörung mehrfach berechnet und akkumuliert. // Bei Bldg 3101 wurde 601 getroffen (50%) und 106 wurde (durch 12-fache Berechnung) zu 100% zerstört (!!!) auto it = neighboursToCheck.find(grade2Neighbour.id()); if (it == neighboursToCheck.end()) { neighboursToCheck.insert(grade2Neighbour.id(), NeighboursToCheck(grade2Neighbour, neighbour.remainingEffect())); } else { // check remainingEffect and use maximum const NeighboursToCheck &curEntry = it.value(); if (neighbour.remainingEffect() > curEntry.remainEffect) neighboursToCheck.insert(grade2Neighbour.id(), NeighboursToCheck(grade2Neighbour, neighbour.remainingEffect())); } newCurrentGradeNeighbours.insert(grade2Neighbour.id()); } } lowerGradeNeighbours += currentGradeNeighbours; currentGradeNeighbours = newCurrentGradeNeighbours; return neighboursToCheck; } QString Building::buildingProperties() const { return m_buildingProperties; } void Building::setBuildingProperties(const QString &buildingProperties) { m_buildingProperties = buildingProperties; } QString Building::segmentProperties(const QString &hostname) const { return m_segmentProperties.value(hostname); } void Building::setSegmentProperties(const QString &hostname, const QString &properties) { m_segmentProperties.insert(hostname, properties); } int Building::checkDublicatePointsInPolygon(int roomId, int idx, int subIdx, const char *type, const QVector &points) const { int numberOfDuplicates = 0; for (int i = 0; i < points.size(); ++i) { const auto &p1 = points.at(i); for (int j = i + 1; j < points.size(); ++j) { const auto &p2 = points.at(j); if (qFuzzyCompare(p1, p2)) { ++numberOfDuplicates; WARNING_LOG(m_logger) << "Building '" << m_name << "' -> " << "Polygon point " << i << " (" << p1 << ") and " << j << " (" << p2 << ") in room " << roomId << ", " << type << "[" << idx << "], element[" << subIdx << "] are the same! (size=" << points.size() << ")"; } } } if (numberOfDuplicates > 0) for (auto point : points) WARNING_LOG(m_logger) << "Building '" << m_name << "' -> " << "point =" << point << " (same points)"; return numberOfDuplicates; } QString Building::toString(const QVector &cont) { QString str; for (auto item : cont) str += QString(QStringLiteral("%1;")).arg(item.room.id()); return str; } QString Building::toString(const QMap &cont) { QString str; for (auto item : cont) str += QString(QStringLiteral("%1;")).arg(item.room.id()); return str; } QString Building::toString(const QSet &cont) { QString str; for (auto item : cont) str += QString(QStringLiteral("%1;")).arg(item); return str; } // // class MakePlanar // MakePlanar::MakePlanar(const QVector &poly) : m_poly(poly) { ranges.resize(3); } QVector MakePlanar::calculate(bool *bMakePlanarDone) { // Folgende Polygon hat eine Abweichung bei 17cm: // 2.859 - 2.689 = 0.17 // static const QVector polyBldg1131_201_3({ // sollte eigentlich waagerechte Wand darstellen // {0.000, 5.987, 2.689}, // idx=0 // {0.000, 5.791, 2.695}, // idx=1 // idx=1 "stört" // {0.000, 0.170, 2.859}, // idx=2 // {3.098, 0.170, 2.859}, // idx=3 // {3.098, 5.791, 2.695}, // idx=4 // {2.928, 5.987, 2.689} // idx=5 // }); if (ratioOfSmallestAndBiggestDimension() >= 10.0 && getRange(m_smallestDim).range() < 0.20 ) { // const int numCoords = m_poly.size(); makePlanar(); if (bMakePlanarDone) { *bMakePlanarDone = true; // std::cout << " MakePlanar smallestRange=_" << getRange(m_smallestDim).range() << "_ ratio=" << m_ratio << " org-numCoords=" << numCoords << std::endl; } } return m_poly; } double MakePlanar::ratioOfSmallestAndBiggestDimension() { // m_poly describes an area. Maybe the area is not planar. // So for this use case, 4 items are required if (m_poly.size() < 4) return -1; MyRange &rangeX = ranges[static_cast(DIMENSION::X)]; MyRange &rangeY = ranges[static_cast(DIMENSION::Y)]; MyRange &rangeZ = ranges[static_cast(DIMENSION::Z)]; // Note: first() required at least an item: rangeX.min = m_poly.first().x(); rangeX.max = m_poly.first().x(); rangeY.min = m_poly.first().y(); rangeY.max = m_poly.first().y(); rangeZ.min = m_poly.first().z(); rangeZ.max = m_poly.first().z(); for (const LtsUtils::QPoint3D &pnt : qAsConst(m_poly)) { rangeX.min = std::min(rangeX.min, pnt.x()); rangeX.max = std::max(rangeX.max, pnt.x()); rangeY.min = std::min(rangeY.min, pnt.y()); rangeY.max = std::max(rangeY.max, pnt.y()); rangeZ.min = std::min(rangeZ.min, pnt.z()); rangeZ.max = std::max(rangeZ.max, pnt.z()); } if (rangeY.range() < rangeX.range()) m_smallestDim = DIMENSION::Y; if (rangeZ.range() < getRange(m_smallestDim).range()) m_smallestDim = DIMENSION::Z; if (rangeY.range() > rangeX.range()) m_biggistDim = DIMENSION::Y; if (rangeZ.range() > getRange(m_biggistDim).range()) m_biggistDim = DIMENSION::Z; // avoid division by zero if (!qFuzzyIsNull(getRange(m_smallestDim).range())) m_ratio = getRange(m_biggistDim).range() / getRange(m_smallestDim).range(); return m_ratio; // could be inf / inifite } void MakePlanar::makePlanar() { // choose new value: const double newValue = (getRange(m_smallestDim).min + getRange(m_smallestDim).max) / 2.0; switch (m_smallestDim) { case DIMENSION::X: for (LtsUtils::QPoint3D &pnt_ref : m_poly) pnt_ref.rx() = newValue; break; case DIMENSION::Y: for (LtsUtils::QPoint3D &pnt_ref : m_poly) pnt_ref.ry() = newValue; break; case DIMENSION::Z: for (LtsUtils::QPoint3D &pnt_ref : m_poly) pnt_ref.rz() = newValue; break; case DIMENSION::Undefined: { // empty } } // switch } void wallElementToBottomLeftTopRight3D(const WallElement &wallElement, LtsUtils::QPoint3D &bottomLeftOut, LtsUtils::QPoint3D &topRightOut) { const LtsUtils::QPoint3D &frontItem = wallElement.front(); bottomLeftOut = frontItem; topRightOut = frontItem; for (auto coord : qAsConst(wallElement)) { if (coord.x() < bottomLeftOut.x()) bottomLeftOut.setX(coord.x()); if (coord.y() < bottomLeftOut.y()) bottomLeftOut.setY(coord.y()); if (coord.z() < bottomLeftOut.z()) bottomLeftOut.setZ(coord.z()); if (coord.x() > topRightOut.x()) topRightOut.setX(coord.x()); if (coord.y() > topRightOut.y()) topRightOut.setY(coord.y()); if (coord.z() > topRightOut.z()) topRightOut.setZ(coord.z()); } } LtsUtils::QPoint3D centerOfWallElement3D(const WallElement &wallElement) { LtsUtils::QPoint3D bottomLeftOut, topRightOut; wallElementToBottomLeftTopRight3D(wallElement, bottomLeftOut, topRightOut); const LtsUtils::QPoint3D ¢er = (bottomLeftOut + topRightOut) / 2; return center; } } // namespace VWM } // namespace BuildingControlUnit