summaryrefslogtreecommitdiff
path: root/src/mbgl/text
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl/text')
-rw-r--r--src/mbgl/text/collision_tile.cpp156
-rw-r--r--src/mbgl/text/collision_tile.hpp9
2 files changed, 96 insertions, 69 deletions
diff --git a/src/mbgl/text/collision_tile.cpp b/src/mbgl/text/collision_tile.cpp
index d6fc1a6ada..e485fbf36c 100644
--- a/src/mbgl/text/collision_tile.cpp
+++ b/src/mbgl/text/collision_tile.cpp
@@ -3,6 +3,7 @@
#include <mbgl/util/constants.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/math/minmax.hpp>
+#include <mbgl/util/intersection_tests.hpp>
#include <mapbox/geometry/envelope.hpp>
#include <mapbox/geometry/multi_point.hpp>
@@ -11,20 +12,7 @@
namespace mbgl {
-auto infinity = std::numeric_limits<float>::infinity();
-
-CollisionTile::CollisionTile(PlacementConfig config_) : config(std::move(config_)),
- edges({{
- // left
- CollisionBox(Point<float>(0, 0), 0, -infinity, 0, infinity, infinity),
- // right
- CollisionBox(Point<float>(util::EXTENT, 0), 0, -infinity, 0, infinity, infinity),
- // top
- CollisionBox(Point<float>(0, 0), -infinity, 0, infinity, 0, infinity),
- // bottom
- CollisionBox(Point<float>(0, util::EXTENT), -infinity, 0, infinity, 0, infinity),
- }}) {
-
+CollisionTile::CollisionTile(PlacementConfig config_) : config(std::move(config_)) {
// Compute the transformation matrix.
const float angle_sin = std::sin(config.angle);
const float angle_cos = std::cos(config.angle);
@@ -36,13 +24,12 @@ CollisionTile::CollisionTile(PlacementConfig config_) : config(std::move(config_
// The amount the map is squished depends on the y position.
// Sort of account for this by making all boxes a bit bigger.
- yStretch = std::pow(_yStretch, 1.3);
+ yStretch = std::pow(_yStretch, 1.3f);
}
-float CollisionTile::findPlacementScale(float minPlacementScale, const Point<float>& anchor,
- const CollisionBox& box, const Point<float>& blockingAnchor, const CollisionBox& blocking) {
-
+float CollisionTile::findPlacementScale(const Point<float>& anchor, const CollisionBox& box, const Point<float>& blockingAnchor, const CollisionBox& blocking) {
+ float minPlacementScale = minScale;
// Find the lowest scale at which the two boxes can fit side by side without overlapping.
// Original algorithm:
@@ -80,7 +67,18 @@ float CollisionTile::findPlacementScale(float minPlacementScale, const Point<flo
return minPlacementScale;
}
-float CollisionTile::placeFeature(const CollisionFeature& feature, const bool allowOverlap, const bool avoidEdges) {
+float CollisionTile::placeFeature(const CollisionFeature& feature, bool allowOverlap, bool avoidEdges) {
+ static const float infinity = std::numeric_limits<float>::infinity();
+ static const std::array<CollisionBox, 4> edges {{
+ // left
+ CollisionBox(Point<float>(0, 0), 0, -infinity, 0, infinity, infinity),
+ // right
+ CollisionBox(Point<float>(util::EXTENT, 0), 0, -infinity, 0, infinity, infinity),
+ // top
+ CollisionBox(Point<float>(0, 0), -infinity, 0, infinity, 0, infinity),
+ // bottom
+ CollisionBox(Point<float>(0, util::EXTENT), -infinity, 0, infinity, 0, infinity)
+ }};
float minPlacementScale = minScale;
@@ -92,20 +90,16 @@ float CollisionTile::placeFeature(const CollisionFeature& feature, const bool al
const CollisionBox& blocking = std::get<1>(*it);
Point<float> blockingAnchor = util::matrixMultiply(rotationMatrix, blocking.anchor);
- minPlacementScale = findPlacementScale(minPlacementScale, anchor, box, blockingAnchor, blocking);
+ minPlacementScale = util::max(minPlacementScale, findPlacementScale(anchor, box, blockingAnchor, blocking));
if (minPlacementScale >= maxScale) return minPlacementScale;
}
}
if (avoidEdges) {
- const Point<float> tl = { box.x1, box.y1 };
- const Point<float> tr = { box.x2, box.y1 };
- const Point<float> bl = { box.x1, box.y2 };
- const Point<float> br = { box.x2, box.y2 };
- const Point<float> rtl = util::matrixMultiply(reverseRotationMatrix, tl);
- const Point<float> rtr = util::matrixMultiply(reverseRotationMatrix, tr);
- const Point<float> rbl = util::matrixMultiply(reverseRotationMatrix, bl);
- const Point<float> rbr = util::matrixMultiply(reverseRotationMatrix, br);
+ const Point<float> rtl = util::matrixMultiply(reverseRotationMatrix, { box.x1, box.y1 });
+ const Point<float> rtr = util::matrixMultiply(reverseRotationMatrix, { box.x2, box.y1 });
+ const Point<float> rbl = util::matrixMultiply(reverseRotationMatrix, { box.x1, box.y2 });
+ const Point<float> rbr = util::matrixMultiply(reverseRotationMatrix, { box.x2, box.y2 });
CollisionBox rotatedBox(box.anchor,
util::min(rtl.x, rtr.x, rbl.x, rbr.x),
util::min(rtl.y, rtr.y, rbl.y, rbr.y),
@@ -114,8 +108,7 @@ float CollisionTile::placeFeature(const CollisionFeature& feature, const bool al
box.maxScale);
for (auto& blocking : edges) {
- minPlacementScale = findPlacementScale(minPlacementScale, box.anchor, rotatedBox, blocking.anchor, blocking);
-
+ minPlacementScale = util::max(minPlacementScale, findPlacementScale(box.anchor, rotatedBox, blocking.anchor, blocking));
if (minPlacementScale >= maxScale) return minPlacementScale;
}
}
@@ -124,7 +117,7 @@ float CollisionTile::placeFeature(const CollisionFeature& feature, const bool al
return minPlacementScale;
}
-void CollisionTile::insertFeature(CollisionFeature& feature, const float minPlacementScale, const bool ignorePlacement) {
+void CollisionTile::insertFeature(CollisionFeature& feature, float minPlacementScale, bool ignorePlacement) {
for (auto& box : feature.boxes) {
box.placementScale = minPlacementScale;
}
@@ -143,7 +136,25 @@ void CollisionTile::insertFeature(CollisionFeature& feature, const float minPlac
}
+// +---------------------------+ As you zoom, the size of the symbol changes
+// |(x1,y1) | | relative to the tile e.g. when zooming in,
+// | | | the symbol gets smaller relative to the tile.
+// | (x1',y1') v |
+// | +-------+-------+ | The boxes inserted into the tree represents
+// | | | | | the bounds at the integer zoom level (where
+// | | | | | the symbol is biggest relative to the tile).
+// | | | | |
+// |---->+-------+-------+<----| This happens because placement is updated
+// | | |(xa,ya)| | once every new integer zoom level e.g.
+// | | | | | std::floor(oldZoom) != std::floor(newZoom).
+// | | | | |
+// | +-------+-------+ | Thus, they don't represent the exact bounds
+// | ^ (x2',y2') | of the symbol at the current zoom level. For
+// | | | calculating the bounds at current zoom level
+// | | (x2,y2)| we must unscale the box using its center as
+// +---------------------------+ transform origin.
Box CollisionTile::getTreeBox(const Point<float>& anchor, const CollisionBox& box, const float scale) {
+ assert(box.x1 <= box.x2 && box.y1 <= box.y2);
return Box{
CollisionPoint{
anchor.x + box.x1 / scale,
@@ -156,49 +167,66 @@ Box CollisionTile::getTreeBox(const Point<float>& anchor, const CollisionBox& bo
};
}
-std::vector<IndexedSubfeature> CollisionTile::queryRenderedSymbols(const GeometryCoordinates& queryGeometry, const float scale) {
-
+std::vector<IndexedSubfeature> CollisionTile::queryRenderedSymbols(const GeometryCoordinates& queryGeometry, float scale) {
std::vector<IndexedSubfeature> result;
- if (queryGeometry.empty()) return result;
+ if (queryGeometry.empty() || (tree.empty() && ignoredTree.empty())) {
+ return result;
+ }
+
+ // Generate a rotated geometry out of the original query geometry.
+ // Scale has already been handled by the prior conversions.
+ GeometryCoordinates polygon;
+ for (const auto& point : queryGeometry) {
+ auto rotated = util::matrixMultiply(rotationMatrix, convertPoint<float>(point));
+ polygon.push_back(convertPoint<int16_t>(rotated));
+ }
+ // Predicate for ruling out already seen features.
std::unordered_map<std::string, std::unordered_set<std::size_t>> sourceLayerFeatures;
+ auto seenFeature = [&] (const CollisionTreeBox& treeBox) -> bool {
+ const IndexedSubfeature& feature = std::get<2>(treeBox);
+ const auto& seenFeatures = sourceLayerFeatures[feature.sourceLayerName];
+ return seenFeatures.find(feature.index) == seenFeatures.end();
+ };
- mapbox::geometry::multi_point<float> rotatedPoints {};
- rotatedPoints.reserve(queryGeometry.size());
- std::transform(queryGeometry.cbegin(), queryGeometry.cend(), std::back_inserter(rotatedPoints),
- [&](const auto& c) { return util::matrixMultiply(rotationMatrix, convertPoint<float>(c)); });
- const auto box = mapbox::geometry::envelope(rotatedPoints);
+ // Account for the rounding done when updating symbol shader variables.
+ const float roundedScale = std::pow(2.0f, std::ceil(util::log2(scale) * 10.0f) / 10.0f);
- const auto& anchor = box.min;
- CollisionBox queryBox(anchor, 0, 0, box.max.x - box.min.x, box.max.y - box.min.y, scale);
- auto predicates = bgi::intersects(getTreeBox(anchor, queryBox));
+ // Check if feature is rendered (collision free) at current scale.
+ auto visibleAtScale = [&] (const CollisionTreeBox& treeBox) -> bool {
+ const CollisionBox& box = std::get<1>(treeBox);
+ return roundedScale >= box.placementScale && roundedScale <= box.maxScale;
+ };
- auto fn = [&] (const Tree& tree_, bool ignorePlacement) {
+ // Check if query polygon intersects with the feature box at current scale.
+ auto intersectsAtScale = [&] (const CollisionTreeBox& treeBox) -> bool {
+ const CollisionBox& collisionBox = std::get<1>(treeBox);
+ const auto anchor = util::matrixMultiply(rotationMatrix, collisionBox.anchor);
+ const int16_t x1 = anchor.x + collisionBox.x1 / scale;
+ const int16_t y1 = anchor.y + collisionBox.y1 / scale * yStretch;
+ const int16_t x2 = anchor.x + collisionBox.x2 / scale;
+ const int16_t y2 = anchor.y + collisionBox.y2 / scale * yStretch;
+ auto bbox = GeometryCoordinates {
+ { x1, y1 }, { x2, y1 }, { x2, y2 }, { x1, y2 }
+ };
+ return util::polygonIntersectsPolygon(polygon, bbox);
+ };
+
+ auto predicates = bgi::satisfies(seenFeature)
+ && bgi::satisfies(visibleAtScale)
+ && bgi::satisfies(intersectsAtScale);
+
+ auto queryTree = [&](const auto& tree_) {
for (auto it = tree_.qbegin(predicates); it != tree_.qend(); ++it) {
- const CollisionBox& blocking = std::get<1>(*it);
- const IndexedSubfeature& indexedFeature = std::get<2>(*it);
-
- auto& seenFeatures = sourceLayerFeatures[indexedFeature.sourceLayerName];
- if (seenFeatures.find(indexedFeature.index) == seenFeatures.end()) {
- if (ignorePlacement) {
- seenFeatures.insert(indexedFeature.index);
- result.push_back(indexedFeature);
- } else {
- auto blockingAnchor = util::matrixMultiply(rotationMatrix, blocking.anchor);
- float minPlacementScale = findPlacementScale(minScale, anchor, queryBox, blockingAnchor, blocking);
- if (minPlacementScale >= scale) {
- seenFeatures.insert(indexedFeature.index);
- result.push_back(indexedFeature);
- }
- }
- }
+ const IndexedSubfeature& feature = std::get<2>(*it);
+ auto& seenFeatures = sourceLayerFeatures[feature.sourceLayerName];
+ seenFeatures.insert(feature.index);
+ result.push_back(feature);
}
};
- bool ignorePlacement = false;
- fn(tree, ignorePlacement);
- ignorePlacement = true;
- fn(ignoredTree, ignorePlacement);
+ queryTree(tree);
+ queryTree(ignoredTree);
return result;
}
diff --git a/src/mbgl/text/collision_tile.hpp b/src/mbgl/text/collision_tile.hpp
index dbc23b2a79..186cd19d28 100644
--- a/src/mbgl/text/collision_tile.hpp
+++ b/src/mbgl/text/collision_tile.hpp
@@ -39,10 +39,10 @@ class CollisionTile {
public:
explicit CollisionTile(PlacementConfig);
- float placeFeature(const CollisionFeature&, const bool allowOverlap, const bool avoidEdges);
- void insertFeature(CollisionFeature&, const float minPlacementScale, const bool ignorePlacement);
+ float placeFeature(const CollisionFeature&, bool allowOverlap, bool avoidEdges);
+ void insertFeature(CollisionFeature&, float minPlacementScale, bool ignorePlacement);
- std::vector<IndexedSubfeature> queryRenderedSymbols(const GeometryCoordinates&, const float scale);
+ std::vector<IndexedSubfeature> queryRenderedSymbols(const GeometryCoordinates&, float scale);
const PlacementConfig config;
@@ -52,10 +52,9 @@ public:
std::array<float, 4> rotationMatrix;
std::array<float, 4> reverseRotationMatrix;
- std::array<CollisionBox, 4> edges;
private:
- float findPlacementScale(float minPlacementScale,
+ float findPlacementScale(
const Point<float>& anchor, const CollisionBox& box,
const Point<float>& blockingAnchor, const CollisionBox& blocking);
Box getTreeBox(const Point<float>& anchor, const CollisionBox& box, const float scale = 1.0);