diff options
author | Ansis Brammanis <brammanis@gmail.com> | 2016-01-19 17:35:20 -0800 |
---|---|---|
committer | Ansis Brammanis <brammanis@gmail.com> | 2016-01-20 16:10:38 -0800 |
commit | 0fda834397b5d937fb5843143ae8df8b27dd8954 (patch) | |
tree | bfb391cf45b7569d2bf0780103ddbe89cdbde2b2 /src | |
parent | f9c9ee93515ab26a508d0dec7a48cdab5fb70500 (diff) | |
download | qtlocation-mapboxgl-0fda834397b5d937fb5843143ae8df8b27dd8954.tar.gz |
[core] implement symbol-avoid-edges
ref #3582
if `symbol-avoid-edges` is true, this prevents symbols from colliding
with tile edges.
Diffstat (limited to 'src')
-rw-r--r-- | src/mbgl/renderer/symbol_bucket.cpp | 12 | ||||
-rw-r--r-- | src/mbgl/text/collision_tile.cpp | 119 | ||||
-rw-r--r-- | src/mbgl/text/collision_tile.hpp | 8 |
3 files changed, 98 insertions, 41 deletions
diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index 45e718fd94..75cf496d15 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -396,10 +396,14 @@ void SymbolBucket::placeFeatures(CollisionTile& collisionTile) { // Calculate the scales at which the text and icon can be placed without collision. - float glyphScale = hasText && !layout.text.allowOverlap ? - collisionTile.placeFeature(symbolInstance.textCollisionFeature) : collisionTile.minScale; - float iconScale = hasIcon && !layout.icon.allowOverlap ? - collisionTile.placeFeature(symbolInstance.iconCollisionFeature) : collisionTile.minScale; + float glyphScale = hasText ? + collisionTile.placeFeature(symbolInstance.textCollisionFeature, + layout.text.allowOverlap, layout.avoidEdges) : + collisionTile.minScale; + float iconScale = hasIcon ? + collisionTile.placeFeature(symbolInstance.iconCollisionFeature, + layout.icon.allowOverlap, layout.avoidEdges) : + collisionTile.minScale; // Combine the scales for icons and text. diff --git a/src/mbgl/text/collision_tile.cpp b/src/mbgl/text/collision_tile.cpp index dbd9a6cb10..b24e2cfcdb 100644 --- a/src/mbgl/text/collision_tile.cpp +++ b/src/mbgl/text/collision_tile.cpp @@ -3,13 +3,26 @@ namespace mbgl { -CollisionTile::CollisionTile(PlacementConfig config_) : config(config_) { +auto infinity = std::numeric_limits<float>::infinity(); + +CollisionTile::CollisionTile(PlacementConfig config_) : config(config_), + edges({{ + // left + CollisionBox(vec2<float>(0, 0), 0, -infinity, 0, infinity, infinity), + // right + CollisionBox(vec2<float>(extent, 0), 0, -infinity, 0, infinity, infinity), + // top + CollisionBox(vec2<float>(0, 0), -infinity, 0, infinity, 0, infinity), + // bottom + CollisionBox(vec2<float>(0, extent), -infinity, 0, infinity, 0, infinity), + }}) { tree.clear(); // Compute the transformation matrix. const float angle_sin = std::sin(config.angle); const float angle_cos = std::cos(config.angle); rotationMatrix = { { angle_cos, -angle_sin, angle_sin, angle_cos } }; + reverseRotationMatrix = { { angle_cos, angle_sin, -angle_sin, angle_cos } }; // Stretch boxes in y direction to account for the map tilt. const float _yStretch = 1.0f / std::cos(config.pitch); @@ -19,54 +32,88 @@ CollisionTile::CollisionTile(PlacementConfig config_) : config(config_) { yStretch = std::pow(_yStretch, 1.3); } -float CollisionTile::placeFeature(const CollisionFeature &feature) { - float minPlacementScale = minScale; +float CollisionTile::findPlacementScale(float minPlacementScale, const vec2<float>& anchor, + const CollisionBox& box, const vec2<float>& blockingAnchor, const CollisionBox& blocking) { - for (auto& box : feature.boxes) { - const auto anchor = box.anchor.matMul(rotationMatrix); - std::vector<CollisionTreeBox> blockingBoxes; - tree.query(bgi::intersects(getTreeBox(anchor, box)), std::back_inserter(blockingBoxes)); + // Find the lowest scale at which the two boxes can fit side by side without overlapping. + // Original algorithm: + float s1 = (blocking.x1 - box.x2) / (anchor.x - blockingAnchor.x); // scale at which new box is to the left of old box + float s2 = (blocking.x2 - box.x1) / (anchor.x - blockingAnchor.x); // scale at which new box is to the right of old box + float s3 = (blocking.y1 - box.y2) * yStretch / (anchor.y - blockingAnchor.y); // scale at which new box is to the top of old box + float s4 = (blocking.y2 - box.y1) * yStretch / (anchor.y - blockingAnchor.y); // scale at which new box is to the bottom of old box - for (auto& blockingTreeBox : blockingBoxes) { - const auto& blocking = std::get<1>(blockingTreeBox); - auto blockingAnchor = blocking.anchor.matMul(rotationMatrix); + if (std::isnan(s1) || std::isnan(s2)) s1 = s2 = 1; + if (std::isnan(s3) || std::isnan(s4)) s3 = s4 = 1; - // Find the lowest scale at which the two boxes can fit side by side without overlapping. - // Original algorithm: - float s1 = (blocking.x1 - box.x2) / (anchor.x - blockingAnchor.x); // scale at which new box is to the left of old box - float s2 = (blocking.x2 - box.x1) / (anchor.x - blockingAnchor.x); // scale at which new box is to the right of old box - float s3 = (blocking.y1 - box.y2) * yStretch / (anchor.y - blockingAnchor.y); // scale at which new box is to the top of old box - float s4 = (blocking.y2 - box.y1) * yStretch / (anchor.y - blockingAnchor.y); // scale at which new box is to the bottom of old box + float collisionFreeScale = ::fmin(::fmax(s1, s2), ::fmax(s3, s4)); + + if (collisionFreeScale > blocking.maxScale) { + // After a box's maxScale the label has shrunk enough that the box is no longer needed to cover it, + // so unblock the new box at the scale that the old box disappears. + collisionFreeScale = blocking.maxScale; + } - if (std::isnan(s1) || std::isnan(s2)) s1 = s2 = 1; - if (std::isnan(s3) || std::isnan(s4)) s3 = s4 = 1; + if (collisionFreeScale > box.maxScale) { + // If the box can only be shown after it is visible, then the box can never be shown. + // But the label can be shown after this box is not visible. + collisionFreeScale = box.maxScale; + } - float collisionFreeScale = ::fmin(::fmax(s1, s2), ::fmax(s3, s4)); + if (collisionFreeScale > minPlacementScale && + collisionFreeScale >= blocking.placementScale) { + // If this collision occurs at a lower scale than previously found collisions + // and the collision occurs while the other label is visible - if (collisionFreeScale > blocking.maxScale) { - // After a box's maxScale the label has shrunk enough that the box is no longer needed to cover it, - // so unblock the new box at the scale that the old box disappears. - collisionFreeScale = blocking.maxScale; - } + // this this is the lowest scale at which the label won't collide with anything + minPlacementScale = collisionFreeScale; + } - if (collisionFreeScale > box.maxScale) { - // If the box can only be shown after it is visible, then the box can never be shown. - // But the label can be shown after this box is not visible. - collisionFreeScale = box.maxScale; - } + return minPlacementScale; +} + +float CollisionTile::placeFeature(const CollisionFeature &feature, const bool allowOverlap, const bool avoidEdges) { + + float minPlacementScale = minScale; + + for (auto& box : feature.boxes) { + const auto anchor = box.anchor.matMul(rotationMatrix); + + if (!allowOverlap) { + std::vector<CollisionTreeBox> blockingBoxes; + tree.query(bgi::intersects(getTreeBox(anchor, box)), std::back_inserter(blockingBoxes)); - if (collisionFreeScale > minPlacementScale && - collisionFreeScale >= blocking.placementScale) { - // If this collision occurs at a lower scale than previously found collisions - // and the collision occurs while the other label is visible + for (auto& blockingTreeBox : blockingBoxes) { + const auto& blocking = std::get<1>(blockingTreeBox); + auto blockingAnchor = blocking.anchor.matMul(rotationMatrix); - // this this is the lowest scale at which the label won't collide with anything - minPlacementScale = collisionFreeScale; + minPlacementScale = findPlacementScale(minPlacementScale, anchor, box, blockingAnchor, blocking); + if (minPlacementScale >= maxScale) return minPlacementScale; } + } - if (minPlacementScale >= maxScale) return minPlacementScale; + if (avoidEdges) { + const vec2<float> tl = { box.x1, box.y1 }; + const vec2<float> tr = { box.x2, box.y1 }; + const vec2<float> bl = { box.x1, box.y2 }; + const vec2<float> br = { box.x2, box.y2 }; + const vec2<float> rtl = tl.matMul(reverseRotationMatrix); + const vec2<float> rtr = tr.matMul(reverseRotationMatrix); + const vec2<float> rbl = bl.matMul(reverseRotationMatrix); + const vec2<float> rbr = br.matMul(reverseRotationMatrix); + CollisionBox rotatedBox(box.anchor, + ::fmin(::fmin(rtl.x, rtr.x), ::fmin(rbl.x, rbr.x)), + ::fmin(::fmin(rtl.y, rtr.y), ::fmin(rbl.y, rbr.y)), + ::fmax(::fmax(rtl.x, rtr.x), ::fmax(rbl.x, rbr.x)), + ::fmax(::fmax(rtl.y, rtr.y), ::fmax(rbl.y, rbr.y)), + box.maxScale); + + for (auto& blocking : edges) { + minPlacementScale = findPlacementScale(minPlacementScale, box.anchor, rotatedBox, blocking.anchor, blocking); + + if (minPlacementScale >= maxScale) return minPlacementScale; + } } } diff --git a/src/mbgl/text/collision_tile.hpp b/src/mbgl/text/collision_tile.hpp index edd5eb61a0..f535dfe4ff 100644 --- a/src/mbgl/text/collision_tile.hpp +++ b/src/mbgl/text/collision_tile.hpp @@ -37,7 +37,7 @@ class CollisionTile { public: explicit CollisionTile(PlacementConfig); - float placeFeature(const CollisionFeature& feature); + float placeFeature(const CollisionFeature& feature, const bool allowOverlap, const bool avoidEdges); void insertFeature(CollisionFeature& feature, const float minPlacementScale); const PlacementConfig config; @@ -47,10 +47,16 @@ public: float yStretch; private: + float findPlacementScale(float minPlacementScale, + const vec2<float>& anchor, const CollisionBox& box, + const vec2<float>& blockingAnchor, const CollisionBox& blocking); Box getTreeBox(const vec2<float>& anchor, const CollisionBox& box); Tree tree; std::array<float, 4> rotationMatrix; + std::array<float, 4> reverseRotationMatrix; + const float extent = 4096; + std::array<CollisionBox, 4> edges; }; } // namespace mbgl |