summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikhail Pozdnyakov <mikhail.pozdnyakov@mapbox.com>2019-12-04 17:05:11 +0200
committerMikhail Pozdnyakov <mikhail.pozdnyakov@mapbox.com>2020-01-08 12:43:26 +0200
commit2040d4d6f56ccb6b626af078fe752e1459565c6b (patch)
tree93af1ee42f718860b33b6e8e9c67059669abd457
parent7e5dc5608a34c691660160ae68746477662b5425 (diff)
downloadqtlocation-mapboxgl-2040d4d6f56ccb6b626af078fe752e1459565c6b.tar.gz
[core] In tile mode, place labels from tile borders first
-rw-r--r--src/mbgl/text/placement.cpp141
1 files changed, 88 insertions, 53 deletions
diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp
index 5e59e91465..32def0b611 100644
--- a/src/mbgl/text/placement.cpp
+++ b/src/mbgl/text/placement.cpp
@@ -142,10 +142,9 @@ Point<float> calculateVariableLayoutOffset(style::SymbolAnchorType anchor,
}
} // namespace
-void Placement::placeBucket(
- const SymbolBucket& bucket,
- const BucketPlacementParameters& params,
- std::set<uint32_t>& seenCrossTileIDs) {
+void Placement::placeBucket(const SymbolBucket& bucket,
+ const BucketPlacementParameters& params,
+ std::set<uint32_t>& seenCrossTileIDs) {
const auto& layout = *bucket.layout;
const auto& renderTile = params.tile;
const auto& state = collisionIndex.getTransformState();
@@ -158,11 +157,12 @@ void Placement::placeBucket(
state.matrixFor(posMatrix, renderTile.id);
matrix::multiply(posMatrix, params.projMatrix, posMatrix);
- mat4 textLabelPlaneMatrix = getLabelPlaneMatrix(posMatrix,
- layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map,
- layout.get<style::TextRotationAlignment>() == style::AlignmentType::Map,
- state,
- pixelsToTileUnits);
+ mat4 textLabelPlaneMatrix =
+ getLabelPlaneMatrix(posMatrix,
+ layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map,
+ layout.get<style::TextRotationAlignment>() == style::AlignmentType::Map,
+ state,
+ pixelsToTileUnits);
mat4 iconLabelPlaneMatrix = getLabelPlaneMatrix(posMatrix,
layout.get<style::IconPitchAlignment>() == style::AlignmentType::Map,
@@ -182,6 +182,7 @@ void Placement::placeBucket(
layout.get<style::SymbolPlacement>() == style::SymbolPlacementType::Line)) {
avoidEdges = tileBorders;
}
+ const bool stickToFirstAnchor = tileBorders && !avoidEdges;
const bool textAllowOverlap = layout.get<style::TextAllowOverlap>();
const bool iconAllowOverlap = layout.get<style::IconAllowOverlap>();
@@ -243,7 +244,7 @@ void Placement::placeBucket(
}
};
- const auto placeTextForPlacementModes = [&] (auto& placeHorizontalFn, auto& placeVerticalFn) {
+ const auto placeTextForPlacementModes = [&](auto& placeHorizontalFn, auto& placeVerticalFn) {
if (bucket.allowVerticalPlacement && symbolInstance.writingModes & WritingModeType::Vertical) {
assert(!bucket.placementModes.empty());
for (auto& placementMode : bucket.placementModes) {
@@ -293,7 +294,8 @@ void Placement::placeBucket(
const auto placeVertical = [&] {
if (bucket.allowVerticalPlacement && symbolInstance.verticalTextCollisionFeature) {
- return placeFeature(*symbolInstance.verticalTextCollisionFeature, style::TextWritingModeType::Vertical);
+ return placeFeature(*symbolInstance.verticalTextCollisionFeature,
+ style::TextWritingModeType::Vertical);
}
return std::pair<bool, bool>{false, false};
};
@@ -340,8 +342,6 @@ void Placement::placeBucket(
std::pair<bool, bool> placedFeature = {false, false};
const size_t anchorsSize = variableTextAnchors.size();
const size_t placementAttempts = textAllowOverlap ? anchorsSize * 2 : anchorsSize;
- const bool stickToFirstAnchor = tileBorders && !avoidEdges;
-
for (size_t i = 0u; i < placementAttempts; ++i) {
auto anchor = variableTextAnchors[i % anchorsSize];
const auto& avoidEdgesForVariableAnchor =
@@ -553,6 +553,44 @@ void Placement::placeBucket(
for (auto it = sortedSymbols.rbegin(); it != sortedSymbols.rend(); ++it) {
placeSymbol(*it);
}
+ } else if (stickToFirstAnchor) {
+ // In this case we first try to place symbols, which intersects the tile borders, so that
+ // those symbols will remain even if each tile is handled independently.
+ assert(mapMode == MapMode::Tile);
+ const auto& symbolInstances = bucket.symbolInstances;
+ std::vector<std::reference_wrapper<const SymbolInstance>> sorted(symbolInstances.begin(),
+ symbolInstances.end());
+ optional<style::TextVariableAnchorType> variableAnchor;
+ if (!variableTextAnchors.empty()) variableAnchor = variableTextAnchors.front();
+
+ auto intersectsTileBorder = [&](const SymbolInstance& symbol) -> bool {
+ if (symbol.textCollisionFeature.boxes.empty()) return false;
+ Point<float> offset{};
+ if (variableAnchor) {
+ const CollisionBox& textBox = symbol.textCollisionFeature.boxes.front();
+ float width = textBox.x2 - textBox.x1;
+ float height = textBox.y2 - textBox.y1;
+ offset = calculateVariableLayoutOffset(*variableAnchor,
+ width,
+ height,
+ symbol.variableTextOffset,
+ symbol.textBoxScale,
+ rotateWithMap,
+ pitchWithMap,
+ state.getBearing());
+ }
+ return collisionIndex.featureIntersectsTileBorders(
+ symbol.textCollisionFeature, offset, posMatrix, pixelRatio, *tileBorders);
+ };
+
+ std::stable_sort(sorted.begin(), sorted.end(), [&](const SymbolInstance& a, const SymbolInstance& b) {
+ return intersectsTileBorder(a) && !intersectsTileBorder(b);
+ });
+
+ for (const SymbolInstance& symbol : sorted) {
+ placeSymbol(symbol);
+ }
+
} else {
for (const SymbolInstance& symbol : bucket.symbolInstances) {
placeSymbol(symbol);
@@ -891,7 +929,8 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket,
}
}
if (symbolInstance.hasIcon()) {
- const auto& opacityVertex = SymbolIconProgram::opacityVertex(opacityState.icon.placed, opacityState.icon.opacity);
+ const auto& opacityVertex =
+ SymbolIconProgram::opacityVertex(opacityState.icon.placed, opacityState.icon.opacity);
auto& iconBuffer = symbolInstance.hasSdfIcon() ? bucket.sdfIcon : bucket.icon;
if (symbolInstance.placedIconIndex) {
@@ -904,7 +943,7 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket,
iconBuffer.placedSymbols[*symbolInstance.placedVerticalIconIndex].hidden = opacityState.isHidden();
}
}
-
+
auto updateIconCollisionBox = [&](const auto& feature, const bool placed, const Point<float>& shift) {
if (feature.alongLine) {
return;
@@ -913,45 +952,41 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket,
bucket.iconCollisionBox->dynamicVertices.extend(feature.boxes.size() * 4, dynamicVertex);
};
- auto updateTextCollisionBox = [this,
- &bucket,
- &symbolInstance,
- &state,
- variablePlacement,
- rotateWithMap,
- pitchWithMap](const auto& feature, const bool placed) {
- Point<float> shift{0.0f, 0.0f};
- if (feature.alongLine) {
- return shift;
- }
- bool used = true;
- if (variablePlacement) {
- auto foundOffset = variableOffsets.find(symbolInstance.crossTileID);
- if (foundOffset != variableOffsets.end()) {
- const VariableOffset& variableOffset = foundOffset->second;
- // This will show either the currently placed position or the last
- // successfully placed position (so you can visualize what collision
- // just made the symbol disappear, and the most likely place for the
- // symbol to come back)
- shift = calculateVariableLayoutOffset(variableOffset.anchor,
- variableOffset.width,
- variableOffset.height,
- variableOffset.offset,
- variableOffset.textBoxScale,
- rotateWithMap,
- pitchWithMap,
- state.getBearing());
- } else {
- // No offset -> this symbol hasn't been placed since coming on-screen
- // No single box is particularly meaningful and all of them would be too noisy
- // Use the center box just to show something's there, but mark it "not used"
- used = false;
+ auto updateTextCollisionBox =
+ [this, &bucket, &symbolInstance, &state, variablePlacement, rotateWithMap, pitchWithMap](
+ const auto& feature, const bool placed) {
+ Point<float> shift{0.0f, 0.0f};
+ if (feature.alongLine) {
+ return shift;
}
- }
- const auto& dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, !used, shift);
- bucket.textCollisionBox->dynamicVertices.extend(feature.boxes.size() * 4, dynamicVertex);
- return shift;
- };
+ bool used = true;
+ if (variablePlacement) {
+ auto foundOffset = variableOffsets.find(symbolInstance.crossTileID);
+ if (foundOffset != variableOffsets.end()) {
+ const VariableOffset& variableOffset = foundOffset->second;
+ // This will show either the currently placed position or the last
+ // successfully placed position (so you can visualize what collision
+ // just made the symbol disappear, and the most likely place for the
+ // symbol to come back)
+ shift = calculateVariableLayoutOffset(variableOffset.anchor,
+ variableOffset.width,
+ variableOffset.height,
+ variableOffset.offset,
+ variableOffset.textBoxScale,
+ rotateWithMap,
+ pitchWithMap,
+ state.getBearing());
+ } else {
+ // No offset -> this symbol hasn't been placed since coming on-screen
+ // No single box is particularly meaningful and all of them would be too noisy
+ // Use the center box just to show something's there, but mark it "not used"
+ used = false;
+ }
+ }
+ const auto& dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, !used, shift);
+ bucket.textCollisionBox->dynamicVertices.extend(feature.boxes.size() * 4, dynamicVertex);
+ return shift;
+ };
auto updateCollisionCircles = [&](const auto& feature, const bool placed, bool isText) {
if (!feature.alongLine) {