[core] Introduce variable text placement for point labels - Placement part
This commit is contained in:
@@ -739,7 +739,7 @@ void SymbolLayout::addToDebugBuffers(SymbolBucket& bucket) {
|
||||
|
||||
// Dynamic vertices are initialized so that the vertex count always agrees with
|
||||
// the layout vertex buffer, but they will always be updated before rendering happens
|
||||
auto dynamicVertex = CollisionBoxProgram::dynamicVertex(false, false);
|
||||
auto dynamicVertex = CollisionBoxProgram::dynamicVertex(false, false, {});
|
||||
collisionBuffer.dynamicVertices.emplace_back(dynamicVertex);
|
||||
collisionBuffer.dynamicVertices.emplace_back(dynamicVertex);
|
||||
collisionBuffer.dynamicVertices.emplace_back(dynamicVertex);
|
||||
|
||||
@@ -13,10 +13,9 @@ namespace mbgl {
|
||||
using CollisionBoxLayoutAttributes = TypeList<
|
||||
attributes::pos,
|
||||
attributes::anchor_pos,
|
||||
attributes::extrude,
|
||||
attributes::shift>;
|
||||
attributes::extrude>;
|
||||
|
||||
using CollisionBoxDynamicAttributes = TypeList<attributes::placed>;
|
||||
using CollisionBoxDynamicAttributes = TypeList<attributes::placed, attributes::shift>;
|
||||
|
||||
class CollisionBoxProgram : public Program<
|
||||
CollisionBoxProgram,
|
||||
@@ -45,17 +44,14 @@ public:
|
||||
{{
|
||||
static_cast<int16_t>(::round(o.x)),
|
||||
static_cast<int16_t>(::round(o.y))
|
||||
}},
|
||||
{{
|
||||
0.0f,
|
||||
0.0f
|
||||
}}
|
||||
};
|
||||
}
|
||||
|
||||
static gfx::Vertex<CollisionBoxDynamicAttributes> dynamicVertex(bool placed, bool notUsed) {
|
||||
static gfx::Vertex<CollisionBoxDynamicAttributes> dynamicVertex(bool placed, bool notUsed, Point<float> shift) {
|
||||
return {
|
||||
{{ static_cast<uint8_t>(placed), static_cast<uint8_t>(notUsed) }}
|
||||
{{ static_cast<uint8_t>(placed), static_cast<uint8_t>(notUsed) }},
|
||||
{{ shift.x, shift.y }}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -139,10 +135,6 @@ public:
|
||||
{{
|
||||
static_cast<int16_t>(::round(o.x)),
|
||||
static_cast<int16_t>(::round(o.y))
|
||||
}},
|
||||
{{
|
||||
0.0f,
|
||||
0.0f
|
||||
}}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -254,8 +254,8 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
|
||||
for (const PlacedSymbol& symbol : bucket.text.placedSymbols) {
|
||||
optional<VariableOffset> variableOffset;
|
||||
if (!symbol.hidden && symbol.crossTileID != 0u) {
|
||||
auto it = parameters.variableOffsets.find(symbol.crossTileID);
|
||||
if (it != parameters.variableOffsets.end()) {
|
||||
auto it = parameters.variableOffsets.get().find(symbol.crossTileID);
|
||||
if (it != parameters.variableOffsets.get().end()) {
|
||||
variableOffset = it->second;
|
||||
hasVariablePacement |= true;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,8 @@ PaintParameters::PaintParameters(gfx::Context& context_,
|
||||
const EvaluatedLight& evaluatedLight_,
|
||||
RenderStaticData& staticData_,
|
||||
ImageManager& imageManager_,
|
||||
LineAtlas& lineAtlas_)
|
||||
LineAtlas& lineAtlas_,
|
||||
Placement::VariableOffsets variableOffsets_)
|
||||
: context(context_),
|
||||
backend(backend_),
|
||||
state(updateParameters.transformState),
|
||||
@@ -26,6 +27,7 @@ PaintParameters::PaintParameters(gfx::Context& context_,
|
||||
contextMode(contextMode_),
|
||||
timePoint(updateParameters.timePoint),
|
||||
pixelRatio(pixelRatio_),
|
||||
variableOffsets(variableOffsets_),
|
||||
#ifndef NDEBUG
|
||||
programs((debugOptions & MapDebugOptions::Overdraw) ? staticData_.overdrawPrograms : staticData_.programs)
|
||||
#else
|
||||
|
||||
@@ -38,7 +38,8 @@ public:
|
||||
const EvaluatedLight&,
|
||||
RenderStaticData&,
|
||||
ImageManager&,
|
||||
LineAtlas&);
|
||||
LineAtlas&,
|
||||
Placement::VariableOffsets);
|
||||
|
||||
gfx::Context& context;
|
||||
RendererBackend& backend;
|
||||
@@ -57,7 +58,7 @@ public:
|
||||
TimePoint timePoint;
|
||||
|
||||
float pixelRatio;
|
||||
std::unordered_map<uint32_t, VariableOffset> variableOffsets;
|
||||
Placement::VariableOffsets variableOffsets;
|
||||
std::array<float, 2> pixelsToGLUnits;
|
||||
algorithm::ClipIDGenerator clipIDGenerator;
|
||||
|
||||
|
||||
@@ -263,7 +263,8 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
|
||||
renderLight.getEvaluated(),
|
||||
*staticData,
|
||||
*imageManager,
|
||||
*lineAtlas
|
||||
*lineAtlas,
|
||||
placement->getVariableOffsets()
|
||||
};
|
||||
|
||||
bool loaded = updateParameters.styleLoaded && isLoaded();
|
||||
@@ -344,26 +345,25 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
|
||||
|
||||
bool symbolBucketsChanged = false;
|
||||
const bool placementChanged = !placement->stillRecent(parameters.timePoint);
|
||||
std::unique_ptr<Placement> newPlacement;
|
||||
std::set<std::string> usedSymbolLayers;
|
||||
|
||||
if (placementChanged) {
|
||||
newPlacement = std::make_unique<Placement>(parameters.state, parameters.mapMode, updateParameters.transitionOptions, updateParameters.crossSourceCollisions);
|
||||
placement = std::make_unique<Placement>(parameters.state, parameters.mapMode, updateParameters.transitionOptions, updateParameters.crossSourceCollisions, std::move(placement));
|
||||
}
|
||||
|
||||
for (const auto& item : renderItemsWithSymbols) {
|
||||
if (crossTileSymbolIndex.addLayer(*item.layer.getSymbolInterface(), parameters.state.getLatLng().longitude())) symbolBucketsChanged = true;
|
||||
|
||||
if (newPlacement) {
|
||||
if (placementChanged) {
|
||||
usedSymbolLayers.insert(item.layer.getID());
|
||||
newPlacement->placeLayer(*item.layer.getSymbolInterface(), parameters.projMatrix, parameters.debugOptions & MapDebugOptions::Collision);
|
||||
placement->placeLayer(*item.layer.getSymbolInterface(), parameters.projMatrix, parameters.debugOptions & MapDebugOptions::Collision);
|
||||
}
|
||||
}
|
||||
|
||||
if (newPlacement) {
|
||||
newPlacement->commit(*placement, parameters.timePoint);
|
||||
if (placementChanged) {
|
||||
placement->commit(parameters.timePoint);
|
||||
crossTileSymbolIndex.pruneUnusedLayers(usedSymbolLayers);
|
||||
placement = std::move(newPlacement);
|
||||
parameters.variableOffsets = placement->getVariableOffsets();
|
||||
updateFadingTiles();
|
||||
} else {
|
||||
placement->setStale();
|
||||
|
||||
@@ -80,6 +80,7 @@ bool CollisionIndex::isInsideTile(const CollisionBox& box, const CollisionTileBo
|
||||
|
||||
|
||||
std::pair<bool,bool> CollisionIndex::placeFeature(CollisionFeature& feature,
|
||||
Point<float> shift,
|
||||
const mat4& posMatrix,
|
||||
const mat4& labelPlaneMatrix,
|
||||
const float textPixelRatio,
|
||||
@@ -95,10 +96,10 @@ std::pair<bool,bool> CollisionIndex::placeFeature(CollisionFeature& feature,
|
||||
CollisionBox& box = feature.boxes.front();
|
||||
const auto projectedPoint = projectAndGetPerspectiveRatio(posMatrix, box.anchor);
|
||||
const float tileToViewport = textPixelRatio * projectedPoint.second;
|
||||
box.px1 = box.x1 * tileToViewport + projectedPoint.first.x;
|
||||
box.py1 = box.y1 * tileToViewport + projectedPoint.first.y;
|
||||
box.px2 = box.x2 * tileToViewport + projectedPoint.first.x;
|
||||
box.py2 = box.y2 * tileToViewport + projectedPoint.first.y;
|
||||
box.px1 = (box.x1 + shift.x) * tileToViewport + projectedPoint.first.x;
|
||||
box.py1 = (box.y1 + shift.y) * tileToViewport + projectedPoint.first.y;
|
||||
box.px2 = (box.x2 + shift.x) * tileToViewport + projectedPoint.first.x;
|
||||
box.py2 = (box.y2 + shift.y) * tileToViewport + projectedPoint.first.y;
|
||||
|
||||
|
||||
if ((avoidEdges && !isInsideTile(box, *avoidEdges)) ||
|
||||
|
||||
@@ -23,6 +23,7 @@ public:
|
||||
explicit CollisionIndex(const TransformState&);
|
||||
|
||||
std::pair<bool,bool> placeFeature(CollisionFeature& feature,
|
||||
Point<float> shift,
|
||||
const mat4& posMatrix,
|
||||
const mat4& labelPlaneMatrix,
|
||||
const float textPixelRatio,
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#include <mbgl/text/placement.hpp>
|
||||
|
||||
#include <mbgl/layout/symbol_layout.hpp>
|
||||
#include <mbgl/renderer/render_layer.hpp>
|
||||
#include <mbgl/renderer/layers/render_layer_symbol_interface.hpp>
|
||||
#include <mbgl/renderer/render_tile.hpp>
|
||||
#include <mbgl/tile/geometry_tile.hpp>
|
||||
#include <mbgl/renderer/buckets/symbol_bucket.hpp>
|
||||
#include <mbgl/renderer/bucket.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
@@ -55,13 +58,18 @@ const CollisionGroups::CollisionGroup& CollisionGroups::get(const std::string& s
|
||||
}
|
||||
}
|
||||
|
||||
Placement::Placement(const TransformState& state_, MapMode mapMode_, style::TransitionOptions transitionOptions_, const bool crossSourceCollisions)
|
||||
Placement::Placement(const TransformState& state_, MapMode mapMode_, style::TransitionOptions transitionOptions_, const bool crossSourceCollisions, std::unique_ptr<Placement> prevPlacement_)
|
||||
: collisionIndex(state_)
|
||||
, state(state_)
|
||||
, mapMode(mapMode_)
|
||||
, transitionOptions(transitionOptions_)
|
||||
, collisionGroups(crossSourceCollisions)
|
||||
{}
|
||||
, prevPlacement(std::move(prevPlacement_))
|
||||
{
|
||||
if (prevPlacement) {
|
||||
prevPlacement->prevPlacement.reset(); // Only hold on to one placement back
|
||||
}
|
||||
}
|
||||
|
||||
void Placement::placeLayer(const RenderLayerSymbolInterface& symbolInterface, const mat4& projMatrix, bool showCollisionBoxes) {
|
||||
|
||||
@@ -121,6 +129,19 @@ void Placement::placeLayer(const RenderLayerSymbolInterface& symbolInterface, co
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
Point<float> calculateVariableLayoutOffset(style::SymbolAnchorType anchor, float width, float height, float radialOffset, float textBoxScale) {
|
||||
AnchorAlignment alignment = AnchorAlignment::getAnchorAlignment(anchor);
|
||||
float shiftX = -(alignment.horizontalAlign - 0.5f) * width;
|
||||
float shiftY = -(alignment.verticalAlign - 0.5f) * height;
|
||||
Point<float> offset = SymbolLayout::evaluateRadialOffset(anchor, radialOffset);
|
||||
return Point<float>(
|
||||
shiftX + offset.x * textBoxScale,
|
||||
shiftY + offset.y * textBoxScale
|
||||
);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Placement::placeLayerBucket(
|
||||
SymbolBucket& bucket,
|
||||
const mat4& posMatrix,
|
||||
@@ -161,8 +182,11 @@ void Placement::placeLayerBucket(
|
||||
// See https://github.com/mapbox/mapbox-gl-native/issues/12683
|
||||
const bool alwaysShowText = textAllowOverlap && (iconAllowOverlap || !bucket.hasIconData() || bucket.layout.get<style::IconOptional>());
|
||||
const bool alwaysShowIcon = iconAllowOverlap && (textAllowOverlap || !bucket.hasTextData() || bucket.layout.get<style::TextOptional>());
|
||||
std::vector<style::TextVariableAnchorType> variableTextAnchors = bucket.layout.get<style::TextVariableAnchor>();
|
||||
const bool rotateWithMap = bucket.layout.get<style::TextRotationAlignment>() == style::AlignmentType::Map;
|
||||
const bool pitchWithMap = bucket.layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map;
|
||||
|
||||
for (auto& symbolInstance : bucket.symbolInstances) {
|
||||
for (SymbolInstance& symbolInstance : bucket.symbolInstances) {
|
||||
|
||||
if (seenCrossTileIDs.count(symbolInstance.crossTileID) == 0) {
|
||||
if (holdingForFade) {
|
||||
@@ -175,30 +199,111 @@ void Placement::placeLayerBucket(
|
||||
bool placeText = false;
|
||||
bool placeIcon = false;
|
||||
bool offscreen = true;
|
||||
|
||||
if (symbolInstance.placedRightTextIndex) {
|
||||
PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*symbolInstance.placedRightTextIndex);
|
||||
optional<size_t> horizontalTextIndex = symbolInstance.getDefaultHorizontalPlacedTextIndex();
|
||||
if (horizontalTextIndex) {
|
||||
CollisionFeature& textCollisionFeature = symbolInstance.textCollisionFeature;
|
||||
PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*horizontalTextIndex);
|
||||
const float fontSize = evaluateSizeForFeature(partiallyEvaluatedTextSize, placedSymbol);
|
||||
if (variableTextAnchors.empty()) {
|
||||
auto placed = collisionIndex.placeFeature(textCollisionFeature, {},
|
||||
posMatrix, textLabelPlaneMatrix, textPixelRatio,
|
||||
placedSymbol, scale, fontSize,
|
||||
bucket.layout.get<style::TextAllowOverlap>(),
|
||||
pitchWithMap,
|
||||
showCollisionBoxes, avoidEdges, collisionGroup.second);
|
||||
placeText = placed.first;
|
||||
offscreen &= placed.second;
|
||||
} else if (!textCollisionFeature.alongLine && !textCollisionFeature.boxes.empty()) {
|
||||
const CollisionBox& textBox = symbolInstance.textCollisionFeature.boxes[0];
|
||||
const float width = textBox.x2 - textBox.x1;
|
||||
const float height = textBox.y2 - textBox.y1;
|
||||
const float textBoxScale = symbolInstance.textBoxScale;
|
||||
|
||||
auto placed = collisionIndex.placeFeature(symbolInstance.textCollisionFeature,
|
||||
posMatrix, textLabelPlaneMatrix, textPixelRatio,
|
||||
placedSymbol, scale, fontSize,
|
||||
bucket.layout.get<style::TextAllowOverlap>(),
|
||||
bucket.layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map,
|
||||
showCollisionBoxes, avoidEdges, collisionGroup.second);
|
||||
placeText = placed.first;
|
||||
offscreen &= placed.second;
|
||||
// If this symbol was in the last placement, shift the previously used
|
||||
// anchor to the front of the anchor list.
|
||||
if (prevPlacement) {
|
||||
auto prevOffset = prevPlacement->variableOffsets.find(symbolInstance.crossTileID);
|
||||
if (prevOffset != prevPlacement->variableOffsets.end() &&
|
||||
variableTextAnchors.front() != prevOffset->second.anchor) {
|
||||
std::vector<style::TextVariableAnchorType> filtered;
|
||||
filtered.reserve(variableTextAnchors.size());
|
||||
filtered.push_back(prevOffset->second.anchor);
|
||||
for (auto anchor : variableTextAnchors) {
|
||||
if (anchor != prevOffset->second.anchor) {
|
||||
filtered.push_back(anchor);
|
||||
}
|
||||
}
|
||||
variableTextAnchors = std::move(filtered);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto anchor : variableTextAnchors) {
|
||||
Point<float> shift = calculateVariableLayoutOffset(anchor, width, height, symbolInstance.radialTextOffset, textBoxScale);
|
||||
if (rotateWithMap) {
|
||||
float angle = pitchWithMap ? state.getBearing() : -state.getBearing();
|
||||
shift = util::rotate(shift, angle);
|
||||
}
|
||||
|
||||
auto placed = collisionIndex.placeFeature(textCollisionFeature, shift,
|
||||
posMatrix, mat4(), textPixelRatio,
|
||||
placedSymbol, scale, fontSize,
|
||||
bucket.layout.get<style::TextAllowOverlap>(),
|
||||
pitchWithMap,
|
||||
showCollisionBoxes, avoidEdges, collisionGroup.second);
|
||||
|
||||
if (placed.first) {
|
||||
assert(symbolInstance.crossTileID != 0u);
|
||||
optional<style::TextVariableAnchorType> prevAnchor;
|
||||
|
||||
// If this label was placed in the previous placement, record the anchor position
|
||||
// to allow us to animate the transition
|
||||
if (prevPlacement) {
|
||||
auto prevOffset = prevPlacement->variableOffsets.find(symbolInstance.crossTileID);
|
||||
auto prevPlacements = prevPlacement->placements.find(symbolInstance.crossTileID);
|
||||
if (prevOffset != prevPlacement->variableOffsets.end() &&
|
||||
prevPlacements != prevPlacement->placements.end() &&
|
||||
prevPlacements->second.text) {
|
||||
prevAnchor = prevOffset->second.anchor;
|
||||
}
|
||||
}
|
||||
|
||||
variableOffsets.insert(std::make_pair(symbolInstance.crossTileID, VariableOffset{
|
||||
symbolInstance.radialTextOffset,
|
||||
width,
|
||||
height,
|
||||
anchor,
|
||||
textBoxScale,
|
||||
prevAnchor
|
||||
}));
|
||||
markUsedJustification(bucket, anchor, symbolInstance);
|
||||
|
||||
placeText = placed.first;
|
||||
offscreen &= placed.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't get placed, we still need to copy our position from the last placement for
|
||||
// fade animations
|
||||
if (prevPlacement && variableOffsets.find(symbolInstance.crossTileID) == variableOffsets.end()) {
|
||||
auto prevOffset = prevPlacement->variableOffsets.find(symbolInstance.crossTileID);
|
||||
if (prevOffset != prevPlacement->variableOffsets.end()) {
|
||||
variableOffsets[symbolInstance.crossTileID] = prevOffset->second;
|
||||
markUsedJustification(bucket, prevOffset->second.anchor, symbolInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (symbolInstance.placedIconIndex) {
|
||||
PlacedSymbol& placedSymbol = bucket.icon.placedSymbols.at(*symbolInstance.placedIconIndex);
|
||||
const float fontSize = evaluateSizeForFeature(partiallyEvaluatedIconSize, placedSymbol);
|
||||
|
||||
auto placed = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature,
|
||||
auto placed = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature, {},
|
||||
posMatrix, iconLabelPlaneMatrix, textPixelRatio,
|
||||
placedSymbol, scale, fontSize,
|
||||
bucket.layout.get<style::IconAllowOverlap>(),
|
||||
bucket.layout.get<style::IconPitchAlignment>() == style::AlignmentType::Map,
|
||||
pitchWithMap,
|
||||
showCollisionBoxes, avoidEdges, collisionGroup.second);
|
||||
placeIcon = placed.first;
|
||||
offscreen &= placed.second;
|
||||
@@ -240,7 +345,8 @@ void Placement::placeLayerBucket(
|
||||
bucket.justReloaded = false;
|
||||
}
|
||||
|
||||
void Placement::commit(const Placement& prevPlacement, TimePoint now) {
|
||||
void Placement::commit(TimePoint now) {
|
||||
assert(prevPlacement);
|
||||
commitTime = now;
|
||||
|
||||
bool placementChanged = false;
|
||||
@@ -248,13 +354,13 @@ void Placement::commit(const Placement& prevPlacement, TimePoint now) {
|
||||
float increment = mapMode == MapMode::Continuous &&
|
||||
transitionOptions.enablePlacementTransitions &&
|
||||
transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION) > Milliseconds(0) ?
|
||||
std::chrono::duration<float>(commitTime - prevPlacement.commitTime) / transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION) :
|
||||
std::chrono::duration<float>(commitTime - prevPlacement->commitTime) / transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION) :
|
||||
1.0;
|
||||
|
||||
// add the opacities from the current placement, and copy their current values from the previous placement
|
||||
for (auto& jointPlacement : placements) {
|
||||
auto prevOpacity = prevPlacement.opacities.find(jointPlacement.first);
|
||||
if (prevOpacity != prevPlacement.opacities.end()) {
|
||||
auto prevOpacity = prevPlacement->opacities.find(jointPlacement.first);
|
||||
if (prevOpacity != prevPlacement->opacities.end()) {
|
||||
opacities.emplace(jointPlacement.first, JointOpacityState(prevOpacity->second, increment, jointPlacement.second.text, jointPlacement.second.icon));
|
||||
placementChanged = placementChanged ||
|
||||
jointPlacement.second.icon != prevOpacity->second.icon.placed ||
|
||||
@@ -266,7 +372,7 @@ void Placement::commit(const Placement& prevPlacement, TimePoint now) {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
for (auto& prevOpacity : prevPlacement->opacities) {
|
||||
if (opacities.find(prevOpacity.first) == opacities.end()) {
|
||||
JointOpacityState jointOpacity(prevOpacity.second, increment, false, false);
|
||||
if (!jointOpacity.isHidden()) {
|
||||
@@ -276,7 +382,16 @@ void Placement::commit(const Placement& prevPlacement, TimePoint now) {
|
||||
}
|
||||
}
|
||||
|
||||
fadeStartTime = placementChanged ? commitTime : prevPlacement.fadeStartTime;
|
||||
for (auto& prevOffset : prevPlacement->variableOffsets) {
|
||||
const uint32_t crossTileID = prevOffset.first;
|
||||
auto foundOffset = variableOffsets.find(crossTileID);
|
||||
auto foundOpacity = opacities.find(crossTileID);
|
||||
if (foundOffset == variableOffsets.end() && foundOpacity != opacities.end() && !foundOpacity->second.isHidden()) {
|
||||
variableOffsets[prevOffset.first] = prevOffset.second;
|
||||
}
|
||||
}
|
||||
|
||||
fadeStartTime = placementChanged ? commitTime : prevPlacement->fadeStartTime;
|
||||
}
|
||||
|
||||
void Placement::updateLayerOpacities(const RenderLayerSymbolInterface& symbolInterface) {
|
||||
@@ -310,7 +425,10 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>&
|
||||
|
||||
const bool textAllowOverlap = bucket.layout.get<style::TextAllowOverlap>();
|
||||
const bool iconAllowOverlap = bucket.layout.get<style::IconAllowOverlap>();
|
||||
|
||||
const bool variablePlacement = !bucket.layout.get<style::TextVariableAnchor>().empty();
|
||||
const bool rotateWithMap = bucket.layout.get<style::TextRotationAlignment>() == style::AlignmentType::Map;
|
||||
const bool pitchWithMap = bucket.layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map;
|
||||
|
||||
// If allow-overlap is true, we can show symbols before placement runs on them
|
||||
// But we have to wait for placement if we potentially depend on a paired icon/text
|
||||
// with allow-overlap: false.
|
||||
@@ -339,19 +457,38 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>&
|
||||
|
||||
if (symbolInstance.hasText) {
|
||||
auto opacityVertex = SymbolSDFTextProgram::opacityVertex(opacityState.text.placed, opacityState.text.opacity);
|
||||
for (size_t i = 0; i < symbolInstance.rightJustifiedGlyphQuads.size() * 4; i++) {
|
||||
bucket.text.opacityVertices.emplace_back(opacityVertex);
|
||||
}
|
||||
for (size_t i = 0; i < symbolInstance.verticalGlyphQuads.size() * 4; i++) {
|
||||
bucket.text.opacityVertices.emplace_back(opacityVertex);
|
||||
}
|
||||
if (symbolInstance.placedRightTextIndex) {
|
||||
for (size_t i = 0; i < symbolInstance.rightJustifiedGlyphQuads.size() * 4; i++) {
|
||||
bucket.text.opacityVertices.emplace_back(opacityVertex);
|
||||
}
|
||||
PlacedSymbol& placed = bucket.text.placedSymbols[*symbolInstance.placedRightTextIndex];
|
||||
placed.hidden = opacityState.isHidden();
|
||||
}
|
||||
if (symbolInstance.placedCenterTextIndex) {
|
||||
for (size_t i = 0; i < symbolInstance.centerJustifiedGlyphQuads.size() * 4; i++) {
|
||||
bucket.text.opacityVertices.emplace_back(opacityVertex);
|
||||
}
|
||||
PlacedSymbol& placed = bucket.text.placedSymbols[*symbolInstance.placedCenterTextIndex];
|
||||
placed.hidden = opacityState.isHidden();
|
||||
}
|
||||
if (symbolInstance.placedLeftTextIndex) {
|
||||
for (size_t i = 0; i < symbolInstance.leftJustifiedGlyphQuads.size() * 4; i++) {
|
||||
bucket.text.opacityVertices.emplace_back(opacityVertex);
|
||||
}
|
||||
PlacedSymbol& placed = bucket.text.placedSymbols[*symbolInstance.placedLeftTextIndex];
|
||||
placed.hidden = opacityState.isHidden();
|
||||
}
|
||||
if (symbolInstance.placedVerticalTextIndex) {
|
||||
for (size_t i = 0; i < symbolInstance.verticalGlyphQuads.size() * 4; i++) {
|
||||
bucket.text.opacityVertices.emplace_back(opacityVertex);
|
||||
}
|
||||
bucket.text.placedSymbols[*symbolInstance.placedVerticalTextIndex].hidden = opacityState.isHidden();
|
||||
}
|
||||
|
||||
auto prevOffset = variableOffsets.find(symbolInstance.crossTileID);
|
||||
if (prevOffset != variableOffsets.end()) {
|
||||
markUsedJustification(bucket, prevOffset->second.anchor, symbolInstance);
|
||||
}
|
||||
}
|
||||
if (symbolInstance.hasIcon) {
|
||||
auto opacityVertex = SymbolIconProgram::opacityVertex(opacityState.icon.placed, opacityState.icon.opacity);
|
||||
@@ -370,7 +507,42 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>&
|
||||
if (feature.alongLine) {
|
||||
return;
|
||||
}
|
||||
auto dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, false);
|
||||
auto dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, false, {});
|
||||
for (size_t i = 0; i < feature.boxes.size() * 4; i++) {
|
||||
bucket.collisionBox.dynamicVertices.emplace_back(dynamicVertex);
|
||||
}
|
||||
};
|
||||
|
||||
auto updateCollisionTextBox = [this, &bucket, &symbolInstance, variablePlacement, rotateWithMap, pitchWithMap](const auto& feature, const bool placed) {
|
||||
if (feature.alongLine) {
|
||||
return;
|
||||
}
|
||||
Point<float> 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.radialOffset,
|
||||
variableOffset.textBoxScale);
|
||||
if (rotateWithMap) {
|
||||
shift = util::rotate(shift, pitchWithMap ? state.getBearing() : -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 dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, !used, shift);
|
||||
for (size_t i = 0; i < feature.boxes.size() * 4; i++) {
|
||||
bucket.collisionBox.dynamicVertices.emplace_back(dynamicVertex);
|
||||
}
|
||||
@@ -381,7 +553,7 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>&
|
||||
return;
|
||||
}
|
||||
for (const CollisionBox& box : feature.boxes) {
|
||||
auto dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, !box.used);
|
||||
auto dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, !box.used, {});
|
||||
bucket.collisionCircle.dynamicVertices.emplace_back(dynamicVertex);
|
||||
bucket.collisionCircle.dynamicVertices.emplace_back(dynamicVertex);
|
||||
bucket.collisionCircle.dynamicVertices.emplace_back(dynamicVertex);
|
||||
@@ -390,7 +562,7 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>&
|
||||
};
|
||||
|
||||
if (bucket.hasCollisionBoxData()) {
|
||||
updateCollisionBox(symbolInstance.textCollisionFeature, opacityState.text.placed);
|
||||
updateCollisionTextBox(symbolInstance.textCollisionFeature, opacityState.text.placed);
|
||||
updateCollisionBox(symbolInstance.iconCollisionFeature, opacityState.icon.placed);
|
||||
}
|
||||
if (bucket.hasCollisionCircleData()) {
|
||||
@@ -407,6 +579,31 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>&
|
||||
}
|
||||
}
|
||||
|
||||
void Placement::markUsedJustification(SymbolBucket& bucket, style::TextVariableAnchorType placedAnchor, SymbolInstance& symbolInstance) {
|
||||
std::map<style::TextJustifyType, optional<size_t>> justificationToIndex {
|
||||
{style::TextJustifyType::Right, symbolInstance.placedRightTextIndex},
|
||||
{style::TextJustifyType::Center, symbolInstance.placedCenterTextIndex},
|
||||
{style::TextJustifyType::Left, symbolInstance.placedLeftTextIndex},
|
||||
};
|
||||
style::TextJustifyType justify = getAnchorJustification(placedAnchor);
|
||||
assert(justify == style::TextJustifyType::Right || justify == style::TextJustifyType::Center || justify == style::TextJustifyType::Left);
|
||||
const optional<size_t> autoIndex = justificationToIndex[justify];
|
||||
|
||||
for (auto& pair : justificationToIndex) {
|
||||
const optional<size_t> index = pair.second;
|
||||
if (index) {
|
||||
assert(bucket.text.placedSymbols.size() > *index);
|
||||
if (autoIndex && *index != *autoIndex) {
|
||||
// There are multiple justifications and this one isn't it: shift offscreen
|
||||
bucket.text.placedSymbols.at(*index).crossTileID = 0u;
|
||||
} else {
|
||||
// Either this is the chosen justification or the justification is hardwired: use this one
|
||||
bucket.text.placedSymbols.at(*index).crossTileID = symbolInstance.crossTileID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float Placement::symbolFadeChange(TimePoint now) const {
|
||||
if (mapMode == MapMode::Continuous && transitionOptions.enablePlacementTransitions &&
|
||||
transitionOptions.duration.value_or(util::DEFAULT_TRANSITION_DURATION) > Milliseconds(0)) {
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace mbgl {
|
||||
|
||||
class RenderLayerSymbolInterface;
|
||||
class SymbolBucket;
|
||||
class SymbolInstance;
|
||||
|
||||
class OpacityState {
|
||||
public:
|
||||
@@ -90,9 +91,9 @@ private:
|
||||
|
||||
class Placement {
|
||||
public:
|
||||
Placement(const TransformState&, MapMode, style::TransitionOptions, const bool crossSourceCollisions);
|
||||
Placement(const TransformState&, MapMode, style::TransitionOptions, const bool crossSourceCollisions, std::unique_ptr<Placement> prevPlacementOrNull = nullptr);
|
||||
void placeLayer(const RenderLayerSymbolInterface&, const mat4&, bool showCollisionBoxes);
|
||||
void commit(const Placement& prevPlacement, TimePoint);
|
||||
void commit(TimePoint);
|
||||
void updateLayerOpacities(const RenderLayerSymbolInterface&);
|
||||
float symbolFadeChange(TimePoint now) const;
|
||||
bool hasTransitions(TimePoint now) const;
|
||||
@@ -104,8 +105,10 @@ public:
|
||||
void setStale();
|
||||
|
||||
const RetainedQueryData& getQueryData(uint32_t bucketInstanceId) const;
|
||||
private:
|
||||
using VariableOffsets = std::reference_wrapper<const std::unordered_map<uint32_t, VariableOffset>>;
|
||||
VariableOffsets getVariableOffsets() const { return std::cref(variableOffsets); }
|
||||
|
||||
private:
|
||||
void placeLayerBucket(
|
||||
SymbolBucket&,
|
||||
const mat4& posMatrix,
|
||||
@@ -119,6 +122,7 @@ private:
|
||||
const CollisionGroups::CollisionGroup& collisionGroup);
|
||||
|
||||
void updateBucketOpacities(SymbolBucket&, std::set<uint32_t>&);
|
||||
void markUsedJustification(SymbolBucket&, style::TextVariableAnchorType, SymbolInstance&);
|
||||
|
||||
CollisionIndex collisionIndex;
|
||||
|
||||
@@ -131,17 +135,13 @@ private:
|
||||
|
||||
std::unordered_map<uint32_t, JointPlacement> placements;
|
||||
std::unordered_map<uint32_t, JointOpacityState> opacities;
|
||||
std::unordered_map<uint32_t, VariableOffset> variableOffsets;
|
||||
|
||||
bool stale = false;
|
||||
|
||||
std::unordered_map<uint32_t, RetainedQueryData> retainedQueryData;
|
||||
CollisionGroups collisionGroups;
|
||||
std::unique_ptr<Placement> prevPlacement;
|
||||
};
|
||||
|
||||
Point<float> calculateVariableLayoutOffset(style::SymbolAnchorType anchor,
|
||||
float width,
|
||||
float height,
|
||||
float radialOffset,
|
||||
float textBoxScale);
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
Reference in New Issue
Block a user