diff --git a/shared/public/CollisionGrid.h b/shared/public/CollisionGrid.h index c12feadec..99f41753b 100644 --- a/shared/public/CollisionGrid.h +++ b/shared/public/CollisionGrid.h @@ -49,10 +49,11 @@ struct IndexRange { class CollisionGrid { public: - CollisionGrid(const std::vector &vpMatrix, const Vec2I &size, float gridAngle) + CollisionGrid(const std::vector &vpMatrix, const Vec2I &size, float gridAngle, bool alwaysInsert) : vpMatrix(vpMatrix), size(size), sinNegGridAngle(std::sin(-gridAngle * M_PI / 180.0)), - cosNegGridAngle(std::cos(-gridAngle * M_PI / 180.0)) { + cosNegGridAngle(std::cos(-gridAngle * M_PI / 180.0)), + alwaysInsert(alwaysInsert) { cellSize = std::min(size.x, size.y) / (float) numCellsMinDim; numCellsX = std::ceil(size.x / cellSize) + 2 * numCellsPadding; numCellsY = std::ceil(size.y / cellSize) + 2 * numCellsPadding; @@ -98,47 +99,26 @@ class CollisionGrid { } } } - for (int16_t y = indexRange.yMin; y <= indexRange.yMax; y++) { - for (int16_t x = indexRange.xMin; x <= indexRange.xMax; x++) { - for (const auto &rect : gridRects[y][x]) { - if (CollisionUtil::checkRectCollision(projectedRectangle, rect)) { - return 1; - } - } - for (const auto &circle : gridCircles[y][x]) { - if (CollisionUtil::checkRectCircleCollision(projectedRectangle, circle)) { - return 1; - } - } - } + if (alwaysInsert) { + return checkRectInsertAlways(rectangle, projectedRectangle, indexRange); + } else { + return checkRectInsertOnCollision(rectangle, projectedRectangle, indexRange); } - - // Only insert, when not colliding - for (int16_t y = indexRange.yMin; y <= indexRange.yMax; y++) { - for (int16_t x = indexRange.xMin; x <= indexRange.xMax; x++) { - gridRects[y][x].push_back(projectedRectangle); - } - } - - if (rectangle.contentHash != 0 && rectangle.symbolSpacing > 0) { - spacedRects[rectangle.contentHash].push_back(projectedRectangle); - } - return 0; } /** * Add a vector of circles (which are then projected with the provided vpMatrix) and receive the feedback, if they have collided * with the previous content of the grid. Assumed to remain circles in the projected space. Only added, when not colliding! */ - bool addAndCheckCollisionCircles(const std::vector &circles) { + uint8_t addAndCheckCollisionCircles(const std::vector &circles) { if (circles.empty()) { - // No circles -> no collision - return false; + // No circles -> no colliding + return 0; } std::vector> projectedCircles; - for (const auto &circle : circles) { + for (const auto &circle: circles) { auto projectedCircle = getProjectedCircle(circle); IndexRange indexRange = getIndexRangeForCircle(projectedCircle); if (indexRange.isValid(numCellsX - 1, numCellsY - 1)) { @@ -148,50 +128,116 @@ class CollisionGrid { if (projectedCircles.empty()) { // no valid IndexRanges - return true; + return 2; } - for (const auto &[projectedCircle, indexRange, contentHash, symbolSpacing] : projectedCircles) { + for (const auto &[projectedCircle, indexRange, contentHash, symbolSpacing]: projectedCircles) { if (contentHash != 0 && symbolSpacing > 0) { const auto &equalRects = spacedRects.find(contentHash); if (equalRects != spacedRects.end()) { - for (const auto &other : equalRects->second) { + for (const auto &other: equalRects->second) { // Assume equal symbol spacing for all primitives with matching content if (CollisionUtil::checkRectCircleCollision(other, projectedCircle, symbolSpacing)) { - return true; + return 1; + } } } const auto &equalCircles = spacedCircles.find(contentHash); if (equalCircles != spacedCircles.end()) { - for (const auto &other : equalCircles->second) { + for (const auto &other: equalCircles->second) { // Assume equal symbol spacing for all primitives with matching content if (CollisionUtil::checkCircleCollision(projectedCircle, other, symbolSpacing)) { - return true; + return 1; + } + } + } + } + } + + if (alwaysInsert) { + return checkCirclesInsertAlways(circles, projectedCircles); + } else { + return checkCirclesInsertOnCollision(circles, projectedCircles); + } + } + +private: + uint8_t checkRectInsertOnCollision(const CollisionRectF &rectangle, const RectF &projectedRectangle, const IndexRange &indexRange) { + for (int16_t y = indexRange.yMin; y <= indexRange.yMax; y++) { + for (int16_t x = indexRange.xMin; x <= indexRange.xMax; x++) { + for (const auto &rect: gridRects[y][x]) { + if (CollisionUtil::checkRectCollision(projectedRectangle, rect)) { + return 1; + } + } + for (const auto &circle: gridCircles[y][x]) { + if (CollisionUtil::checkRectCircleCollision(projectedRectangle, circle)) { + return 1; + } + } + } + } + + for (int16_t y = indexRange.yMin; y <= indexRange.yMax; y++) { + for (int16_t x = indexRange.xMin; x <= indexRange.xMax; x++) { + gridRects[y][x].push_back(projectedRectangle); + } + } + if (rectangle.contentHash != 0 && rectangle.symbolSpacing > 0) { + spacedRects[rectangle.contentHash].push_back(projectedRectangle); + } + + return 0; + } + + uint8_t checkRectInsertAlways(const CollisionRectF &rectangle, const RectF &projectedRectangle, const IndexRange &indexRange) { + bool colliding = false; + for (int16_t y = indexRange.yMin; y <= indexRange.yMax; y++) { + for (int16_t x = indexRange.xMin; x <= indexRange.xMax; x++) { + if (!colliding) { + for (const auto &rect: gridRects[y][x]) { + if (CollisionUtil::checkRectCollision(projectedRectangle, rect)) { + colliding = true; + } + } + for (const auto &circle: gridCircles[y][x]) { + if (CollisionUtil::checkRectCircleCollision(projectedRectangle, circle)) { + colliding = true; } } } + gridRects[y][x].push_back(projectedRectangle); } + } + + if (rectangle.contentHash != 0 && rectangle.symbolSpacing > 0) { + spacedRects[rectangle.contentHash].push_back(projectedRectangle); + } + return colliding ? 1 : 0; + } + + uint8_t checkCirclesInsertOnCollision(const std::vector &circles, const std::vector> &projectedCircles) { + for (const auto &[projectedCircle, indexRange, contentHash, symbolSpacing] : projectedCircles) { for (int16_t y = indexRange.yMin; y <= indexRange.yMax; y++) { for (int16_t x = indexRange.xMin; x <= indexRange.xMax; x++) { for (const auto &rect : gridRects[y][x]) { if (CollisionUtil::checkRectCircleCollision(rect, projectedCircle)) { - return true; + return 1; } } for (const auto &circle : gridCircles[y][x]) { if (CollisionUtil::checkCircleCollision(projectedCircle, circle)) { - return true; + return 1; } } } } } - // Only insert when none colliding - for (const auto &[projectedCircle, indexRange, contentHash, symbolSpacing] : projectedCircles) { + for (const auto &[projectedCircle, indexRange, contentHash, symbolSpacing]: projectedCircles) { for (int16_t y = indexRange.yMin; y <= indexRange.yMax; y++) { for (int16_t x = indexRange.xMin; x <= indexRange.xMax; x++) { gridCircles[y][x].push_back(projectedCircle); @@ -201,10 +247,38 @@ class CollisionGrid { spacedCircles[contentHash].push_back(projectedCircle); } } - return false; + + return 0; + } + + uint8_t checkCirclesInsertAlways(const std::vector &circles, const std::vector> &projectedCircles) { + bool colliding = false; + for (const auto &[projectedCircle, indexRange, contentHash, symbolSpacing]: projectedCircles) { + for (int16_t y = indexRange.yMin; y <= indexRange.yMax; y++) { + for (int16_t x = indexRange.xMin; x <= indexRange.xMax; x++) { + if (!colliding) { + for (const auto &rect: gridRects[y][x]) { + if (CollisionUtil::checkRectCircleCollision(rect, projectedCircle)) { + colliding = true; + } + } + for (const auto &circle: gridCircles[y][x]) { + if (CollisionUtil::checkCircleCollision(projectedCircle, circle)) { + colliding = true; + } + } + } + gridCircles[y][x].push_back(projectedCircle); + } + } + if (contentHash != 0 && symbolSpacing > 0) { + spacedCircles[contentHash].push_back(projectedCircle); + } + } + + return colliding ? 1 : 0; } -private: RectF getProjectedRectangle(const CollisionRectF &rectangle) { temp2[0] = rectangle.x - rectangle.anchorX; // move x to the anchor temp2[1] = rectangle.y - rectangle.anchorY; @@ -304,5 +378,7 @@ class CollisionGrid { std::unordered_map> spacedRects; std::unordered_map> spacedCircles; + bool alwaysInsert = false; + std::vector temp1 = {0, 0, 0, 0}, temp2 = {0, 0, 0, 0}; }; diff --git a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayer.cpp b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayer.cpp index 8e5dbb3f6..cf719a802 100644 --- a/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayer.cpp +++ b/shared/src/map/layers/tiled/vector/Tiled2dMapVectorLayer.cpp @@ -561,9 +561,12 @@ void Tiled2dMapVectorLayer::update() { if (now - lastCollitionCheck > 3000 || tilesChanged) { lastCollitionCheck = now; bool enforceUpdate = !prevCollisionStillValid.test_and_set(); - collisionManager.syncAccess([&vpMatrix, &viewportSize, viewportRotation, enforceUpdate](const auto &manager) { - manager->collisionDetection(*vpMatrix, viewportSize, viewportRotation, enforceUpdate); - }); + collisionManager.syncAccess( + [&vpMatrix, &viewportSize, viewportRotation, enforceUpdate, persistingPlacement = mapDescription->persistingSymbolPlacement]( + const auto &manager) { + manager->collisionDetection(*vpMatrix, viewportSize, viewportRotation, enforceUpdate, + persistingPlacement); + }); isAnimating = true; } } diff --git a/shared/src/map/layers/tiled/vector/symbol/SymbolObjectCollisionWrapper.h b/shared/src/map/layers/tiled/vector/symbol/SymbolObjectCollisionWrapper.h index c19b713b0..5e794a297 100644 --- a/shared/src/map/layers/tiled/vector/symbol/SymbolObjectCollisionWrapper.h +++ b/shared/src/map/layers/tiled/vector/symbol/SymbolObjectCollisionWrapper.h @@ -42,12 +42,6 @@ class SymbolObjectCollisionWrapper { } bool operator<(const SymbolObjectCollisionWrapper &o) const { - if (isColliding != o.isColliding) { - return isColliding; - } - if (symbolObject->smallestVisibleZoom != o.symbolObject->smallestVisibleZoom) { - return symbolObject->smallestVisibleZoom > o.symbolObject->smallestVisibleZoom; - } if (symbolSortKey == o.symbolSortKey) { return symbolTileIndex > o.symbolTileIndex; } diff --git a/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSourceSymbolCollisionManager.cpp b/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSourceSymbolCollisionManager.cpp index 863e0fb6c..e4893db13 100644 --- a/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSourceSymbolCollisionManager.cpp +++ b/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSourceSymbolCollisionManager.cpp @@ -11,7 +11,7 @@ #include "Tiled2dMapVectorSourceSymbolCollisionManager.h" #include "CollisionGrid.h" -void Tiled2dMapVectorSourceSymbolCollisionManager::collisionDetection(const std::vector &vpMatrix, const Vec2I &viewportSize, float viewportRotation, bool enforceRecomputation) { +void Tiled2dMapVectorSourceSymbolCollisionManager::collisionDetection(const std::vector &vpMatrix, const Vec2I &viewportSize, float viewportRotation, bool enforceRecomputation, bool persistingPlacement) { std::vector layers; std::string currentSource; @@ -21,7 +21,7 @@ void Tiled2dMapVectorSourceSymbolCollisionManager::collisionDetection(const std: } lastVpMatrix = vpMatrix; - auto collisionGrid = std::make_shared(vpMatrix, viewportSize, viewportRotation); + auto collisionGrid = std::make_shared(vpMatrix, viewportSize, viewportRotation, persistingPlacement); const auto lambda = [&collisionGrid, &layers](auto manager){ if (auto strongManager = manager.lock()) { diff --git a/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSourceSymbolCollisionManager.h b/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSourceSymbolCollisionManager.h index e11386702..58b3c44db 100644 --- a/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSourceSymbolCollisionManager.h +++ b/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSourceSymbolCollisionManager.h @@ -18,7 +18,7 @@ class Tiled2dMapVectorSourceSymbolCollisionManager: public ActorObject { public: Tiled2dMapVectorSourceSymbolCollisionManager(const std::unordered_map> &symbolSourceDataManagers, std::shared_ptr mapDescription): symbolSourceDataManagers(symbolSourceDataManagers), mapDescription(mapDescription) {}; - void collisionDetection(const std::vector &vpMatrix, const Vec2I &viewportSize, float viewportRotation, bool enforceRecomputation); + void collisionDetection(const std::vector &vpMatrix, const Vec2I &viewportSize, float viewportRotation, bool enforceRecomputation, bool persistingPlacement); private: std::unordered_map> symbolSourceDataManagers; std::shared_ptr mapDescription; diff --git a/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSourceSymbolDataManager.cpp b/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSourceSymbolDataManager.cpp index b3ab657a6..1a69f0e5a 100644 --- a/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSourceSymbolDataManager.cpp +++ b/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSourceSymbolDataManager.cpp @@ -639,21 +639,9 @@ void Tiled2dMapVectorSourceSymbolDataManager::collisionDetection(std::vector(objectsIt->second)) { - symbolGroup.syncAccess([&allObjects, zoomIdentifier, persistingPlacement = persistingSymbolPlacement](auto group){ - const auto &objects = group->getSymbolObjectsForCollision(); - if (persistingPlacement) { - for (auto &object : objects) { - if (object.symbolObject->largestCollisionZoom == -1 || object.symbolObject->largestCollisionZoom < zoomIdentifier) { - allObjects.push_back(object); - } - else { - object.symbolObject->setHideFromCollision(true); - } - } - } else { - for(auto& o : objects) { - allObjects.push_back(o); - } + symbolGroup.syncAccess([&allObjects](auto group){ + for(auto& o : group->getSymbolObjectsForCollision()) { + allObjects.push_back(o); } }); } diff --git a/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolObject.cpp b/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolObject.cpp index 98e7e3300..6c75efe81 100644 --- a/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolObject.cpp +++ b/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolObject.cpp @@ -873,18 +873,12 @@ void Tiled2dMapVectorSymbolObject::collisionDetection(const double zoomIdentifie std::optional> boundingCircles = getMapAlignedBoundingCircles(zoomIdentifier, textSymbolPlacement != TextSymbolPlacement::POINT, true); // Collide, if no valid boundingCircles if (boundingCircles.has_value()) { - willCollide = collisionGrid->addAndCheckCollisionCircles(*boundingCircles); + auto check = collisionGrid->addAndCheckCollisionCircles(*boundingCircles); + willCollide = check == 1; + outside = check == 2; } else { willCollide = false; - } - outside = false; - } - - if (persistingSymbolPlacement) { - if (!willCollide && !outside && zoomIdentifier < smallestVisibleZoom) { - smallestVisibleZoom = zoomIdentifier; - } else if (willCollide && (largestCollisionZoom == -1 || zoomIdentifier > largestCollisionZoom)) { - largestCollisionZoom = zoomIdentifier; + outside = false; } } diff --git a/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolObject.h b/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolObject.h index 4c25851f7..d42f84f8d 100644 --- a/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolObject.h +++ b/shared/src/map/layers/tiled/vector/symbol/Tiled2dMapVectorSymbolObject.h @@ -110,9 +110,6 @@ class Tiled2dMapVectorSymbolObject { int customTextureOffset = 0; std::string stringIdentifier; - double smallestVisibleZoom = 999; - double largestCollisionZoom = -1; - private: double lastZoomEvaluation = -1; void evaluateStyleProperties(const double zoomIdentifier);