/** * @RDE_HEADER@ * Read VWM and building xml to check if BSM positions are correct * $Id$ * $HeadURL$ * $Author$ * Created by Gerlinde Rudolphus, SLSE, 2016-01-25 */ #include "vwm_math.h" #include "vwm_xml_reader.h" #include "vwm_building_room_geometry.h" #include "vwm_api.h" #include #include #include #include #include #include #include #include #include static QDomElement getChildElement(const QDomElement &parent, const QString &childTag, bool bThrow = true) { const QDomElement ret = parent.firstChildElement(childTag); if (ret.isNull() && bThrow) throw BuildingControlUnit::XmlReaderException(QStringLiteral("Invalid XML Structure - no '%1' tag").arg(childTag)); return ret; } static int getInteger(const QDomElement &element, const QString &attribute) { bool bOk = true; const int ret = element.attribute(attribute).toInt(&bOk); if (!bOk) throw BuildingControlUnit::XmlReaderException(QStringLiteral("Can not find attribute %1 in xml element %2 (line %3:%4)") .arg(attribute, element.tagName()).arg(element.lineNumber()).arg(element.columnNumber())); return ret; } static inline bool checkRange(double d) { return (fabs(d) < 10000000); } static inline LtsUtils::QPoint3D positionFromElement(const QDomElement &elem) { bool bOkX, bOkY, bOkZ; auto ret (LtsUtils::QPoint3D(elem.attribute(QStringLiteral("X")).toDouble(&bOkX), elem.attribute(QStringLiteral("Y")).toDouble(&bOkY), elem.attribute(QStringLiteral("Z")).toDouble(&bOkZ))); if (!bOkX || !checkRange(ret.x())) throw BuildingControlUnit::XmlReaderException(QStringLiteral("invalid value for X: %1 (line %2:%3)") .arg(elem.attribute(QStringLiteral("X"))).arg(elem.lineNumber()).arg(elem.columnNumber())); if (!bOkY || !checkRange(ret.y())) throw BuildingControlUnit::XmlReaderException(QStringLiteral("invalid value for Y: %1 (line %2:%3)") .arg(elem.attribute(QStringLiteral("Y"))).arg(elem.lineNumber()).arg(elem.columnNumber())); if (!bOkZ || !checkRange(ret.z())) throw BuildingControlUnit::XmlReaderException(QStringLiteral("invalid value for Z: %1 (line %2:%3)") .arg(elem.attribute(QStringLiteral("Z"))).arg(elem.lineNumber()).arg(elem.columnNumber())); return ret; } struct MyRoom { LtsUtils::QPoint3D bBoxMin; LtsUtils::QPoint3D bBoxMax; int id = 0; LtsUtils::QPoint3D center() const { return (bBoxMin + bBoxMax) / 2; } double maxZ() const { return std::max(bBoxMax.z(), bBoxMin.z()); } double minZ() const { return std::min(bBoxMax.z(), bBoxMin.z()); } }; static BuildingControlUnit::Bsm getRefMod(const QDomElement &extRef) { BuildingControlUnit::Bsm bsm; const QDomElement nameElement = getChildElement(extRef, QStringLiteral("Name"), false); if (!nameElement.text().startsWith(QLatin1String("REF_MOD_AP_"), Qt::CaseInsensitive)) return bsm; bsm.id = getInteger(getChildElement(extRef, QStringLiteral("SubId")), QStringLiteral("ID")); bsm.position = positionFromElement(getChildElement(extRef, QStringLiteral("Coord"))); bsm.direction = positionFromElement(getChildElement(extRef, QStringLiteral("Dir"))); // xyz=hpr? return bsm; } template static inline T positionFromElement(const QDomElement &elem) { return T(elem.attribute(QStringLiteral("X")).toDouble(), elem.attribute(QStringLiteral("Y")).toDouble(), elem.attribute(QStringLiteral("Z")).toDouble()); } static void parseRooms(const QDomElement &domGroup, BuildingControlUnit::BuildingBsmConfig &bsmConfig, QMap &rooms) { MyRoom room; QDomElement domGroup1 = getChildElement(domGroup, QStringLiteral("Group")); for ( ; !domGroup1.isNull(); domGroup1 = domGroup1.nextSiblingElement(domGroup1.tagName())) { const QDomElement nameElement = getChildElement(domGroup1, QStringLiteral("Name"), false); if (!nameElement.text().startsWith(QLatin1String("floor_"), Qt::CaseInsensitive)) continue; QDomElement domSwitch = getChildElement(domGroup1, QStringLiteral("Switch"), false); for ( ; !domSwitch.isNull(); domSwitch = domSwitch.nextSiblingElement(domSwitch.tagName())) { const QDomElement roomNameElement = getChildElement(domSwitch, QStringLiteral("Name"), false); if (!roomNameElement.text().startsWith(QLatin1String("SW_room_"), Qt::CaseInsensitive)) continue; room.id = getInteger(getChildElement(domSwitch, QStringLiteral("SubId")), QStringLiteral("ID")); QDomElement domGroup2 = getChildElement(domSwitch, QStringLiteral("Group"), false); for ( ; !domGroup2.isNull(); domGroup2 = domGroup2.nextSiblingElement(domGroup2.tagName())) { const QDomElement domBBMin = getChildElement(domGroup2, QStringLiteral("BBox_Min")); const QDomElement domBBMax = getChildElement(domGroup2, QStringLiteral("BBox_Max")); room.bBoxMin = positionFromElement(domBBMin); room.bBoxMax = positionFromElement(domBBMax); rooms[room.id] = room; QDomElement extRef = getChildElement(domGroup2, QStringLiteral("External_Reference"), false); for ( ; !extRef.isNull(); extRef = extRef.nextSiblingElement(extRef.tagName())) { const BuildingControlUnit::Bsm bsm = getRefMod(extRef); if (bsm.id) bsmConfig.bsmMap.insert(bsm.id, bsm); } } } } } static bool parseFlt(NGL::INGLLogger &logger, const QString &fn, BuildingControlUnit::BuildingBsmConfig &bsmConfig, BuildingControlUnit::BuildingBsmConfig &bsmConfigOhneRaumbezug, QMap &rooms, std::vector &footprint_ref) { const QDomElement domRoot = BuildingControlUnit::readFile(fn, QStringLiteral("STREIF")); if (domRoot.isNull()) { WARNING_LOG(logger) << "domRoot isNull"; return false; } try { // main part const QDomElement domHdr = domRoot.firstChildElement(QStringLiteral("Header")); const QDomElement domGroup = domHdr.firstChildElement(QStringLiteral("Group")); QDomElement extRef = getChildElement(domGroup, QStringLiteral("External_Reference")); for ( ; !extRef.isNull(); extRef = extRef.nextSiblingElement(extRef.tagName())) { const BuildingControlUnit::Bsm bsm = getRefMod(extRef); if (bsm.id) bsmConfig.bsmMap.insert(bsm.id, bsm); } bsmConfigOhneRaumbezug = bsmConfig; parseRooms(domGroup, bsmConfig, rooms); // bldg-footprint { const QDomElement domHdrFootprint = domRoot.firstChildElement(QStringLiteral("Header")); const QDomElement domGroupFootprint = domHdrFootprint.firstChildElement(QStringLiteral("Group")); QDomElement groupElem = domGroupFootprint.firstChildElement(QStringLiteral("Group")); footprint_ref.clear(); for (; !groupElem.isNull(); groupElem = groupElem.nextSiblingElement(groupElem.tagName())) { const QDomElement nameElement = getChildElement(groupElem, QStringLiteral("Name"), false); if (!nameElement.text().startsWith(QLatin1String("FOOTPRINT"), Qt::CaseInsensitive)) continue; const QDomElement polygonElem = getChildElement(groupElem, QStringLiteral("Polygon"), false); if (polygonElem.isNull()) continue; QDomElement vertexElem = polygonElem.firstChildElement(QStringLiteral("Vertex")); for (; !vertexElem.isNull(); vertexElem = vertexElem.nextSiblingElement(vertexElem.tagName())) { // TODO: xml hat keine Reihenfolge! Man kann es anhand der Namen sortieren const QDomElement coordElem = vertexElem.firstChildElement(QStringLiteral("Coord")); if (coordElem.isNull()) continue; footprint_ref.emplace_back(positionFromElement(coordElem)); } break; } // for } } catch (const BuildingControlUnit::XmlReaderException &ex) { WARNING_LOG(logger) << ex.what(); return false; } return !bsmConfig.bsmMap.isEmpty(); } /* * Folgende Probleme werder ermittlt: * VWM: doppelte Punkte in den Polygonen * * checkBsmPosition using rooms: * VWM: << "Hit on center point for room " << myRoom.id << " will not hit building - no outterWalls" * Beschuss von oben auf den CenterPoint eines jeden Raumes * Hier machen die Aussentreppen Probleme (s.a. GUEZE-704) * Diese Probleme werden durch Vergleich mit dem Footprint mittlerweile erkannt * und nicht mehr als Fehler eingestuft. * * << "Hit on center point for room results in hit pos behind a wall (" * // hitPos.distance negative * * FLT: * checkBsmPosition using BSMs: * << "Effect on bsm " << bsm.id << " will not hit building ! - position: " * * << "Effect on bsm " << bsm.id << " results in hit pos behind a wall (" * BSM is not modelled in the correct place */ int main(int argc, char *argv[]) { NGLCoreApplication app(argc, argv); app.createLogger("BCU", 1); // logcxx_bcu.props if (app.arguments().size() != 3) { std::cerr << "Usage: " << app.arguments().constFirst().toStdString() << " "; return -1; } const QString flt = app.arguments().at(1); const QString vwm = app.arguments().at(2); BuildingControlUnit::VWM::Building building; if (!BuildingControlUnit::VWM::XmlReader::readBuilding(vwm, building)) { WARNING_LOG(app.baseLogger()) << "parsing failed for '" << flt; return 1; } bool bAllOk = true; INFO_LOG(app.baseLogger()) << "******** Building '" << building.name() << "' -> checkPolygons for duplicate..."; int numberOfDuplicates = building.checkPolygons(); if (numberOfDuplicates == 0) INFO_LOG(app.baseLogger()) << "Building '" << building.name() << "' -> " << "fine, no duplicates founds"; else { WARNING_LOG(app.baseLogger()) << "Building '" << building.name() << "' -> first search: " << numberOfDuplicates << " duplicates found"; bAllOk = false; } // cleanup { int numberOfRemoved = building.cleanupPolygons(); WARNING_LOG(app.baseLogger()) << "Building '" << building.name() << "' -> cleanupPolygons: " << numberOfRemoved << " removed"; int numberOfDuplicates2 = building.checkPolygons(); WARNING_LOG(app.baseLogger()) << "Building '" << building.name() << "' -> second search: " << numberOfDuplicates2 << " duplicates found" << " bAllOk=" << bAllOk; bAllOk = true; } BuildingControlUnit::BuildingBsmConfig bsmConfig; BuildingControlUnit::BuildingBsmConfig bsmConfigOhneRaumbezug; QMap rooms; std::vector footprint; if (!parseFlt(app.baseLogger(), flt, bsmConfig, bsmConfigOhneRaumbezug, rooms, footprint) ) { // Exception: // bldg_BN_Kanaele.flt.xml does not have REF_MOD / BSM // so parseFlt return false; // Exception: // bldg_BN_Kanaele.flt.xml does not have REF_MOD / BSM if (flt.contains(QStringLiteral("bldg_BN_Kanaele")) || flt.contains(QStringLiteral("bldg_0910")) || flt.contains(QStringLiteral("bldg_2105")) || flt.contains(QStringLiteral("bldg_2106")) || flt.contains(QStringLiteral("bldg_2107")) || flt.contains(QStringLiteral("bldg_2108")) || flt.contains(QStringLiteral("bldg_2109")) || flt.contains(QStringLiteral("bldg_2110")) || flt.contains(QStringLiteral("bldg_2112")) || flt.contains(QStringLiteral("bldg_2116")) || flt.contains(QStringLiteral("bldg_2117")) || flt.contains(QStringLiteral("bldg_2118")) || flt.contains(QStringLiteral("bldg_2121")) || flt.contains(QStringLiteral("bldg_2122")) ) { // check rooms instead of bsmMap if (rooms.empty()) { WARNING_LOG(app.baseLogger()) << "parsing failed for '" << flt << " (" << rooms.size() << ")"; return 2; } } else { WARNING_LOG(app.baseLogger()) << "parsing failed for '" << flt << " (" << rooms.size() << ")"; return 2; } } // footprint des Bldgs: QPolygonF footprintPoly; footprintPoly.reserve(footprint.size()); for (auto coord : qAsConst(footprint)) footprintPoly << coord.toPointF(); double maxZ = 0; for(const MyRoom &room : qAsConst(rooms)) { TRACE_LOG(app.baseLogger()) << "Room: " << room.id << ", bboxMin: " << room.bBoxMin << ", bboxMax: " << room.bBoxMax << ", center: " << room.center() << ", zMax: " << room.maxZ(); maxZ = std::max(maxZ, room.maxZ()); } const QDomElement domRoot = BuildingControlUnit::readFile(flt, QStringLiteral("STREIF")); if (domRoot.isNull()) { WARNING_LOG(app.baseLogger()) << "domRoot isNull"; return 2; } //#define USE_BUILDINGROOMGEOMETRY #ifdef USE_BUILDINGROOMGEOMETRY const QDomElement domHdr = domRoot.firstChildElement(QStringLiteral("Header")); const QDomElement &domGroup = domHdr.firstChildElement(QStringLiteral("Group")); BuildingControlUnit::VWM::BuildingRoomGeometry buildingRoomGeometry; if (!buildingRoomGeometry.init(domGroup)) { WARNING_LOG(app.baseLogger()) << " BuildingRoomGeometry failed for '" << flt; return -2; } #endif // USE_BUILDINGROOMGEOMETRY // Guez-Building 1114: // 2021-07-02 12:21:41.600 INFO BCU::VWM::Math |#| Searching nearest room for bsm position (6.023,-4.634,8.556), bsmViewDirection (0,0,1), hit direction (-0,-0,-1), #rooms = 11 (ies/Buildings/BuildingControlUnit/VWM/vwm_math.cpp:452) // 2021-07-02 12:21:41.600 INFO BCU::VWM::Math |#| HitPosition: 1, roomId = 2301, outerWallIndex = 11, exclusionZoneIndex = -1 (ies/Buildings/BuildingControlUnit/VWM/vwm_math.cpp:539) // 2021-07-02 12:21:41.600 WARN BCU::main |#| Building 'bldg_1114' -> Hit on center point for room results in hit pos behind a wall (-7.41573e-16m) instead in front - looks like the house is more than 8.556m high!?!?, (hitPos=state = HitPosition::State::OuterWall, roomId = 2301, outerWallIdx = 11, exclusionZoneIdx = -1, distance = -7.41573e-16 (Buildings/BuildingControlUnit/VWM/test_vwm_xml.cpp:217) // 2021-12-17 13:50:42.658 WARN BCU::main |#| Building 'bldg_3127' -> Hit on center point for room results in hit pos behind a wall (-0.00194683m) instead in front - looks like the house is more than 8.681m high!?!?, (hitPos=state = HitPosition::State::OuterWall, roomId = 2401, outerWallIdx = 4, exclusionZoneIdx = -1, distance = -0.00194683 (Buildings/BuildingControlUnit/VWM/test_vwm_xml.cpp:408) // --> accept s small neg hitPos.distance: // if (hitPos.distance < -0.00001) // INFO_LOG(app.baseLogger()) << "******** Building '" << building.name() << "' -> BSMs ohne Raumbezug " << "(maxZ=" << maxZ << ")" ; if (bsmConfigOhneRaumbezug.bsmMap.isEmpty()) { INFO_LOG(app.baseLogger()) << "Building '" << building.name() << "' -> no outdoor BSM"; } else { WARNING_LOG(app.baseLogger()) << "Building '" << building.name() << "' -> has outdoor BSMs. Size=" << bsmConfigOhneRaumbezug.bsmMap.size(); for (auto bsm : bsmConfigOhneRaumbezug.bsmMap) { WARNING_LOG(app.baseLogger()) << "Building '" << building.name() << "' -> has outdoor BSMs:" << "bsm " << bsm.id << " ; position: " << bsm.position << " ; bsm direction: " << bsm.direction; } } INFO_LOG(app.baseLogger()) << "******** Building '" << building.name() << "' -> checkBsmPosition using rooms"; for(const MyRoom &myRoom : qAsConst(rooms)) { const int roomIdToCheck = myRoom.id; LtsUtils::QPoint3D center = myRoom.center(); center.setZ(maxZ); // Bldg 1147, Raum 2203 (r 5075) // Maybe the BSM is on the edge of an triangle (wall/ceiling is modelled using 2 triangles) // |-----| // | / | // | / | // | / | // |/ | // |-----| // // Bldg 4127, Raum 101, 102, 201, 301: Hit is at the edge of the roof (y value) // Could be moved one millimeter to overcome that detection. // Since we simulate AWES here, it´s not realistic to hit the edge: center.rx() -= 0.1; // For bldg 1147, 3127 center.ry() -= 0.2; // For bldg 4127, 3127 const LtsUtils::QPoint3D direction(0,0,1); // bsm dir -> looking upwards auto hitPos = BuildingControlUnit::VWM::Math::checkBsmPosition(center, direction, building.rooms()); if (hitPos.state != BuildingControlUnit::VWM::HitPosition::State::OuterWall) { // find room at building to get number of OuterWalls: int numberOuterWalls = -1; const BuildingControlUnit::VWM::Room &room = building.room(roomIdToCheck); if (room.id() == roomIdToCheck) numberOuterWalls = room.outerWalls().size(); if (numberOuterWalls == 0) WARNING_LOG(app.baseLogger()) << "Building '" << building.name() << "' -> " << "Hit on center point for room " << roomIdToCheck << " will not hit building - no outterWalls" << " - position: " << center << ", bsm direction: " << direction << ", (hitPos=" << hitPos << ")"; else { // possible reasons: wall vertical | Aussentreppe const bool withinFootprint = footprintPoly.containsPoint(center.toPointF(), Qt::OddEvenFill); if ((footprintPoly.size() >= 4) && // valid withinFootprint) { // Aussentreppen haben oft keine Wände... WARNING_LOG(app.baseLogger()) << "Building '" << building.name() << "' -> " << "Hit on center point for room " << roomIdToCheck << " will not hit building - " << " maybe according outterWalls (#=" << numberOuterWalls << ") are vertical?" << " - position: " << center << ", bsm direction: " << direction << ", (hitPos=" << hitPos << ")"; } else { // Aussentreppen liegen oft ausserhalb... WARNING_LOG(app.baseLogger()) << "Building '" << building.name() << "' -> " << "Hit on center point for room " << roomIdToCheck << " will not hit building - " << " it´s OUT OF FOOTPRINT" << " - position: " << center << ", bsm direction: " << direction; bAllOk = false; } } } // Guez-Building 1114: (renoirRepo 4722) // 2021-07-02 12:21:41.600 INFO BCU::VWM::Math |#| Searching nearest room for bsm position (6.023,-4.634,8.556), bsmViewDirection (0,0,1), hit direction (-0,-0,-1), #rooms = 11 (ies/Buildings/BuildingControlUnit/VWM/vwm_math.cpp:452) // 2021-07-02 12:21:41.600 INFO BCU::VWM::Math |#| HitPosition: 1, roomId = 2301, outerWallIndex = 11, exclusionZoneIndex = -1 (ies/Buildings/BuildingControlUnit/VWM/vwm_math.cpp:539) // 2021-07-02 12:21:41.600 WARN BCU::main |#| Building 'bldg_1114' -> Hit on center point for room results in hit pos behind a wall (-7.41573e-16m) instead in front - looks like the house is more than 8.556m high!?!?, (hitPos=state = HitPosition::State::OuterWall, roomId = 2301, outerWallIdx = 11, exclusionZoneIdx = -1, distance = -7.41573e-16 (Buildings/BuildingControlUnit/VWM/test_vwm_xml.cpp:217) // 2021-07-02 12:21:41.600 INFO BCU::VWM::Math |#| Searching nearest room for bsm position (6.023,-4.634,8.556), bsmViewDirection (0,0,1), hit direction (-0,-0,-1), #rooms = 11 (ies/Buildings/BuildingControlUnit/VWM/vwm_math.cpp:452) // 2021-07-02 12:21:41.601 INFO BCU::VWM::Math |#| HitPosition: 1, roomId = 2301, outerWallIndex = 11, exclusionZoneIndex = -1 (ies/Buildings/BuildingControlUnit/VWM/vwm_math.cpp:539) // 2021-07-02 12:21:41.601 WARN BCU::main |#| Building 'bldg_1114' -> Hit on center point for room results in hit pos behind a wall (-7.41573e-16m) instead in front - looks like the house is more than 8.556m high!?!?, (hitPos=state = HitPosition::State::OuterWall, roomId = 2301, outerWallIdx = 11, exclusionZoneIdx = -1, distance = -7.41573e-16 (Buildings/BuildingControlUnit/VWM/test_vwm_xml.cpp:217) // --> accept s small neg hitPos.distance: // if (hitPos.distance < -0.00001) // else if (hitPos.distance < -0.02) { WARNING_LOG(app.baseLogger()) << " Building '" << building.name() << "' -> " << "Hit on center point for room results in hit pos behind a wall (" << QString::number(hitPos.distance) << "m) instead in front - looks like the house is more than " << center.z() << "m high!?!?" << ", (hitPos=" << hitPos; bAllOk = false; } else { // set new Z to compare with room height of room which is hit center.setZ(rooms.value(hitPos.roomId).maxZ()); // hit calc -> mirror direction hitPos = BuildingControlUnit::VWM::Math::getHitPosition(center, direction * -1, building.rooms()); INFO_LOG(app.baseLogger()) << " Building '" << building.name() << "' -> " << "Hit on center point for room " << myRoom.id << " looks fine - room which will be hit: " << hitPos.roomId << ", " << QString::number(hitPos.distance) << "m away from outer wall" << ((hitPos.distance < 1) ? "" : " - to far away from ceiling!"); } } // for all rooms INFO_LOG(app.baseLogger()) << "******** Building '" << building.name() << "' -> checkBsmPosition using BSMs"; for (const BuildingControlUnit::Bsm &bsm : qAsConst(bsmConfig.bsmMap)) { const auto hitPos = BuildingControlUnit::VWM::Math::checkBsmPosition(bsm.position, bsm.direction, building.rooms()); if (hitPos.state != BuildingControlUnit::VWM::HitPosition::State::OuterWall) { WARNING_LOG(app.baseLogger()) << "Building '" << building.name() << "' -> " << "Effect on bsm " << bsm.id << " will not hit building ! - position: " << bsm.position << ", bsm direction: " << bsm.direction << ", state: " << hitPos.state; bAllOk = false; } else if (hitPos.distance < -0.02) { WARNING_LOG(app.baseLogger()) << "Building '" << building.name() << "' -> " << "Effect on bsm " << bsm.id << " results in hit pos behind a wall (" << QString::number(hitPos.distance) << "m) instead in front - BSM is not modelled in the correct place: " << bsm.position << ", bsm direction: " << bsm.direction << ", roomId which was hit: " << hitPos.roomId; bAllOk = false; } else { INFO_LOG(app.baseLogger()) << "Building '" << building.name() << "' -> " << "BSM with id " << bsm.id << " looks fine - room which will be hit: " << hitPos.roomId << ", " << QString::number(hitPos.distance) << "m away from outer wall"; } } #ifdef USE_BUILDINGROOMGEOMETRY int counterOk = 0; int counterZero = 0; int counterToMuch = 0; INFO_LOG(app.baseLogger()) << "******** Building '" << building.name() << "' -> pointIsInWhichRooms using rooms"; int heightZOffset = 2; for (int heightZ = 1; heightZ " << ", roomIdToCheck = " << roomIdToCheck << ", detoLoc = " << delayedDetonationLocation << " fine, found just one room: " << roomsWithDetonation.first(); } else { QString roomIdsStr(QStringLiteral("roomIds(")); for (auto roomId : qAsConst(roomsWithDetonation)) roomIdsStr.append(QString(QStringLiteral("%1 ")).arg(roomId)); roomIdsStr.append(QStringLiteral(")")); WARNING_LOG(app.baseLogger()) << " Building / pointIsInWhichRooms '" << building.name() << "'" << ", roomIdToCheck = " << roomIdToCheck << ", delayedDetonationLocation = " << delayedDetonationLocation << " wrongSize roomsWithDetonation.size() != 1 , size=" << roomsWithDetonation.size() << ", roomIds=" << roomIdsStr; bAllOk = false; } } } // // for all rooms heightZ += heightZOffset; } // for heightZ INFO_LOG(app.baseLogger()) << "******** Building '" << building.name() << "' -> pointIsInWhichRooms using rooms RESULT" << ", counterZero = " << counterZero << ", counterOk = " << counterOk << ", counterToMuch = " << counterToMuch << ", percentOk = " << counterOk*100.0 / (counterZero+counterOk+counterToMuch); #endif // USE_BUILDINGROOMGEOMETRY return bAllOk ? 0 : 5; }