[core] Fix combination of icon-text-fit with text-variable-anchors and text-writing-mode

This commit is contained in:
Alexander Shalamov
2019-08-08 13:56:22 +03:00
parent 073a243862
commit 9ba700fd47
15 changed files with 344 additions and 184 deletions

View File

@@ -157,6 +157,7 @@ void Placement::placeBucket(
std::vector<style::TextVariableAnchorType> variableTextAnchors = layout.get<style::TextVariableAnchor>();
const bool rotateWithMap = layout.get<style::TextRotationAlignment>() == style::AlignmentType::Map;
const bool pitchWithMap = layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map;
const bool hasIconTextFit = layout.get<style::IconTextFit>() != style::IconTextFitType::None;
const bool hasCollisionCircleData = bucket.hasCollisionCircleData();
const bool zOrderByViewportY = layout.get<style::SymbolZOrder>() == style::SymbolZOrderType::ViewportY;
@@ -179,7 +180,9 @@ void Placement::placeBucket(
bool placeIcon = false;
bool offscreen = true;
std::pair<bool, bool> placed{ false, false };
std::pair<bool, bool> placedVertical{ false, false };
std::pair<bool, bool> placedVerticalText{ false, false };
std::pair<bool, bool> placedVerticalIcon{ false, false };
Point<float> shift{0.0f, 0.0f};
optional<size_t> horizontalTextIndex = symbolInstance.getDefaultHorizontalPlacedTextIndex();
if (horizontalTextIndex) {
const PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*horizontalTextIndex);
@@ -200,7 +203,7 @@ void Placement::placeBucket(
assert(!bucket.placementModes.empty());
for (auto& placementMode : bucket.placementModes) {
if (placementMode == style::TextWritingModeType::Vertical) {
placedVertical = placed = placeVerticalFn();
placedVerticalText = placed = placeVerticalFn();
} else {
placed = placeHorizontalFn();
}
@@ -280,7 +283,7 @@ void Placement::placeBucket(
for (size_t i = 0u; i < placementAttempts; ++i) {
auto anchor = variableTextAnchors[i % anchorsSize];
const bool allowOverlap = (i >= anchorsSize);
Point<float> shift = calculateVariableLayoutOffset(anchor, width, height, symbolInstance.radialTextOffset, textBoxScale);
shift = calculateVariableLayoutOffset(anchor, width, height, symbolInstance.radialTextOffset, textBoxScale);
if (rotateWithMap) {
float angle = pitchWithMap ? state.getBearing() : -state.getBearing();
shift = util::rotate(shift, angle);
@@ -359,15 +362,28 @@ void Placement::placeBucket(
}
if (symbolInstance.placedIconIndex) {
if (!hasIconTextFit || !placeText || variableTextAnchors.empty()) {
shift = {0.0f, 0.0f};
}
const PlacedSymbol& placedSymbol = bucket.icon.placedSymbols.at(*symbolInstance.placedIconIndex);
const float fontSize = evaluateSizeForFeature(partiallyEvaluatedIconSize, placedSymbol);
const auto& placeIconFeature = [&] (const CollisionFeature& collisionFeature) {
return collisionIndex.placeFeature(collisionFeature, shift,
posMatrix, iconLabelPlaneMatrix, pixelRatio,
placedSymbol, scale, fontSize,
layout.get<style::IconAllowOverlap>(),
pitchWithMap,
params.showCollisionBoxes, avoidEdges,
collisionGroup.second, iconBoxes);
};
auto placedIcon = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature, {},
posMatrix, iconLabelPlaneMatrix, pixelRatio,
placedSymbol, scale, fontSize,
layout.get<style::IconAllowOverlap>(),
pitchWithMap,
params.showCollisionBoxes, avoidEdges, collisionGroup.second, iconBoxes);
std::pair<bool, bool> placedIcon = {false, false};
if (placedVerticalText.first && symbolInstance.verticalIconCollisionFeature) {
placedIcon = placedVerticalIcon = placeIconFeature(*symbolInstance.verticalIconCollisionFeature);
} else {
placedIcon = placeIconFeature(symbolInstance.iconCollisionFeature);
}
placeIcon = placedIcon.first;
offscreen &= placedIcon.second;
}
@@ -385,7 +401,7 @@ void Placement::placeBucket(
}
if (placeText) {
if (placedVertical.first && symbolInstance.verticalTextCollisionFeature) {
if (placedVerticalText.first && symbolInstance.verticalTextCollisionFeature) {
collisionIndex.insertFeature(*symbolInstance.verticalTextCollisionFeature, textBoxes, layout.get<style::TextIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first);
} else {
collisionIndex.insertFeature(symbolInstance.textCollisionFeature, textBoxes, layout.get<style::TextIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first);
@@ -393,7 +409,11 @@ void Placement::placeBucket(
}
if (placeIcon) {
collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, iconBoxes, layout.get<style::IconIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first);
if (placedVerticalIcon.first && symbolInstance.verticalIconCollisionFeature) {
collisionIndex.insertFeature(*symbolInstance.verticalIconCollisionFeature, iconBoxes, layout.get<style::IconIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first);
} else {
collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, iconBoxes, layout.get<style::IconIgnorePlacement>(), bucket.bucketInstanceId, collisionGroup.first);
}
}
if (hasCollisionCircleData) {
@@ -518,6 +538,8 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor
using namespace style;
const auto& layout = *bucket.layout;
const bool alongLine = layout.get<SymbolPlacement>() != SymbolPlacementType::Point;
const bool hasVariableAnchors = !layout.get<TextVariableAnchor>().empty() && bucket.hasTextData();
const bool updateTextFitIcon = layout.get<IconTextFit>() != IconTextFitType::None && (bucket.allowVerticalPlacement || hasVariableAnchors) && bucket.hasIconData();
bool result = false;
if (alongLine) {
@@ -538,7 +560,7 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor
tile, *bucket.textSizeBinder, state);
result = true;
}
} else if (!layout.get<TextVariableAnchor>().empty() && bucket.hasTextData()) {
} else if (hasVariableAnchors) {
bucket.text.dynamicVertices.clear();
bucket.hasVariablePlacement = false;
@@ -548,8 +570,10 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor
const bool pitchWithMap = layout.get<TextPitchAlignment>() == AlignmentType::Map;
const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1.0, state.getZoom());
const auto labelPlaneMatrix = getLabelPlaneMatrix(tile.matrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits);
std::unordered_map<std::size_t, std::pair<std::size_t, Point<float>>> placedTextShifts;
for (const PlacedSymbol& symbol : bucket.text.placedSymbols) {
for (std::size_t i = 0; i < bucket.text.placedSymbols.size(); ++i) {
const PlacedSymbol& symbol = bucket.text.placedSymbols[i];
optional<VariableOffset> variableOffset;
const bool skipOrientation = bucket.allowVerticalPlacement && !symbol.placedOrientation;
if (!symbol.hidden && symbol.crossTileID != 0u && !skipOrientation) {
@@ -598,24 +622,57 @@ bool Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const Transfor
projectedAnchor.first.y + shift.y);
}
for (std::size_t i = 0; i < symbol.glyphOffsets.size(); ++i) {
if (updateTextFitIcon && symbol.placedIconIndex) {
placedTextShifts.emplace(*symbol.placedIconIndex,
std::pair<std::size_t, Point<float>>{i, shiftedAnchor});
}
for (std::size_t j = 0; j < symbol.glyphOffsets.size(); ++j) {
addDynamicAttributes(shiftedAnchor, symbol.angle, bucket.text.dynamicVertices);
}
}
}
if (updateTextFitIcon && bucket.hasVariablePlacement) {
bucket.icon.dynamicVertices.clear();
for (std::size_t i = 0; i < bucket.icon.placedSymbols.size(); ++i) {
const PlacedSymbol& placedIcon = bucket.icon.placedSymbols[i];
if (placedIcon.hidden || (!placedIcon.placedOrientation && bucket.allowVerticalPlacement)) {
hideGlyphs(placedIcon.glyphOffsets.size(), bucket.icon.dynamicVertices);
} else {
const auto& pair = placedTextShifts.find(i);
if (pair == placedTextShifts.end()) {
hideGlyphs(placedIcon.glyphOffsets.size(), bucket.icon.dynamicVertices);
} else {
for (std::size_t j = 0; j < placedIcon.glyphOffsets.size(); ++j) {
addDynamicAttributes(pair->second.second, placedIcon.angle, bucket.icon.dynamicVertices);
}
}
}
}
}
result = true;
} else if (bucket.allowVerticalPlacement && bucket.hasTextData()) {
bucket.text.dynamicVertices.clear();
for (const PlacedSymbol& symbol : bucket.text.placedSymbols) {
if (symbol.hidden || !symbol.placedOrientation) {
hideGlyphs(symbol.glyphOffsets.size(), bucket.text.dynamicVertices);
} else {
for (std::size_t i = 0; i < symbol.glyphOffsets.size(); ++i) {
addDynamicAttributes(symbol.anchorPoint, symbol.angle, bucket.text.dynamicVertices);
const auto updateDynamicVertices = [](SymbolBucket::Buffer& buffer) {
buffer.dynamicVertices.clear();
for (const PlacedSymbol& symbol : buffer.placedSymbols) {
if (symbol.hidden || !symbol.placedOrientation) {
hideGlyphs(symbol.glyphOffsets.size(), buffer.dynamicVertices);
} else {
for (std::size_t j = 0; j < symbol.glyphOffsets.size(); ++j) {
addDynamicAttributes(symbol.anchorPoint, symbol.angle, buffer.dynamicVertices);
}
}
}
};
updateDynamicVertices(bucket.text);
// When text box is rotated, icon-text-fit icon must be rotated as well.
if (updateTextFitIcon) {
updateDynamicVertices(bucket.icon);
}
result = true;
}
@@ -635,6 +692,7 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState
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;
const bool hasIconTextFit = bucket.layout->get<style::IconTextFit>() != style::IconTextFitType::None;
// 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
@@ -703,25 +761,30 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState
}
if (symbolInstance.hasIcon) {
const auto& opacityVertex = SymbolIconProgram::opacityVertex(opacityState.icon.placed, opacityState.icon.opacity);
bucket.icon.opacityVertices.extend(4, opacityVertex);
if (symbolInstance.placedIconIndex) {
bucket.icon.opacityVertices.extend(4, opacityVertex);
bucket.icon.placedSymbols[*symbolInstance.placedIconIndex].hidden = opacityState.isHidden();
}
if (symbolInstance.placedVerticalIconIndex) {
bucket.icon.opacityVertices.extend(4, opacityVertex);
bucket.icon.placedSymbols[*symbolInstance.placedVerticalIconIndex].hidden = opacityState.isHidden();
}
}
auto updateCollisionBox = [&](const auto& feature, const bool placed) {
auto updateCollisionBox = [&](const auto& feature, const bool placed, const Point<float>& shift) {
if (feature.alongLine) {
return;
}
const auto& dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, false, {});
const auto& dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, false, shift);
bucket.collisionBox->dynamicVertices.extend(feature.boxes.size() * 4, dynamicVertex);
};
auto updateCollisionTextBox = [this, &bucket, &symbolInstance, &state, variablePlacement, rotateWithMap, pitchWithMap](const auto& feature, const bool placed) {
Point<float> shift{0.0f, 0.0f};
if (feature.alongLine) {
return;
return shift;
}
Point<float> shift;
bool used = true;
if (variablePlacement) {
auto foundOffset = variableOffsets.find(symbolInstance.crossTileID);
@@ -748,6 +811,7 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState
}
const auto& dynamicVertex = CollisionBoxProgram::dynamicVertex(placed, !used, shift);
bucket.collisionBox->dynamicVertices.extend(feature.boxes.size() * 4, dynamicVertex);
return shift;
};
auto updateCollisionCircles = [&](const auto& feature, const bool placed) {
@@ -768,12 +832,17 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, const TransformState
};
if (bucket.hasCollisionBoxData()) {
// TODO: update collision box opacity based on selected text variant (horizontal | vertical).
updateCollisionTextBox(symbolInstance.textCollisionFeature, opacityState.text.placed);
if (bucket.allowVerticalPlacement && symbolInstance.verticalTextCollisionFeature) {
updateCollisionTextBox(*symbolInstance.verticalTextCollisionFeature, opacityState.text.placed);
const auto& textShift = updateCollisionTextBox(symbolInstance.textCollisionFeature, opacityState.text.placed);
if (bucket.allowVerticalPlacement) {
Point<float> verticalTextShift{0.0f, 0.0f};
if (symbolInstance.verticalTextCollisionFeature) {
verticalTextShift = updateCollisionTextBox(*symbolInstance.verticalTextCollisionFeature, opacityState.text.placed);
}
if (symbolInstance.verticalIconCollisionFeature) {
updateCollisionBox(*symbolInstance.verticalIconCollisionFeature, opacityState.text.placed, hasIconTextFit ? verticalTextShift : Point<float>{0.0f, 0.0f});
}
}
updateCollisionBox(symbolInstance.iconCollisionFeature, opacityState.icon.placed);
updateCollisionBox(symbolInstance.iconCollisionFeature, opacityState.icon.placed, hasIconTextFit ? textShift : Point<float>{0.0f, 0.0f});
}
if (bucket.hasCollisionCircleData()) {
updateCollisionCircles(symbolInstance.textCollisionFeature, opacityState.text.placed);
@@ -850,6 +919,14 @@ void Placement::markUsedOrientation(SymbolBucket& bucket, style::TextWritingMode
if (symbolInstance.placedVerticalTextIndex) {
bucket.text.placedSymbols.at(*symbolInstance.placedVerticalTextIndex).placedOrientation = vertical;
}
if (symbolInstance.placedIconIndex) {
bucket.icon.placedSymbols.at(*symbolInstance.placedIconIndex).placedOrientation = horizontal;
}
if (symbolInstance.placedVerticalIconIndex) {
bucket.icon.placedSymbols.at(*symbolInstance.placedVerticalIconIndex).placedOrientation = vertical;
}
}
float Placement::symbolFadeChange(TimePoint now) const {