summaryrefslogtreecommitdiff
path: root/src/mbgl/text/placement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl/text/placement.cpp')
-rw-r--r--src/mbgl/text/placement.cpp237
1 files changed, 237 insertions, 0 deletions
diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp
new file mode 100644
index 0000000000..af0724bf80
--- /dev/null
+++ b/src/mbgl/text/placement.cpp
@@ -0,0 +1,237 @@
+#include <mbgl/text/placement.hpp>
+#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/renderer/layers/render_symbol_layer.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/tile/geometry_tile.cpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
+#include <mbgl/renderer/bucket.hpp>
+
+namespace mbgl {
+
+OpacityState::OpacityState(float targetOpacity_) : opacity(0), targetOpacity(targetOpacity_) {}
+
+OpacityState::OpacityState(OpacityState& prevState, float increment, float targetOpacity) :
+ opacity(std::fmax(0, std::fmin(1, prevState.opacity + prevState.targetOpacity == 1.0 ? increment : -increment))),
+ targetOpacity(targetOpacity) {}
+
+bool OpacityState::isHidden() const {
+ return opacity == 0 && targetOpacity == 0;
+}
+
+JointOpacityState::JointOpacityState(float iconOpacity_, float textOpacity_) :
+ icon(OpacityState(iconOpacity_)),
+ text(OpacityState(textOpacity_)) {}
+
+JointOpacityState::JointOpacityState(JointOpacityState& prevOpacityState, float increment, float iconOpacity, float textOpacity) :
+ icon(OpacityState(prevOpacityState.icon, increment, iconOpacity)),
+ text(OpacityState(prevOpacityState.text, increment, textOpacity)) {}
+
+
+Placement::Placement(const TransformState& state_) : collisionIndex(state_), state(state_) {}
+
+void Placement::placeLayer(RenderSymbolLayer& symbolLayer, bool showCollisionBoxes) {
+ for (RenderTile& renderTile : symbolLayer.renderTiles) {
+ if (!renderTile.tile.isRenderable()) {
+ continue;
+ }
+
+ auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl);
+ assert(dynamic_cast<SymbolBucket*>(bucket));
+ SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket);
+
+ auto& layout = symbolBucket.layout;
+
+ const float pixelsToTileUnits = renderTile.id.pixelsToTileUnits(1, state.getZoom());
+
+ const float scale = std::pow(2, state.getZoom() - renderTile.id.canonical.z);
+
+ mat4 textLabelPlaneMatrix = getLabelPlaneMatrix(renderTile.matrix,
+ layout.get<TextPitchAlignment>() == style::AlignmentType::Map,
+ layout.get<TextRotationAlignment>() == style::AlignmentType::Map,
+ state,
+ pixelsToTileUnits);
+
+ mat4 iconLabelPlaneMatrix = getLabelPlaneMatrix(renderTile.matrix,
+ layout.get<IconPitchAlignment>() == style::AlignmentType::Map,
+ layout.get<IconRotationAlignment>() == style::AlignmentType::Map,
+ state,
+ pixelsToTileUnits);
+
+ placeLayerBucket(symbolLayer, symbolBucket, renderTile.matrix, textLabelPlaneMatrix, iconLabelPlaneMatrix, scale, showCollisionBoxes);
+ }
+}
+
+void Placement::placeLayerBucket(
+ RenderSymbolLayer&,
+ SymbolBucket& bucket,
+ const mat4& posMatrix,
+ const mat4& textLabelPlaneMatrix,
+ const mat4& iconLabelPlaneMatrix,
+ const float scale,
+ const bool showCollisionBoxes) {
+ //assert(dynamic_cast<const SymbolLayer::Impl*>(&*symbolLayer.baseImpl));
+ //const auto& impl = static_cast<const style::SymbolLayer::Impl&>(*symbolLayer.baseImpl);
+
+ // TODO collision debug array clearing
+
+ auto partiallyEvaluatedTextSize = bucket.textSizeBinder->evaluateForZoom(state.getZoom());
+ auto partiallyEvaluatedIconSize = bucket.iconSizeBinder->evaluateForZoom(state.getZoom());
+
+ const bool iconWithoutText = !bucket.hasTextData() || bucket.layout.get<TextOptional>();
+ const bool textWithoutIcon = !bucket.hasIconData() || bucket.layout.get<IconOptional>();
+ float pixelRatio = util::EXTENT / util::tileSize;
+
+ for (auto& symbolInstance : bucket.symbolInstances) {
+
+
+ bool placeText = false;
+ bool placeIcon = false;
+
+ if (!symbolInstance.isDuplicate) {
+
+ if (symbolInstance.placedTextIndices.size()) {
+ assert(symbolInstance.placedTextIndices.size() != 0);
+
+ PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(symbolInstance.placedTextIndices.at(0));
+ const float fontSize = evaluateSizeForFeature(partiallyEvaluatedTextSize, placedSymbol);
+
+ placeText = collisionIndex.placeFeature(symbolInstance.textCollisionFeature,
+ posMatrix, textLabelPlaneMatrix, pixelRatio,
+ placedSymbol,scale, fontSize,
+ bucket.layout.get<TextAllowOverlap>(),
+ bucket.layout.get<TextPitchAlignment>() == style::AlignmentType::Map,
+ showCollisionBoxes);
+ }
+
+ if (symbolInstance.placedIconIndices.size()) {
+
+ PlacedSymbol& placedSymbol = bucket.icon.placedSymbols.at(symbolInstance.placedIconIndices.at(0));
+ const float fontSize = evaluateSizeForFeature(partiallyEvaluatedIconSize, placedSymbol);
+
+ placeIcon = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature,
+ posMatrix, iconLabelPlaneMatrix, pixelRatio,
+ placedSymbol, scale, fontSize,
+ bucket.layout.get<IconAllowOverlap>(),
+ bucket.layout.get<IconPitchAlignment>() == style::AlignmentType::Map,
+ showCollisionBoxes);
+ }
+
+ // combine placements for icon and text
+ if (!iconWithoutText && !textWithoutIcon) {
+ placeText = placeIcon = placeText && placeIcon;
+ } else if (!textWithoutIcon) {
+ placeText = placeText && placeIcon;
+ } else if (!iconWithoutText) {
+ placeIcon = placeText && placeIcon;
+ }
+
+ if (placeText) {
+ collisionIndex.insertFeature(symbolInstance.textCollisionFeature, bucket.layout.get<TextIgnorePlacement>());
+ }
+
+ if (placeIcon) {
+ collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, bucket.layout.get<IconIgnorePlacement>());
+ }
+
+ if (symbolInstance.crossTileID == 0) {
+ // TODO properly assign these
+ symbolInstance.crossTileID = ++maxCrossTileID;
+ }
+
+ placements.emplace(symbolInstance.crossTileID, PlacementPair(placeText, placeIcon));
+ }
+ }
+}
+
+void Placement::commit(std::unique_ptr<Placement> prevPlacement, TimePoint now) {
+ commitTime = now;
+
+ if (!prevPlacement) {
+ // First time doing placement. Fade in all labels from 0.
+ for (auto& placementPair : placements) {
+ opacities.emplace(placementPair.first, JointOpacityState(placementPair.second.icon, placementPair.second.text));
+ }
+
+ } else {
+ const Duration symbolFadeDuration(300);
+ float increment = (commitTime - prevPlacement->commitTime) / symbolFadeDuration;
+
+ // add the opacities from the current placement, and copy their current values from the previous placement
+ for (auto& placementPair : placements) {
+ auto prevOpacity = prevPlacement->opacities.find(placementPair.first);
+ if (prevOpacity != prevPlacement->opacities.end()) {
+ opacities.emplace(placementPair.first, JointOpacityState(prevOpacity->second, increment, placementPair.second.icon, placementPair.second.text));
+ } else {
+ opacities.emplace(placementPair.first, JointOpacityState(placementPair.second.icon, placementPair.second.text));
+ }
+ }
+
+ // copy and update values from the previous placement that aren't in the current placement but haven't finished fading
+ for (auto& prevOpacity : prevPlacement->opacities) {
+ if (opacities.find(prevOpacity.first) != opacities.end()) {
+ JointOpacityState jointOpacity(prevOpacity.second, increment, 0, 0);
+ if (jointOpacity.icon.opacity != jointOpacity.icon.targetOpacity ||
+ jointOpacity.text.opacity != jointOpacity.text.targetOpacity) {
+ opacities.emplace(prevOpacity.first, jointOpacity);
+ }
+ }
+ }
+ }
+}
+
+void Placement::updateLayerOpacities(RenderSymbolLayer& symbolLayer, gl::Context& context) {
+ for (RenderTile& renderTile : symbolLayer.renderTiles) {
+ if (!renderTile.tile.isRenderable()) {
+ continue;
+ }
+
+ auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl);
+ assert(dynamic_cast<SymbolBucket*>(bucket));
+ SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket);
+ updateBucketOpacities(symbolBucket, context);
+ }
+}
+
+void Placement::updateBucketOpacities(SymbolBucket& bucket, gl::Context& context) {
+ // TODO check if this clear is necessary, whether the vector has been moved out
+ if (bucket.hasTextData()) bucket.text.opacityVertices.clear();
+ if (bucket.hasIconData()) bucket.icon.opacityVertices.clear();
+
+ for (SymbolInstance& symbolInstance : bucket.symbolInstances) {
+ auto opacityState = getOpacity(symbolInstance.crossTileID);
+
+ // TODO check if hasText is the right thing here, or if there are cases where hasText is true but it's not added to the buffers
+ if (symbolInstance.hasText) {
+ // TODO mark PlacedSymbols as hidden so that they don't need to be projected at render time
+ auto opacityVertex = SymbolOpacityAttributes::vertex(opacityState.text.targetOpacity, opacityState.text.opacity);
+ for (size_t i = 0; i < symbolInstance.glyphQuads.size() * 4; i++) {
+ bucket.text.opacityVertices.emplace_back(opacityVertex);
+ }
+ }
+ if (symbolInstance.hasIcon) {
+ // TODO mark PlacedSymbols as hidden so that they don't need to be projected at render time
+ auto opacityVertex = SymbolOpacityAttributes::vertex(opacityState.icon.targetOpacity, opacityState.icon.opacity);
+ if (symbolInstance.iconQuad) {
+ bucket.icon.opacityVertices.emplace_back(opacityVertex);
+ bucket.icon.opacityVertices.emplace_back(opacityVertex);
+ bucket.icon.opacityVertices.emplace_back(opacityVertex);
+ bucket.icon.opacityVertices.emplace_back(opacityVertex);
+ }
+ }
+ }
+
+ if (bucket.hasTextData()) context.updateVertexBuffer(*bucket.text.opacityVertexBuffer, std::move(bucket.text.opacityVertices));
+ if (bucket.hasIconData()) context.updateVertexBuffer(*bucket.icon.opacityVertexBuffer, std::move(bucket.icon.opacityVertices));
+}
+
+JointOpacityState Placement::getOpacity(uint32_t crossTileSymbolID) const {
+ auto it = opacities.find(crossTileSymbolID);
+ if (it != opacities.end()) {
+ return it->second;
+ } else {
+ return JointOpacityState(0, 0);
+ }
+
+}
+
+} // namespace mbgl