[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

@@ -21,15 +21,18 @@ const Shaping& getAnyShaping(const ShapedTextOrientations& shapedTextOrientation
SymbolInstanceSharedData::SymbolInstanceSharedData(GeometryCoordinates line_,
const ShapedTextOrientations& shapedTextOrientations,
const optional<PositionedIcon>& shapedIcon,
const optional<PositionedIcon>& verticallyShapedIcon,
const style::SymbolLayoutProperties::Evaluated& layout,
const float layoutTextSize,
const style::SymbolPlacementType textPlacement,
const std::array<float, 2>& textOffset,
const GlyphPositions& positions,
bool allowVerticalPlacement) : line(std::move(line_)) {
// Create the quads used for rendering the icon and glyphs.
if (shapedIcon) {
iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.horizontal);
iconQuad = getIconQuad(*shapedIcon, getAnyShaping(shapedTextOrientations).writingMode);
if (verticallyShapedIcon) {
verticalIconQuad = getIconQuad(*verticallyShapedIcon, shapedTextOrientations.vertical.writingMode);
}
}
bool singleLineInitialized = false;
@@ -69,6 +72,7 @@ SymbolInstance::SymbolInstance(Anchor& anchor_,
std::shared_ptr<SymbolInstanceSharedData> sharedData_,
const ShapedTextOrientations& shapedTextOrientations,
const optional<PositionedIcon>& shapedIcon,
const optional<PositionedIcon>& verticallyShapedIcon,
const float textBoxScale_,
const float textPadding,
const SymbolPlacementType textPlacement,
@@ -107,6 +111,14 @@ SymbolInstance::SymbolInstance(Anchor& anchor_,
if (allowVerticalPlacement && shapedTextOrientations.vertical) {
const float verticalPointLabelAngle = 90.0f;
verticalTextCollisionFeature = CollisionFeature(line(), anchor, shapedTextOrientations.vertical, textBoxScale_, textPadding, textPlacement, indexedFeature, overscaling, textRotation + verticalPointLabelAngle);
if (verticallyShapedIcon) {
verticalIconCollisionFeature = CollisionFeature(sharedData->line,
anchor,
verticallyShapedIcon,
iconBoxScale, iconPadding,
indexedFeature,
iconRotation + verticalPointLabelAngle);
}
}
rightJustifiedGlyphQuadsSize = sharedData->rightJustifiedGlyphQuads.size();
@@ -153,6 +165,11 @@ const optional<SymbolQuad>& SymbolInstance::iconQuad() const {
return sharedData->iconQuad;
}
const optional<SymbolQuad>& SymbolInstance::verticalIconQuad() const {
assert(sharedData);
return sharedData->verticalIconQuad;
}
void SymbolInstance::releaseSharedData() {
sharedData.reset();
}

View File

@@ -25,8 +25,8 @@ struct SymbolInstanceSharedData {
SymbolInstanceSharedData(GeometryCoordinates line,
const ShapedTextOrientations& shapedTextOrientations,
const optional<PositionedIcon>& shapedIcon,
const optional<PositionedIcon>& verticallyShapedIcon,
const style::SymbolLayoutProperties::Evaluated& layout,
const float layoutTextSize,
const style::SymbolPlacementType textPlacement,
const std::array<float, 2>& textOffset,
const GlyphPositions& positions,
@@ -39,6 +39,7 @@ struct SymbolInstanceSharedData {
SymbolQuads leftJustifiedGlyphQuads;
SymbolQuads verticalGlyphQuads;
optional<SymbolQuad> iconQuad;
optional<SymbolQuad> verticalIconQuad;
};
class SymbolInstance {
@@ -47,6 +48,7 @@ public:
std::shared_ptr<SymbolInstanceSharedData> sharedData,
const ShapedTextOrientations& shapedTextOrientations,
const optional<PositionedIcon>& shapedIcon,
const optional<PositionedIcon>& verticallyShapedIcon,
const float textBoxScale,
const float textPadding,
const style::SymbolPlacementType textPlacement,
@@ -71,6 +73,7 @@ public:
const SymbolQuads& centerJustifiedGlyphQuads() const;
const SymbolQuads& verticalGlyphQuads() const;
const optional<SymbolQuad>& iconQuad() const;
const optional<SymbolQuad>& verticalIconQuad() const;
void releaseSharedData();
private:
@@ -89,6 +92,7 @@ public:
CollisionFeature textCollisionFeature;
CollisionFeature iconCollisionFeature;
optional<CollisionFeature> verticalTextCollisionFeature = nullopt;
optional<CollisionFeature> verticalIconCollisionFeature = nullopt;
WritingModeType writingModes;
std::size_t layoutFeatureIndex; // Index into the set of features included at layout time
std::size_t dataFeatureIndex; // Index into the underlying tile data feature set
@@ -101,6 +105,7 @@ public:
optional<size_t> placedLeftTextIndex;
optional<size_t> placedVerticalTextIndex;
optional<size_t> placedIconIndex;
optional<size_t> placedVerticalIconIndex;
float textBoxScale;
float radialTextOffset;
bool singleLine;

View File

@@ -419,7 +419,7 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
// if either shapedText or icon position is present, add the feature
if (getDefaultHorizontalShaping(shapedTextOrientations) || shapedIcon) {
addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositions, textOffset);
addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, std::move(shapedIcon), glyphPositions, textOffset);
}
feature.geometry.clear();
@@ -431,7 +431,7 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
const SymbolFeature& feature,
const ShapedTextOrientations& shapedTextOrientations,
const optional<PositionedIcon>& shapedIcon,
optional<PositionedIcon> shapedIcon,
const GlyphPositions& glyphPositions,
Point<float> offset) {
const float minScale = 0.5f;
@@ -467,6 +467,22 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
const float textRepeatDistance = symbolSpacing / 2;
const auto evaluatedLayoutProperties = layout->evaluate(zoom, feature);
IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketLeaderID, symbolInstances.size());
const bool hasIconTextFit = evaluatedLayoutProperties.get<style::IconTextFit>() != IconTextFitType::None;
// Adjust shaped icon size when icon-text-fit is used.
optional<PositionedIcon> verticallyShapedIcon;
if (shapedIcon && hasIconTextFit) {
// Create vertically shaped icon for vertical writing mode if needed.
if (allowVerticalPlacement && shapedTextOrientations.vertical) {
verticallyShapedIcon = shapedIcon;
verticallyShapedIcon->fitIconToText(evaluatedLayoutProperties,
shapedTextOrientations.vertical,
layoutTextSize);
}
shapedIcon->fitIconToText(evaluatedLayoutProperties,
getDefaultHorizontalShaping(shapedTextOrientations),
layoutTextSize);
}
auto addSymbolInstance = [&] (Anchor& anchor, std::shared_ptr<SymbolInstanceSharedData> sharedData) {
assert(sharedData);
@@ -478,7 +494,8 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
// In tiled rendering mode, add all symbols in the buffers so that we can:
// (1) render symbols that overlap into this tile
// (2) approximate collision detection effects from neighboring symbols
symbolInstances.emplace_back(anchor, std::move(sharedData), shapedTextOrientations, shapedIcon,
symbolInstances.emplace_back(anchor, std::move(sharedData), shapedTextOrientations,
shapedIcon, verticallyShapedIcon,
textBoxScale, textPadding, textPlacement, textOffset,
iconBoxScale, iconPadding, iconOffset, indexedFeature,
layoutFeatureIndex, feature.index,
@@ -489,7 +506,7 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
const auto createSymbolInstanceSharedData = [&] (GeometryCoordinates line) {
return std::make_shared<SymbolInstanceSharedData>(std::move(line),
shapedTextOrientations, shapedIcon, evaluatedLayoutProperties, layoutTextSize,
shapedTextOrientations, shapedIcon, verticallyShapedIcon, evaluatedLayoutProperties,
textPlacement, textOffset, glyphPositions, allowVerticalPlacement);
};
@@ -617,6 +634,32 @@ void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptr<FeatureIn
// Insert final placement into collision tree and add glyphs/icons to buffers
// Process icon first, so that text symbols would have reference to iconIndex which
// is used when dynamic vertices for icon-text-fit image have to be updated.
if (hasIcon) {
if (symbolInstance.hasIcon) {
const Range<float> sizeData = bucket->iconSizeBinder->getVertexSizeData(feature);
const auto placeIcon = [&] (const SymbolQuad& iconQuad, auto& index, const WritingModeType writingMode) {
bucket->icon.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
symbolInstance.iconOffset, writingMode, symbolInstance.line(), std::vector<float>());
index = bucket->icon.placedSymbols.size() - 1;
PlacedSymbol& iconSymbol = bucket->icon.placedSymbols.back();
iconSymbol.angle = (allowVerticalPlacement && writingMode == WritingModeType::Vertical) ? M_PI_2 : 0;
iconSymbol.vertexStartIndex = addSymbol(bucket->icon, sizeData, iconQuad,
symbolInstance.anchor, iconSymbol, feature.sortKey);
};
placeIcon(*symbolInstance.iconQuad(), symbolInstance.placedIconIndex, WritingModeType::None);
if (symbolInstance.verticalIconQuad()) {
placeIcon(*symbolInstance.verticalIconQuad(), symbolInstance.placedVerticalIconIndex, WritingModeType::Vertical);
}
for (auto& pair : bucket->paintProperties) {
pair.second.iconBinders.populateVertexVectors(feature, bucket->icon.vertices.elements(), {}, {});
}
}
}
if (hasText && feature.formattedText) {
optional<std::size_t> lastAddedSection;
if (singleLine) {
@@ -643,21 +686,6 @@ void SymbolLayout::createBucket(const ImagePositions&, std::unique_ptr<FeatureIn
updatePaintPropertiesForSection(*bucket, feature, *lastAddedSection);
}
if (hasIcon) {
if (symbolInstance.hasIcon) {
const Range<float> sizeData = bucket->iconSizeBinder->getVertexSizeData(feature);
bucket->icon.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
symbolInstance.iconOffset, WritingModeType::None, symbolInstance.line(), std::vector<float>());
symbolInstance.placedIconIndex = bucket->icon.placedSymbols.size() - 1;
PlacedSymbol& iconSymbol = bucket->icon.placedSymbols.back();
iconSymbol.vertexStartIndex = addSymbol(bucket->icon, sizeData, *symbolInstance.iconQuad(),
symbolInstance.anchor, iconSymbol, feature.sortKey);
for (auto& pair : bucket->paintProperties) {
pair.second.iconBinders.populateVertexVectors(feature, bucket->icon.vertices.elements(), {}, {});
}
}
}
symbolInstance.releaseSharedData();
}
@@ -693,9 +721,9 @@ std::size_t SymbolLayout::addSymbolGlyphQuads(SymbolBucket& bucket,
optional<std::size_t> lastAddedSection) {
const Range<float> sizeData = bucket.textSizeBinder->getVertexSizeData(feature);
const bool hasFormatSectionOverrides = bucket.hasFormatSectionOverrides();
const auto& placedIconIndex = writingMode == WritingModeType::Vertical ? symbolInstance.placedVerticalIconIndex : symbolInstance.placedIconIndex;
bucket.text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
symbolInstance.textOffset, writingMode, symbolInstance.line(), CalculateTileDistances(symbolInstance.line(), symbolInstance.anchor));
symbolInstance.textOffset, writingMode, symbolInstance.line(), CalculateTileDistances(symbolInstance.line(), symbolInstance.anchor), placedIconIndex);
placedIndex = bucket.text.placedSymbols.size() - 1;
PlacedSymbol& placedSymbol = bucket.text.placedSymbols.back();
placedSymbol.angle = (allowVerticalPlacement && writingMode == WritingModeType::Vertical) ? M_PI_2 : 0;
@@ -837,6 +865,9 @@ void SymbolLayout::addToDebugBuffers(SymbolBucket& bucket) {
if (symbolInstance.verticalTextCollisionFeature) {
populateCollisionBox(*symbolInstance.verticalTextCollisionFeature);
}
if (symbolInstance.verticalIconCollisionFeature) {
populateCollisionBox(*symbolInstance.verticalIconCollisionFeature);
}
populateCollisionBox(symbolInstance.iconCollisionFeature);
}
}

View File

@@ -51,7 +51,7 @@ private:
void addFeature(const size_t,
const SymbolFeature&,
const ShapedTextOrientations& shapedTextOrientations,
const optional<PositionedIcon>& shapedIcon,
optional<PositionedIcon> shapedIcon,
const GlyphPositions&,
Point<float> textOffset);

View File

@@ -67,7 +67,7 @@ Values makeValues(const bool isText,
const bool rotateInShader = rotateWithMap && !pitchWithMap && !alongLine;
mat4 labelPlaneMatrix;
if (alongLine || (isText && hasVariablePacement)) {
if (alongLine || hasVariablePacement) {
// For labels that follow lines the first part of the projection is handled on the cpu.
// Pass an identity matrix because no transformation needs to be done in the vertex shader.
matrix::identity(labelPlaneMatrix);

View File

@@ -228,6 +228,10 @@ void SymbolBucket::sortFeatures(const float angle) {
if (symbolInstance.placedIconIndex) {
addPlacedSymbol(icon.triangles, icon.placedSymbols[*symbolInstance.placedIconIndex]);
}
if (symbolInstance.placedVerticalIconIndex) {
addPlacedSymbol(icon.triangles, icon.placedSymbols[*symbolInstance.placedVerticalIconIndex]);
}
}
}

View File

@@ -21,9 +21,9 @@ class CrossTileSymbolLayerIndex;
class PlacedSymbol {
public:
PlacedSymbol(Point<float> anchorPoint_, uint16_t segment_, float lowerSize_, float upperSize_,
std::array<float, 2> lineOffset_, WritingModeType writingModes_, GeometryCoordinates line_, std::vector<float> tileDistances_) :
std::array<float, 2> lineOffset_, WritingModeType writingModes_, GeometryCoordinates line_, std::vector<float> tileDistances_, optional<size_t> placedIconIndex_ = nullopt) :
anchorPoint(anchorPoint_), segment(segment_), lowerSize(lowerSize_), upperSize(upperSize_),
lineOffset(lineOffset_), writingModes(writingModes_), line(std::move(line_)), tileDistances(std::move(tileDistances_)), hidden(false), vertexStartIndex(0)
lineOffset(lineOffset_), writingModes(writingModes_), line(std::move(line_)), tileDistances(std::move(tileDistances_)), hidden(false), vertexStartIndex(0), placedIconIndex(std::move(placedIconIndex_))
{
}
Point<float> anchorPoint;
@@ -43,6 +43,9 @@ public:
// placement for orientation variants.
optional<style::TextWritingModeType> placedOrientation;
float angle = 0;
// Reference to placed icon, only applicable for text symbols.
optional<size_t> placedIconIndex;
};
class SymbolBucket final : public Bucket {

View File

@@ -131,11 +131,12 @@ void drawIcon(const DrawFn& draw,
: gfx::TextureFilterType::Nearest };
const Size& iconSize = tile.getIconAtlasTexture().size;
const bool variablePlacedIcon = bucket.hasVariablePlacement && layout.get<IconTextFit>() != IconTextFitType::None;
if (bucket.sdfIcons) {
if (values.hasHalo) {
draw(parameters.programs.getSymbolLayerPrograms().symbolIconSDF,
SymbolSDFIconProgram::layoutUniformValues(false, false, values, iconSize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo),
SymbolSDFIconProgram::layoutUniformValues(false, variablePlacedIcon, values, iconSize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo),
bucket.icon,
iconSegments,
bucket.iconSizeBinder,
@@ -149,7 +150,7 @@ void drawIcon(const DrawFn& draw,
if (values.hasFill) {
draw(parameters.programs.getSymbolLayerPrograms().symbolIconSDF,
SymbolSDFIconProgram::layoutUniformValues(false, false, values, iconSize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill),
SymbolSDFIconProgram::layoutUniformValues(false, variablePlacedIcon, values, iconSize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill),
bucket.icon,
iconSegments,
bucket.iconSizeBinder,
@@ -162,7 +163,7 @@ void drawIcon(const DrawFn& draw,
}
} else {
draw(parameters.programs.getSymbolLayerPrograms().symbolIcon,
SymbolIconProgram::layoutUniformValues(false, false, values, iconSize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange),
SymbolIconProgram::layoutUniformValues(false, variablePlacedIcon, values, iconSize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange),
bucket.icon,
iconSegments,
bucket.iconSizeBinder,

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 {

View File

@@ -14,9 +14,7 @@ namespace mbgl {
using namespace style;
SymbolQuad getIconQuad(const PositionedIcon& shapedIcon,
const SymbolLayoutProperties::Evaluated& layout,
const float layoutTextSize,
const Shaping& shapedText) {
WritingModeType writingMode) {
const ImagePosition& image = shapedIcon.image();
// If you have a 10px icon that isn't perfectly aligned to the pixel grid it will cover 11 actual
@@ -28,43 +26,11 @@ SymbolQuad getIconQuad(const PositionedIcon& shapedIcon,
float left = shapedIcon.left() - border / image.pixelRatio;
float bottom = shapedIcon.bottom() + border / image.pixelRatio;
float right = shapedIcon.right() + border / image.pixelRatio;
Point<float> tl;
Point<float> tr;
Point<float> br;
Point<float> bl;
if (layout.get<IconTextFit>() != IconTextFitType::None && shapedText) {
auto iconWidth = right - left;
auto iconHeight = bottom - top;
auto size = layoutTextSize / 24.0f;
auto textLeft = shapedText.left * size;
auto textRight = shapedText.right * size;
auto textTop = shapedText.top * size;
auto textBottom = shapedText.bottom * size;
auto textWidth = textRight - textLeft;
auto textHeight = textBottom - textTop;
auto padT = layout.get<IconTextFitPadding>()[0];
auto padR = layout.get<IconTextFitPadding>()[1];
auto padB = layout.get<IconTextFitPadding>()[2];
auto padL = layout.get<IconTextFitPadding>()[3];
auto offsetY = layout.get<IconTextFit>() == IconTextFitType::Width ? (textHeight - iconHeight) * 0.5 : 0;
auto offsetX = layout.get<IconTextFit>() == IconTextFitType::Height ? (textWidth - iconWidth) * 0.5 : 0;
auto width = layout.get<IconTextFit>() == IconTextFitType::Width || layout.get<IconTextFit>() == IconTextFitType::Both ? textWidth : iconWidth;
auto height = layout.get<IconTextFit>() == IconTextFitType::Height || layout.get<IconTextFit>() == IconTextFitType::Both ? textHeight : iconHeight;
left = textLeft + offsetX - padL;
top = textTop + offsetY - padT;
right = textLeft + offsetX + padR + width;
bottom = textTop + offsetY + padB + height;
tl = {left, top};
tr = {right, top};
br = {right, bottom};
bl = {left, bottom};
} else {
tl = {left, top};
tr = {right, top};
br = {right, bottom};
bl = {left, bottom};
}
Point<float> tl{left, top};
Point<float> tr{right, top};
Point<float> br{right, bottom};
Point<float> bl{left, bottom};
const float angle = shapedIcon.angle();
@@ -88,7 +54,7 @@ SymbolQuad getIconQuad(const PositionedIcon& shapedIcon,
static_cast<uint16_t>(image.textureRect.h + border * 2)
};
return SymbolQuad { tl, tr, bl, br, textureRect, shapedText.writingMode, { 0.0f, 0.0f } };
return SymbolQuad { tl, tr, bl, br, textureRect, writingMode, { 0.0f, 0.0f } };
}
SymbolQuads getGlyphQuads(const Shaping& shapedText,

View File

@@ -44,9 +44,7 @@ public:
using SymbolQuads = std::vector<SymbolQuad>;
SymbolQuad getIconQuad(const PositionedIcon& shapedIcon,
const style::SymbolLayoutProperties::Evaluated&,
const float layoutTextSize,
const Shaping& shapedText);
WritingModeType writingMode);
SymbolQuads getGlyphQuads(const Shaping& shapedText,
const std::array<float, 2> textOffset,

View File

@@ -68,16 +68,49 @@ style::TextJustifyType getAnchorJustification(style::SymbolAnchorType anchor) {
}
}
PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, const std::array<float, 2>& iconOffset, style::SymbolAnchorType iconAnchor, const float iconRotation) {
PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image,
const std::array<float, 2>& iconOffset,
style::SymbolAnchorType iconAnchor,
const float iconRotation) {
AnchorAlignment anchorAlign = AnchorAlignment::getAnchorAlignment(iconAnchor);
float dx = iconOffset[0];
float dy = iconOffset[1];
float x1 = dx - image.displaySize()[0] * anchorAlign.horizontalAlign;
float x2 = x1 + image.displaySize()[0];
float y1 = dy - image.displaySize()[1] * anchorAlign.verticalAlign;
float y2 = y1 + image.displaySize()[1];
float left = dx - image.displaySize()[0] * anchorAlign.horizontalAlign;
float right = left + image.displaySize()[0];
float top = dy - image.displaySize()[1] * anchorAlign.verticalAlign;
float bottom = top + image.displaySize()[1];
return PositionedIcon { image, y1, y2, x1, x2, iconRotation };
return PositionedIcon { image, top, bottom, left, right, iconRotation };
}
void PositionedIcon::fitIconToText(const style::SymbolLayoutProperties::Evaluated& layout,
const Shaping& shapedText,
float layoutTextSize) {
using namespace style;
assert(layout.get<IconTextFit>() != IconTextFitType::None);
if (shapedText) {
auto iconWidth = _right - _left;
auto iconHeight = _bottom - _top;
auto size = layoutTextSize / 24.0f;
auto textLeft = shapedText.left * size;
auto textRight = shapedText.right * size;
auto textTop = shapedText.top * size;
auto textBottom = shapedText.bottom * size;
auto textWidth = textRight - textLeft;
auto textHeight = textBottom - textTop;
auto padT = layout.get<IconTextFitPadding>()[0];
auto padR = layout.get<IconTextFitPadding>()[1];
auto padB = layout.get<IconTextFitPadding>()[2];
auto padL = layout.get<IconTextFitPadding>()[3];
auto offsetY = layout.get<IconTextFit>() == IconTextFitType::Width ? (textHeight - iconHeight) * 0.5 : 0;
auto offsetX = layout.get<IconTextFit>() == IconTextFitType::Height ? (textWidth - iconWidth) * 0.5 : 0;
auto width = layout.get<IconTextFit>() == IconTextFitType::Width || layout.get<IconTextFit>() == IconTextFitType::Both ? textWidth : iconWidth;
auto height = layout.get<IconTextFit>() == IconTextFitType::Height || layout.get<IconTextFit>() == IconTextFitType::Both ? textHeight : iconHeight;
_left = textLeft + offsetX - padL;
_top = textTop + offsetY - padT;
_right = textLeft + offsetX + padR + width;
_bottom = textTop + offsetY + padB + height;
}
}
void align(Shaping& shaping,

View File

@@ -4,6 +4,7 @@
#include <mbgl/text/tagged_string.hpp>
#include <mbgl/renderer/image_atlas.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
namespace mbgl {
@@ -52,6 +53,12 @@ public:
style::SymbolAnchorType iconAnchor,
const float iconRotation);
// Updates shaped icon's bounds based on shaped text's bounds and provided
// layout properties.
void fitIconToText(const style::SymbolLayoutProperties::Evaluated& layout,
const Shaping& shapedText,
float layoutTextSize);
const ImagePosition& image() const { return _image; }
float top() const { return _top; }
float bottom() const { return _bottom; }

View File

@@ -16,9 +16,9 @@ SymbolInstance makeSymbolInstance(float x, float y, std::u16string key) {
style::SymbolPlacementType placementType = style::SymbolPlacementType::Point;
auto sharedData = std::make_shared<SymbolInstanceSharedData>(std::move(line),
shaping, nullopt, layout_, 0.0f, placementType,
shaping, nullopt, nullopt, layout_, placementType,
textOffset, positions, false);
return SymbolInstance(anchor, std::move(sharedData), shaping, nullopt, 0, 0, placementType, textOffset, 0, 0, iconOffset, subfeature, 0, 0, key, 0.0f, 0.0f, 0.0f, 0.0f, false);
return SymbolInstance(anchor, std::move(sharedData), shaping, nullopt, nullopt, 0, 0, placementType, textOffset, 0, 0, iconOffset, subfeature, 0, 0, key, 0.0f, 0.0f, 0.0f, 0.0f, false);
}

View File

@@ -20,10 +20,9 @@ TEST(getIconQuads, normal) {
auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -6.5f, -4.5f }}, SymbolAnchorType::Center, 0);
GeometryCoordinates line;
Shaping shapedText;
SymbolQuad quad =
getIconQuad(shapedIcon, layout, 16.0f, shapedText);
getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -14);
EXPECT_EQ(quad.tl.y, -10);
@@ -42,8 +41,6 @@ TEST(getIconQuads, style) {
style::Image::Impl("test", PremultipliedImage({1,1}), 1.0)
};
auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
GeometryCoordinates line;
Shaping shapedText;
shapedText.top = -10.0f;
@@ -54,9 +51,10 @@ TEST(getIconQuads, style) {
// none
{
auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
SymbolLayoutProperties::Evaluated layout;
SymbolQuad quad =
getIconQuad(shapedIcon, layout, 12.0f, shapedText);
getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -19.5);
EXPECT_EQ(quad.tl.y, -19.5);
@@ -73,16 +71,18 @@ TEST(getIconQuads, style) {
SymbolLayoutProperties::Evaluated layout;
layout.get<TextSize>() = 24.0f;
layout.get<IconTextFit>() = IconTextFitType::Width;
auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
shapedIcon.fitIconToText(layout, shapedText, 24.0f);
SymbolQuad quad =
getIconQuad(shapedIcon, layout, 24.0f, shapedText);
getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -60);
EXPECT_EQ(quad.tl.x, -61);
EXPECT_EQ(quad.tl.y, 0);
EXPECT_EQ(quad.tr.x, 20);
EXPECT_EQ(quad.tr.x, 21);
EXPECT_EQ(quad.tr.y, 0);
EXPECT_EQ(quad.bl.x, -60);
EXPECT_EQ(quad.bl.x, -61);
EXPECT_EQ(quad.bl.y, 20);
EXPECT_EQ(quad.br.x, 20);
EXPECT_EQ(quad.br.x, 21);
EXPECT_EQ(quad.br.y, 20);
}
@@ -91,16 +91,18 @@ TEST(getIconQuads, style) {
SymbolLayoutProperties::Evaluated layout;
layout.get<TextSize>() = 12.0f;
layout.get<IconTextFit>() = IconTextFitType::Width;
auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
shapedIcon.fitIconToText(layout, shapedText, 12.0f);
SymbolQuad quad =
getIconQuad(shapedIcon, layout, 12.0f, shapedText);
getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -30);
EXPECT_EQ(quad.tl.x, -31);
EXPECT_EQ(quad.tl.y, -5);
EXPECT_EQ(quad.tr.x, 10);
EXPECT_EQ(quad.tr.x, 11);
EXPECT_EQ(quad.tr.y, -5);
EXPECT_EQ(quad.bl.x, -30);
EXPECT_EQ(quad.bl.x, -31);
EXPECT_EQ(quad.bl.y, 15);
EXPECT_EQ(quad.br.x, 10);
EXPECT_EQ(quad.br.x, 11);
EXPECT_EQ(quad.br.y, 15);
}
@@ -113,16 +115,18 @@ TEST(getIconQuads, style) {
layout.get<IconTextFitPadding>()[1] = 10.0f;
layout.get<IconTextFitPadding>()[2] = 5.0f;
layout.get<IconTextFitPadding>()[3] = 10.0f;
auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
shapedIcon.fitIconToText(layout, shapedText, 12.0f);
SymbolQuad quad =
getIconQuad(shapedIcon, layout, 12.0f, shapedText);
getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -40);
EXPECT_EQ(quad.tl.x, -41);
EXPECT_EQ(quad.tl.y, -10);
EXPECT_EQ(quad.tr.x, 20);
EXPECT_EQ(quad.tr.x, 21);
EXPECT_EQ(quad.tr.y, -10);
EXPECT_EQ(quad.bl.x, -40);
EXPECT_EQ(quad.bl.x, -41);
EXPECT_EQ(quad.bl.y, 20);
EXPECT_EQ(quad.br.x, 20);
EXPECT_EQ(quad.br.x, 21);
EXPECT_EQ(quad.br.y, 20);
}
@@ -131,17 +135,19 @@ TEST(getIconQuads, style) {
SymbolLayoutProperties::Evaluated layout;
layout.get<TextSize>() = 24.0f;
layout.get<IconTextFit>() = IconTextFitType::Height;
auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
shapedIcon.fitIconToText(layout, shapedText, 24.0f);
SymbolQuad quad =
getIconQuad(shapedIcon, layout, 24.0f, shapedText);
getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -30);
EXPECT_EQ(quad.tl.y, -10);
EXPECT_EQ(quad.tl.y, -11);
EXPECT_EQ(quad.tr.x, -10);
EXPECT_EQ(quad.tr.y, -10);
EXPECT_EQ(quad.tr.y, -11);
EXPECT_EQ(quad.bl.x, -30);
EXPECT_EQ(quad.bl.y, 30);
EXPECT_EQ(quad.bl.y, 31);
EXPECT_EQ(quad.br.x, -10);
EXPECT_EQ(quad.br.y, 30);
EXPECT_EQ(quad.br.y, 31);
}
// height x textSize
@@ -149,17 +155,19 @@ TEST(getIconQuads, style) {
SymbolLayoutProperties::Evaluated layout;
layout.get<TextSize>() = 12.0f;
layout.get<IconTextFit>() = IconTextFitType::Height;
auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
shapedIcon.fitIconToText(layout, shapedText, 12.0f);
SymbolQuad quad =
getIconQuad(shapedIcon, layout, 12.0f, shapedText);
getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -20);
EXPECT_EQ(quad.tl.y, -5);
EXPECT_EQ(quad.tl.y, -6);
EXPECT_EQ(quad.tr.x, 0);
EXPECT_EQ(quad.tr.y, -5);
EXPECT_EQ(quad.tr.y, -6);
EXPECT_EQ(quad.bl.x, -20);
EXPECT_EQ(quad.bl.y, 15);
EXPECT_EQ(quad.bl.y, 16);
EXPECT_EQ(quad.br.x, 0);
EXPECT_EQ(quad.br.y, 15);
EXPECT_EQ(quad.br.y, 16);
}
// height x textSize + padding
@@ -171,17 +179,19 @@ TEST(getIconQuads, style) {
layout.get<IconTextFitPadding>()[1] = 10.0f;
layout.get<IconTextFitPadding>()[2] = 5.0f;
layout.get<IconTextFitPadding>()[3] = 10.0f;
auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
shapedIcon.fitIconToText(layout, shapedText, 12.0f);
SymbolQuad quad =
getIconQuad(shapedIcon, layout, 12.0f, shapedText);
getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -30);
EXPECT_EQ(quad.tl.y, -10);
EXPECT_EQ(quad.tl.y, -11);
EXPECT_EQ(quad.tr.x, 10);
EXPECT_EQ(quad.tr.y, -10);
EXPECT_EQ(quad.tr.y, -11);
EXPECT_EQ(quad.bl.x, -30);
EXPECT_EQ(quad.bl.y, 20);
EXPECT_EQ(quad.bl.y, 21);
EXPECT_EQ(quad.br.x, 10);
EXPECT_EQ(quad.br.y, 20);
EXPECT_EQ(quad.br.y, 21);
}
// both
@@ -189,17 +199,19 @@ TEST(getIconQuads, style) {
SymbolLayoutProperties::Evaluated layout;
layout.get<TextSize>() = 24.0f;
layout.get<IconTextFit>() = IconTextFitType::Both;
auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
shapedIcon.fitIconToText(layout, shapedText, 24.0f);
SymbolQuad quad =
getIconQuad(shapedIcon, layout, 24.0f, shapedText);
getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -60);
EXPECT_EQ(quad.tl.y, -10);
EXPECT_EQ(quad.tr.x, 20);
EXPECT_EQ(quad.tr.y, -10);
EXPECT_EQ(quad.bl.x, -60);
EXPECT_EQ(quad.bl.y, 30);
EXPECT_EQ(quad.br.x, 20);
EXPECT_EQ(quad.br.y, 30);
EXPECT_EQ(quad.tl.x, -61);
EXPECT_EQ(quad.tl.y, -11);
EXPECT_EQ(quad.tr.x, 21);
EXPECT_EQ(quad.tr.y, -11);
EXPECT_EQ(quad.bl.x, -61);
EXPECT_EQ(quad.bl.y, 31);
EXPECT_EQ(quad.br.x, 21);
EXPECT_EQ(quad.br.y, 31);
}
// both x textSize
@@ -207,17 +219,19 @@ TEST(getIconQuads, style) {
SymbolLayoutProperties::Evaluated layout;
layout.get<TextSize>() = 12.0f;
layout.get<IconTextFit>() = IconTextFitType::Both;
auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
shapedIcon.fitIconToText(layout, shapedText, 12.0f);
SymbolQuad quad =
getIconQuad(shapedIcon, layout, 12.0f, shapedText);
getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -30);
EXPECT_EQ(quad.tl.y, -5);
EXPECT_EQ(quad.tr.x, 10);
EXPECT_EQ(quad.tr.y, -5);
EXPECT_EQ(quad.bl.x, -30);
EXPECT_EQ(quad.bl.y, 15);
EXPECT_EQ(quad.br.x, 10);
EXPECT_EQ(quad.br.y, 15);
EXPECT_EQ(quad.tl.x, -31);
EXPECT_EQ(quad.tl.y, -6);
EXPECT_EQ(quad.tr.x, 11);
EXPECT_EQ(quad.tr.y, -6);
EXPECT_EQ(quad.bl.x, -31);
EXPECT_EQ(quad.bl.y, 16);
EXPECT_EQ(quad.br.x, 11);
EXPECT_EQ(quad.br.y, 16);
}
// both x textSize + padding
@@ -229,17 +243,19 @@ TEST(getIconQuads, style) {
layout.get<IconTextFitPadding>()[1] = 10.0f;
layout.get<IconTextFitPadding>()[2] = 5.0f;
layout.get<IconTextFitPadding>()[3] = 10.0f;
auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
shapedIcon.fitIconToText(layout, shapedText, 12.0f);
SymbolQuad quad =
getIconQuad(shapedIcon, layout, 12.0f, shapedText);
getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -40);
EXPECT_EQ(quad.tl.y, -10);
EXPECT_EQ(quad.tr.x, 20);
EXPECT_EQ(quad.tr.y, -10);
EXPECT_EQ(quad.bl.x, -40);
EXPECT_EQ(quad.bl.y, 20);
EXPECT_EQ(quad.br.x, 20);
EXPECT_EQ(quad.br.y, 20);
EXPECT_EQ(quad.tl.x, -41);
EXPECT_EQ(quad.tl.y, -11);
EXPECT_EQ(quad.tr.x, 21);
EXPECT_EQ(quad.tr.y, -11);
EXPECT_EQ(quad.bl.x, -41);
EXPECT_EQ(quad.bl.y, 21);
EXPECT_EQ(quad.br.x, 21);
EXPECT_EQ(quad.br.y, 21);
}
// both x textSize + padding t/r/b/l
@@ -251,17 +267,19 @@ TEST(getIconQuads, style) {
layout.get<IconTextFitPadding>()[1] = 5.0f;
layout.get<IconTextFitPadding>()[2] = 10.0f;
layout.get<IconTextFitPadding>()[3] = 15.0f;
auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
shapedIcon.fitIconToText(layout, shapedText, 12.0f);
SymbolQuad quad =
getIconQuad(shapedIcon, layout, 12.0f, shapedText);
getIconQuad(shapedIcon, WritingModeType::Horizontal);
EXPECT_EQ(quad.tl.x, -45);
EXPECT_EQ(quad.tl.y, -5);
EXPECT_EQ(quad.tr.x, 15);
EXPECT_EQ(quad.tr.y, -5);
EXPECT_EQ(quad.bl.x, -45);
EXPECT_EQ(quad.bl.y, 25);
EXPECT_EQ(quad.br.x, 15);
EXPECT_EQ(quad.br.y, 25);
EXPECT_EQ(quad.tl.x, -46);
EXPECT_EQ(quad.tl.y, -6);
EXPECT_EQ(quad.tr.x, 16);
EXPECT_EQ(quad.tr.y, -6);
EXPECT_EQ(quad.bl.x, -46);
EXPECT_EQ(quad.bl.y, 26);
EXPECT_EQ(quad.br.x, 16);
EXPECT_EQ(quad.br.y, 26);
}
}