diff options
Diffstat (limited to 'src/mbgl/layout')
-rw-r--r-- | src/mbgl/layout/symbol_instance.cpp | 37 | ||||
-rw-r--r-- | src/mbgl/layout/symbol_instance.hpp | 16 | ||||
-rw-r--r-- | src/mbgl/layout/symbol_layout.cpp | 300 | ||||
-rw-r--r-- | src/mbgl/layout/symbol_layout.hpp | 15 | ||||
-rw-r--r-- | src/mbgl/layout/symbol_projection.cpp | 160 | ||||
-rw-r--r-- | src/mbgl/layout/symbol_projection.hpp | 42 |
6 files changed, 336 insertions, 234 deletions
diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp index 02fb800df6..7dfa8edf43 100644 --- a/src/mbgl/layout/symbol_instance.cpp +++ b/src/mbgl/layout/symbol_instance.cpp @@ -11,7 +11,6 @@ SymbolInstance::SymbolInstance(Anchor& anchor_, optional<PositionedIcon> shapedIcon, const SymbolLayoutProperties::Evaluated& layout, const float layoutTextSize, - const bool addToBuffers, const uint32_t index_, const float textBoxScale, const float textPadding, @@ -19,38 +18,38 @@ SymbolInstance::SymbolInstance(Anchor& anchor_, const std::array<float, 2> textOffset_, const float iconBoxScale, const float iconPadding, - const SymbolPlacementType iconPlacement, const std::array<float, 2> iconOffset_, const GlyphPositionMap& positions, const IndexedSubfeature& indexedFeature, - const std::size_t featureIndex_) : + const std::size_t featureIndex_, + const std::u16string& key_, + const float overscaling) : anchor(anchor_), line(line_), index(index_), - hasText(shapedTextOrientations.first || shapedTextOrientations.second), + hasText(false), hasIcon(shapedIcon), // Create the collision features that will be used to check whether this symbol instance can be placed - textCollisionFeature(line_, anchor, shapedTextOrientations.second ?: shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature), - iconCollisionFeature(line_, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature), + textCollisionFeature(line_, anchor, shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature, overscaling), + iconCollisionFeature(line_, anchor, shapedIcon, iconBoxScale, iconPadding, indexedFeature), featureIndex(featureIndex_), textOffset(textOffset_), - iconOffset(iconOffset_) { + iconOffset(iconOffset_), + key(key_) { // Create the quads used for rendering the icon and glyphs. - if (addToBuffers) { - if (shapedIcon) { - iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.first); - } - if (shapedTextOrientations.first) { - auto quads = getGlyphQuads(shapedTextOrientations.first, layout, textPlacement, positions); - glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end()); - } - if (shapedTextOrientations.second) { - auto quads = getGlyphQuads(shapedTextOrientations.second, layout, textPlacement, positions); - glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end()); - } + if (shapedIcon) { + iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.first); } + if (shapedTextOrientations.first) { + horizontalGlyphQuads = getGlyphQuads(shapedTextOrientations.first, layout, textPlacement, positions); + } + if (shapedTextOrientations.second) { + verticalGlyphQuads = getGlyphQuads(shapedTextOrientations.second, layout, textPlacement, positions); + } + // 'hasText' depends on finding at least one glyph in the shaping that's also in the GlyphPositionMap + hasText = horizontalGlyphQuads.size() > 0 || verticalGlyphQuads.size() > 0; if (shapedTextOrientations.first && shapedTextOrientations.second) { writingModes = WritingModeType::Horizontal | WritingModeType::Vertical; diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp index f1df416cd1..827a5dbbdb 100644 --- a/src/mbgl/layout/symbol_instance.hpp +++ b/src/mbgl/layout/symbol_instance.hpp @@ -5,6 +5,7 @@ #include <mbgl/text/collision_feature.hpp> #include <mbgl/style/layers/symbol_layer_properties.hpp> + namespace mbgl { class Anchor; @@ -18,7 +19,6 @@ public: optional<PositionedIcon> shapedIcon, const style::SymbolLayoutProperties::Evaluated&, const float layoutTextSize, - const bool inside, const uint32_t index, const float textBoxScale, const float textPadding, @@ -26,18 +26,20 @@ public: const std::array<float, 2> textOffset, const float iconBoxScale, const float iconPadding, - style::SymbolPlacementType iconPlacement, const std::array<float, 2> iconOffset, const GlyphPositionMap&, const IndexedSubfeature&, - const std::size_t featureIndex); + const std::size_t featureIndex, + const std::u16string& key, + const float overscaling); Anchor anchor; GeometryCoordinates line; uint32_t index; bool hasText; bool hasIcon; - SymbolQuads glyphQuads; + SymbolQuads horizontalGlyphQuads; + SymbolQuads verticalGlyphQuads; optional<SymbolQuad> iconQuad; CollisionFeature textCollisionFeature; CollisionFeature iconCollisionFeature; @@ -45,6 +47,12 @@ public: std::size_t featureIndex; std::array<float, 2> textOffset; std::array<float, 2> iconOffset; + std::u16string key; + bool isDuplicate; + optional<size_t> placedTextIndex; + optional<size_t> placedVerticalTextIndex; + optional<size_t> placedIconIndex; + uint32_t crossTileID = 0; }; } // namespace mbgl diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 2c90b69b08..b2f6fd450f 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -8,7 +8,6 @@ #include <mbgl/renderer/image_atlas.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> #include <mbgl/text/get_anchors.hpp> -#include <mbgl/text/collision_tile.hpp> #include <mbgl/text/shaping.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/utf.hpp> @@ -33,7 +32,7 @@ using namespace style; template <class Property> static bool has(const style::SymbolLayoutProperties::PossiblyEvaluated& layout) { return layout.get<Property>().match( - [&] (const std::string& s) { return !s.empty(); }, + [&] (const typename Property::Type& t) { return !t.empty(); }, [&] (const auto&) { return true; } ); } @@ -43,8 +42,8 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, std::unique_ptr<GeometryTileLayer> sourceLayer_, ImageDependencies& imageDependencies, GlyphDependencies& glyphDependencies) - : sourceLayer(std::move(sourceLayer_)), - bucketName(layers.at(0)->getID()), + : bucketLeaderID(layers.at(0)->getID()), + sourceLayer(std::move(sourceLayer_)), overscaling(parameters.tileID.overscaleFactor()), zoom(parameters.tileID.overscaledZ), mode(parameters.mode), @@ -83,7 +82,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, layout.get<IconPitchAlignment>() = layout.get<IconRotationAlignment>(); } - const bool hasText = has<TextField>(layout) && !layout.get<TextFont>().empty(); + const bool hasText = has<TextField>(layout) && has<TextFont>(layout); const bool hasIcon = has<IconImage>(layout); if (!hasText && !hasIcon) { @@ -101,7 +100,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, const size_t featureCount = sourceLayer->featureCount(); for (size_t i = 0; i < featureCount; ++i) { auto feature = sourceLayer->getFeature(i); - if (!leader.filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); })) + if (!leader.filter(expression::EvaluationContext { this->zoom, feature.get() })) continue; SymbolFeature ft(std::move(feature)); @@ -127,7 +126,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, if (hasText) { std::string u8string = layout.evaluate<TextField>(zoom, ft); - if (layout.get<TextField>().isConstant()) { + if (layout.get<TextField>().isConstant() && !leader.layout.get<TextField>().isExpression()) { u8string = util::replaceTokens(u8string, getValue); } @@ -144,12 +143,15 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, && layout.get<SymbolPlacement>() == SymbolPlacementType::Line && util::i18n::allowsVerticalWritingMode(*ft.text); + FontStack fontStack = layout.evaluate<TextFont>(zoom, ft); + GlyphIDs& dependencies = glyphDependencies[fontStack]; + // Loop through all characters of this text and collect unique codepoints. for (char16_t chr : *ft.text) { - glyphDependencies[layout.get<TextFont>()].insert(chr); + dependencies.insert(chr); if (canVerticalizeText) { if (char16_t verticalChr = util::i18n::verticalizePunctuation(chr)) { - glyphDependencies[layout.get<TextFont>()].insert(verticalChr); + dependencies.insert(verticalChr); } } } @@ -157,7 +159,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, if (hasIcon) { std::string icon = layout.evaluate<IconImage>(zoom, ft); - if (layout.get<IconImage>().isConstant()) { + if (layout.get<IconImage>().isConstant() && !leader.layout.get<IconImage>().isExpression()) { icon = util::replaceTokens(icon, getValue); } ft.icon = icon; @@ -183,18 +185,20 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map && layout.get<SymbolPlacement>() == SymbolPlacementType::Line; - auto glyphMapIt = glyphMap.find(layout.get<TextFont>()); - const Glyphs& glyphs = glyphMapIt != glyphMap.end() - ? glyphMapIt->second : Glyphs(); - - auto glyphPositionsIt = glyphPositions.find(layout.get<TextFont>()); - const GlyphPositionMap& glyphPositionMap = glyphPositionsIt != glyphPositions.end() - ? glyphPositionsIt->second : GlyphPositionMap(); - for (auto it = features.begin(); it != features.end(); ++it) { auto& feature = *it; if (feature.geometry.empty()) continue; + FontStack fontStack = layout.evaluate<TextFont>(zoom, feature); + + auto glyphMapIt = glyphMap.find(fontStack); + const Glyphs& glyphs = glyphMapIt != glyphMap.end() + ? glyphMapIt->second : Glyphs(); + + auto glyphPositionsIt = glyphPositions.find(fontStack); + const GlyphPositionMap& glyphPositionMap = glyphPositionsIt != glyphPositions.end() + ? glyphPositionsIt->second : GlyphPositionMap(); + std::pair<Shaping, Shaping> shapedTextOrientations; optional<PositionedIcon> shapedIcon; @@ -288,12 +292,9 @@ void SymbolLayout::addFeature(const std::size_t index, const SymbolPlacementType textPlacement = layout.get<TextRotationAlignment>() != AlignmentType::Map ? SymbolPlacementType::Point : layout.get<SymbolPlacement>(); - const SymbolPlacementType iconPlacement = layout.get<IconRotationAlignment>() != AlignmentType::Map - ? SymbolPlacementType::Point - : layout.get<SymbolPlacement>(); + const float textRepeatDistance = symbolSpacing / 2; - IndexedSubfeature indexedFeature = { feature.index, sourceLayer->getName(), bucketName, - symbolInstances.size() }; + IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketLeaderID, symbolInstances.size()); auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) { // https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers @@ -314,14 +315,14 @@ void SymbolLayout::addFeature(const std::size_t index, if (avoidEdges && !inside) return; - const bool addToBuffers = mode == MapMode::Still || withinPlus0; - - symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, - layout.evaluate(zoom, feature), layoutTextSize, - addToBuffers, symbolInstances.size(), - textBoxScale, textPadding, textPlacement, textOffset, - iconBoxScale, iconPadding, iconPlacement, iconOffset, - glyphPositionMap, indexedFeature, index); + if (mode == MapMode::Tile || withinPlus0) { + symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, + layout.evaluate(zoom, feature), layoutTextSize, + symbolInstances.size(), + textBoxScale, textPadding, textPlacement, textOffset, + iconBoxScale, iconPadding, iconOffset, + glyphPositionMap, indexedFeature, index, feature.text.value_or(std::u16string()), overscaling); + } }; const auto& type = feature.getType(); @@ -392,108 +393,93 @@ bool SymbolLayout::anchorIsTooClose(const std::u16string& text, const float repe return false; } -std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) { - auto bucket = std::make_unique<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear); - - // Calculate which labels can be shown and when they can be shown and - // create the bufers used for rendering. - - const SymbolPlacementType textPlacement = layout.get<TextRotationAlignment>() != AlignmentType::Map - ? SymbolPlacementType::Point - : layout.get<SymbolPlacement>(); - const SymbolPlacementType iconPlacement = layout.get<IconRotationAlignment>() != AlignmentType::Map - ? SymbolPlacementType::Point - : layout.get<SymbolPlacement>(); +// Analog of `addToLineVertexArray` in JS. This version doesn't need to build up a line array like the +// JS version does, but it uses the same logic to calculate tile distances. +std::vector<float> CalculateTileDistances(const GeometryCoordinates& line, const Anchor& anchor) { + std::vector<float> tileDistances(line.size()); + if (anchor.segment != -1) { + auto sumForwardLength = util::dist<float>(anchor.point, line[anchor.segment + 1]); + auto sumBackwardLength = util::dist<float>(anchor.point, line[anchor.segment]); + for (size_t i = anchor.segment + 1; i < line.size(); i++) { + tileDistances[i] = sumForwardLength; + if (i < line.size() - 1) { + sumForwardLength += util::dist<float>(line[i + 1], line[i]); + } + } + for (auto i = anchor.segment; i >= 0; i--) { + tileDistances[i] = sumBackwardLength; + if (i > 0) { + sumBackwardLength += util::dist<float>(line[i - 1], line[i]); + } + } + } + return tileDistances; +} +std::unique_ptr<SymbolBucket> SymbolLayout::place(const bool showCollisionBoxes) { const bool mayOverlap = layout.get<TextAllowOverlap>() || layout.get<IconAllowOverlap>() || layout.get<TextIgnorePlacement>() || layout.get<IconIgnorePlacement>(); + + auto bucket = std::make_unique<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, mayOverlap, bucketLeaderID, std::move(symbolInstances)); - const bool keepUpright = layout.get<TextKeepUpright>(); - - // Sort symbols by their y position on the canvas so that they lower symbols - // are drawn on top of higher symbols. - // Don't sort symbols that won't overlap because it isn't necessary and - // because it causes more labels to pop in and out when rotating. - if (mayOverlap) { - const float sin = std::sin(collisionTile.config.angle); - const float cos = std::cos(collisionTile.config.angle); - - std::sort(symbolInstances.begin(), symbolInstances.end(), [sin, cos](SymbolInstance &a, SymbolInstance &b) { - const int32_t aRotated = sin * a.anchor.point.x + cos * a.anchor.point.y; - const int32_t bRotated = sin * b.anchor.point.x + cos * b.anchor.point.y; - return aRotated != bRotated ? - aRotated < bRotated : - a.index > b.index; - }); - } - - for (SymbolInstance &symbolInstance : symbolInstances) { + for (SymbolInstance &symbolInstance : bucket->symbolInstances) { const bool hasText = symbolInstance.hasText; const bool hasIcon = symbolInstance.hasIcon; - const bool iconWithoutText = layout.get<TextOptional>() || !hasText; - const bool textWithoutIcon = layout.get<IconOptional>() || !hasIcon; - - // Calculate the scales at which the text and icon can be placed without collision. - - float glyphScale = hasText ? - collisionTile.placeFeature(symbolInstance.textCollisionFeature, - layout.get<TextAllowOverlap>(), layout.get<SymbolAvoidEdges>()) : - collisionTile.minScale; - float iconScale = hasIcon ? - collisionTile.placeFeature(symbolInstance.iconCollisionFeature, - layout.get<IconAllowOverlap>(), layout.get<SymbolAvoidEdges>()) : - collisionTile.minScale; - - - // Combine the scales for icons and text. - - if (!iconWithoutText && !textWithoutIcon) { - iconScale = glyphScale = util::max(iconScale, glyphScale); - } else if (!textWithoutIcon && glyphScale) { - glyphScale = util::max(iconScale, glyphScale); - } else if (!iconWithoutText && iconScale) { - iconScale = util::max(iconScale, glyphScale); - } - const auto& feature = features.at(symbolInstance.featureIndex); // Insert final placement into collision tree and add glyphs/icons to buffers if (hasText) { - const float placementZoom = util::max(util::log2(glyphScale) + zoom, 0.0f); - collisionTile.insertFeature(symbolInstance.textCollisionFeature, glyphScale, layout.get<TextIgnorePlacement>()); - if (glyphScale < collisionTile.maxScale) { - - const float labelAngle = std::fmod((symbolInstance.anchor.angle + collisionTile.config.angle) + 2 * M_PI, 2 * M_PI); - const bool inVerticalRange = ( - (labelAngle > M_PI * 1.0 / 4.0 && labelAngle <= M_PI * 3.0 / 4) || - (labelAngle > M_PI * 5.0 / 4.0 && labelAngle <= M_PI * 7.0 / 4)); - const bool useVerticalMode = symbolInstance.writingModes & WritingModeType::Vertical && inVerticalRange; - - const Range<float> sizeData = bucket->textSizeBinder->getVertexSizeData(feature); + const Range<float> sizeData = bucket->textSizeBinder->getVertexSizeData(feature); + bucket->text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max, + symbolInstance.textOffset, symbolInstance.writingModes, symbolInstance.line, CalculateTileDistances(symbolInstance.line, symbolInstance.anchor)); + symbolInstance.placedTextIndex = bucket->text.placedSymbols.size() - 1; + PlacedSymbol& horizontalSymbol = bucket->text.placedSymbols.back(); + + bool firstHorizontal = true; + for (const auto& symbol : symbolInstance.horizontalGlyphQuads) { + size_t index = addSymbol( + bucket->text, sizeData, symbol, + symbolInstance.anchor, horizontalSymbol); + if (firstHorizontal) { + horizontalSymbol.vertexStartIndex = index; + firstHorizontal = false; + } + } + + if (symbolInstance.writingModes & WritingModeType::Vertical) { bucket->text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max, - symbolInstance.textOffset, placementZoom, useVerticalMode, symbolInstance.line); - - for (const auto& symbol : symbolInstance.glyphQuads) { - addSymbol( - bucket->text, sizeData, symbol, placementZoom, - keepUpright, textPlacement, symbolInstance.anchor, bucket->text.placedSymbols.back()); + symbolInstance.textOffset, WritingModeType::Vertical, symbolInstance.line, CalculateTileDistances(symbolInstance.line, symbolInstance.anchor)); + symbolInstance.placedVerticalTextIndex = bucket->text.placedSymbols.size() - 1; + + PlacedSymbol& verticalSymbol = bucket->text.placedSymbols.back(); + bool firstVertical = true; + + for (const auto& symbol : symbolInstance.verticalGlyphQuads) { + size_t index = addSymbol( + bucket->text, sizeData, symbol, + symbolInstance.anchor, verticalSymbol); + + if (firstVertical) { + verticalSymbol.vertexStartIndex = index; + firstVertical = false; + } } } } if (hasIcon) { - const float placementZoom = util::max(util::log2(iconScale) + zoom, 0.0f); - collisionTile.insertFeature(symbolInstance.iconCollisionFeature, iconScale, layout.get<IconIgnorePlacement>()); - if (iconScale < collisionTile.maxScale && symbolInstance.iconQuad) { + if (symbolInstance.iconQuad) { 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, placementZoom, false, symbolInstance.line); - addSymbol( - bucket->icon, sizeData, *symbolInstance.iconQuad, placementZoom, - keepUpright, iconPlacement, symbolInstance.anchor, bucket->icon.placedSymbols.back()); + 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); } } @@ -503,20 +489,17 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) } } - if (collisionTile.config.debug) { - addToDebugBuffers(collisionTile, *bucket); + if (showCollisionBoxes) { + addToDebugBuffers(*bucket); } return bucket; } template <typename Buffer> -void SymbolLayout::addSymbol(Buffer& buffer, +size_t SymbolLayout::addSymbol(Buffer& buffer, const Range<float> sizeData, const SymbolQuad& symbol, - const float placementZoom, - const bool keepUpright, - const style::SymbolPlacementType placement, const Anchor& labelAnchor, PlacedSymbol& placedSymbol) { constexpr const uint16_t vertexLength = 4; @@ -527,11 +510,6 @@ void SymbolLayout::addSymbol(Buffer& buffer, const auto &br = symbol.br; const auto &tex = symbol.tex; - if (placement == style::SymbolPlacementType::Line && keepUpright) { - // drop incorrectly oriented glyphs - if ((symbol.writingMode == WritingModeType::Vertical) != placedSymbol.useVerticalMode) return; - } - if (buffer.segments.empty() || buffer.segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) { buffer.segments.emplace_back(buffer.vertices.vertexSize(), buffer.triangles.indexSize()); } @@ -548,11 +526,19 @@ void SymbolLayout::addSymbol(Buffer& buffer, buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, bl, symbol.glyphOffset.y, tex.x, tex.y + tex.h, sizeData)); buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, br, symbol.glyphOffset.y, tex.x + tex.w, tex.y + tex.h, sizeData)); - auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(labelAnchor.point, 0, placementZoom); + // Dynamic/Opacity 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 = SymbolDynamicLayoutAttributes::vertex(labelAnchor.point, 0); buffer.dynamicVertices.emplace_back(dynamicVertex); buffer.dynamicVertices.emplace_back(dynamicVertex); buffer.dynamicVertices.emplace_back(dynamicVertex); buffer.dynamicVertices.emplace_back(dynamicVertex); + + auto opacityVertex = SymbolOpacityAttributes::vertex(1.0, 1.0); + buffer.opacityVertices.emplace_back(opacityVertex); + buffer.opacityVertices.emplace_back(opacityVertex); + buffer.opacityVertices.emplace_back(opacityVertex); + buffer.opacityVertices.emplace_back(opacityVertex); // add the two triangles, referencing the four coordinates we just inserted. buffer.triangles.emplace_back(index + 0, index + 1, index + 2); @@ -562,54 +548,62 @@ void SymbolLayout::addSymbol(Buffer& buffer, segment.indexLength += 6; placedSymbol.glyphOffsets.push_back(symbol.glyphOffset.x); + + return index; } -void SymbolLayout::addToDebugBuffers(CollisionTile& collisionTile, SymbolBucket& bucket) { +void SymbolLayout::addToDebugBuffers(SymbolBucket& bucket) { if (!hasSymbolInstances()) { return; } - const float yStretch = collisionTile.yStretch; - - auto& collisionBox = bucket.collisionBox; - for (const SymbolInstance &symbolInstance : symbolInstances) { auto populateCollisionBox = [&](const auto& feature) { + SymbolBucket::CollisionBuffer& collisionBuffer = feature.alongLine ? + static_cast<SymbolBucket::CollisionBuffer&>(bucket.collisionCircle) : + static_cast<SymbolBucket::CollisionBuffer&>(bucket.collisionBox); for (const CollisionBox &box : feature.boxes) { auto& anchor = box.anchor; - Point<float> tl{box.x1, box.y1 * yStretch}; - Point<float> tr{box.x2, box.y1 * yStretch}; - Point<float> bl{box.x1, box.y2 * yStretch}; - Point<float> br{box.x2, box.y2 * yStretch}; - tl = util::matrixMultiply(collisionTile.reverseRotationMatrix, tl); - tr = util::matrixMultiply(collisionTile.reverseRotationMatrix, tr); - bl = util::matrixMultiply(collisionTile.reverseRotationMatrix, bl); - br = util::matrixMultiply(collisionTile.reverseRotationMatrix, br); - - const float maxZoom = util::clamp(zoom + util::log2(box.maxScale), util::MIN_ZOOM_F, util::MAX_ZOOM_F); - const float placementZoom = util::clamp(zoom + util::log2(box.placementScale), util::MIN_ZOOM_F, util::MAX_ZOOM_F); + Point<float> tl{box.x1, box.y1}; + Point<float> tr{box.x2, box.y1}; + Point<float> bl{box.x1, box.y2}; + Point<float> br{box.x2, box.y2}; static constexpr std::size_t vertexLength = 4; - static constexpr std::size_t indexLength = 8; + const std::size_t indexLength = feature.alongLine ? 6 : 8; - if (collisionBox.segments.empty() || collisionBox.segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) { - collisionBox.segments.emplace_back(collisionBox.vertices.vertexSize(), collisionBox.lines.indexSize()); + if (collisionBuffer.segments.empty() || collisionBuffer.segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) { + collisionBuffer.segments.emplace_back(collisionBuffer.vertices.vertexSize(), + feature.alongLine? bucket.collisionCircle.triangles.indexSize() : bucket.collisionBox.lines.indexSize()); } - auto& segment = collisionBox.segments.back(); + auto& segment = collisionBuffer.segments.back(); uint16_t index = segment.vertexLength; - collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tl, maxZoom, placementZoom)); - collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tr, maxZoom, placementZoom)); - collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, br, maxZoom, placementZoom)); - collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, bl, maxZoom, placementZoom)); - - collisionBox.lines.emplace_back(index + 0, index + 1); - collisionBox.lines.emplace_back(index + 1, index + 2); - collisionBox.lines.emplace_back(index + 2, index + 3); - collisionBox.lines.emplace_back(index + 3, index + 0); + collisionBuffer.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tl)); + collisionBuffer.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tr)); + collisionBuffer.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, br)); + collisionBuffer.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, bl)); + + // 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 = CollisionBoxDynamicAttributes::vertex(false, false); + collisionBuffer.dynamicVertices.emplace_back(dynamicVertex); + collisionBuffer.dynamicVertices.emplace_back(dynamicVertex); + collisionBuffer.dynamicVertices.emplace_back(dynamicVertex); + collisionBuffer.dynamicVertices.emplace_back(dynamicVertex); + + if (feature.alongLine) { + bucket.collisionCircle.triangles.emplace_back(index, index + 1, index + 2); + bucket.collisionCircle.triangles.emplace_back(index, index + 2, index + 3); + } else { + bucket.collisionBox.lines.emplace_back(index + 0, index + 1); + bucket.collisionBox.lines.emplace_back(index + 1, index + 2); + bucket.collisionBox.lines.emplace_back(index + 2, index + 3); + bucket.collisionBox.lines.emplace_back(index + 3, index + 0); + } segment.vertexLength += vertexLength; segment.indexLength += indexLength; diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp index 90f5b3c91d..43b577859f 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -16,7 +16,6 @@ namespace mbgl { class BucketParameters; -class CollisionTile; class SymbolBucket; class Anchor; class RenderLayer; @@ -37,13 +36,16 @@ public: void prepare(const GlyphMap&, const GlyphPositions&, const ImageMap&, const ImagePositions&); - std::unique_ptr<SymbolBucket> place(CollisionTile&); + std::unique_ptr<SymbolBucket> place(const bool showCollisionBoxes); bool hasSymbolInstances() const; std::map<std::string, std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>> layerPaintProperties; + const std::string bucketLeaderID; + std::vector<SymbolInstance> symbolInstances; + private: void addFeature(const size_t, const SymbolFeature&, @@ -54,23 +56,19 @@ private: bool anchorIsTooClose(const std::u16string& text, const float repeatDistance, const Anchor&); std::map<std::u16string, std::vector<Anchor>> compareText; - void addToDebugBuffers(CollisionTile&, SymbolBucket&); + void addToDebugBuffers(SymbolBucket&); // Adds placed items to the buffer. template <typename Buffer> - void addSymbol(Buffer&, + size_t addSymbol(Buffer&, const Range<float> sizeData, const SymbolQuad&, - float scale, - const bool keepUpright, - const style::SymbolPlacementType, const Anchor& labelAnchor, PlacedSymbol& placedSymbol); // Stores the layer so that we can hold on to GeometryTileFeature instances in SymbolFeature, // which may reference data from this object. const std::unique_ptr<GeometryTileLayer> sourceLayer; - const std::string bucketName; const float overscaling; const float zoom; const MapMode mode; @@ -87,7 +85,6 @@ private: style::TextSize::UnevaluatedType textSize; style::IconSize::UnevaluatedType iconSize; - std::vector<SymbolInstance> symbolInstances; std::vector<SymbolFeature> features; BiDi bidi; // Consider moving this up to geometry tile worker to reduce reinstantiation costs; use of BiDi/ubiditransform object must be constrained to one thread diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp index d97bfb1ac0..ef669c6e19 100644 --- a/src/mbgl/layout/symbol_projection.cpp +++ b/src/mbgl/layout/symbol_projection.cpp @@ -3,7 +3,6 @@ #include <mbgl/renderer/render_tile.hpp> #include <mbgl/renderer/buckets/symbol_bucket.hpp> #include <mbgl/renderer/layers/render_symbol_layer.hpp> -#include <mbgl/renderer/frame_history.hpp> #include <mbgl/util/optional.hpp> #include <mbgl/util/math.hpp> @@ -93,9 +92,6 @@ namespace mbgl { return m; } - - typedef std::pair<Point<float>,float> PointAndCameraDistance; - PointAndCameraDistance project(const Point<float>& point, const mat4& matrix) { vec4 pos = {{ point.x, point.y, 0, 1 }}; matrix::transformMat4(pos, pos, matrix); @@ -114,7 +110,7 @@ namespace mbgl { } } - bool isVisible(const vec4& anchorPos, const float placementZoom, const std::array<double, 2>& clippingBuffer, const FrameHistory& frameHistory) { + bool isVisible(const vec4& anchorPos, const std::array<double, 2>& clippingBuffer) { const float x = anchorPos[0] / anchorPos[3]; const float y = anchorPos[1] / anchorPos[3]; const bool inPaddedViewport = ( @@ -122,12 +118,12 @@ namespace mbgl { x <= clippingBuffer[0] && y >= -clippingBuffer[1] && y <= clippingBuffer[1]); - return inPaddedViewport && frameHistory.isVisible(placementZoom); + return inPaddedViewport; } - void addDynamicAttributes(const Point<float>& anchorPoint, const float angle, const float placementZoom, + void addDynamicAttributes(const Point<float>& anchorPoint, const float angle, gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) { - auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(anchorPoint, angle, placementZoom); + auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(anchorPoint, angle); dynamicVertexArray.emplace_back(dynamicVertex); dynamicVertexArray.emplace_back(dynamicVertex); dynamicVertexArray.emplace_back(dynamicVertex); @@ -137,20 +133,15 @@ namespace mbgl { void hideGlyphs(size_t numGlyphs, gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) { const Point<float> offscreenPoint = { -INFINITY, -INFINITY }; for (size_t i = 0; i < numGlyphs; i++) { - addDynamicAttributes(offscreenPoint, 0, 25, dynamicVertexArray); + addDynamicAttributes(offscreenPoint, 0, dynamicVertexArray); } } - struct PlacedGlyph { - PlacedGlyph(Point<float> point_, float angle_) : point(point_), angle(angle_) {} - Point<float> point; - float angle; - }; - enum PlacementResult { OK, NotEnoughRoom, - NeedsFlipping + NeedsFlipping, + UseVertical }; Point<float> projectTruncatedLineSegment(const Point<float>& previousTilePoint, const Point<float>& currentTilePoint, const Point<float>& previousProjectedPoint, const float minimumLength, const mat4& projectionMatrix) { @@ -165,7 +156,7 @@ namespace mbgl { } optional<PlacedGlyph> placeGlyphAlongLine(const float offsetX, const float lineOffsetX, const float lineOffsetY, const bool flip, - const Point<float>& projectedAnchorPoint, const Point<float>& tileAnchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const mat4& labelPlaneMatrix) { + const Point<float>& projectedAnchorPoint, const Point<float>& tileAnchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const std::vector<float>& tileDistances, const mat4& labelPlaneMatrix, const bool returnTileDistance) { const float combinedOffsetX = flip ? offsetX - lineOffsetX : @@ -185,6 +176,7 @@ namespace mbgl { int32_t currentIndex = dir > 0 ? anchorSegment : anchorSegment + 1; + const int32_t initialIndex = currentIndex; Point<float> current = projectedAnchorPoint; Point<float> prev = projectedAnchorPoint; float distanceToPrev = 0.0; @@ -195,7 +187,9 @@ namespace mbgl { currentIndex += dir; // offset does not fit on the projected line - if (currentIndex < 0 || currentIndex >= static_cast<int32_t>(line.size())) return {}; + if (currentIndex < 0 || currentIndex >= static_cast<int32_t>(line.size())) { + return {}; + } prev = current; PointAndCameraDistance projection = project(convertPoint<float>(line.at(currentIndex)), labelPlaneMatrix); @@ -225,7 +219,66 @@ namespace mbgl { const float segmentAngle = angle + std::atan2(current.y - prev.y, current.x - prev.x); - return {{ p, segmentAngle }}; + return {{ + p, + segmentAngle, + returnTileDistance ? + TileDistance( + (currentIndex - dir) == initialIndex ? 0 : tileDistances[currentIndex - dir], + absOffsetX - distanceToPrev + ) : + optional<TileDistance>() + }}; + } + + optional<std::pair<PlacedGlyph, PlacedGlyph>> placeFirstAndLastGlyph(const float fontScale, + const float lineOffsetX, + const float lineOffsetY, + const bool flip, + const Point<float>& anchorPoint, + const Point<float>& tileAnchorPoint, + const PlacedSymbol& symbol, + const mat4& labelPlaneMatrix, + const bool returnTileDistance) { + if (symbol.glyphOffsets.empty()) { + assert(false); + return optional<std::pair<PlacedGlyph, PlacedGlyph>>(); + } + + const float firstGlyphOffset = symbol.glyphOffsets.front(); + const float lastGlyphOffset = symbol.glyphOffsets.back();; + + optional<PlacedGlyph> firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, returnTileDistance); + if (!firstPlacedGlyph) + return optional<std::pair<PlacedGlyph, PlacedGlyph>>(); + + optional<PlacedGlyph> lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, returnTileDistance); + if (!lastPlacedGlyph) + return optional<std::pair<PlacedGlyph, PlacedGlyph>>(); + + return std::make_pair(*firstPlacedGlyph, *lastPlacedGlyph); + } + + optional<PlacementResult> requiresOrientationChange(const WritingModeType writingModes, const Point<float>& firstPoint, const Point<float>& lastPoint, const float aspectRatio) { + if (writingModes == (WritingModeType::Horizontal | WritingModeType::Vertical)) { + // On top of choosing whether to flip, choose whether to render this version of the glyphs or the alternate + // vertical glyphs. We can't just filter out vertical glyphs in the horizontal range because the horizontal + // and vertical versions can have slightly different projections which could lead to angles where both or + // neither showed. + auto rise = std::abs(lastPoint.y - firstPoint.y); + auto run = std::abs(lastPoint.x - firstPoint.x) * aspectRatio; + if (rise > run) { + return PlacementResult::UseVertical; + } + } + + if ((writingModes == WritingModeType::Vertical) ? + (firstPoint.y < lastPoint.y) : + (firstPoint.x > lastPoint.x)) { + // Includes "horizontalOnly" case for labels without vertical glyphs + return PlacementResult::NeedsFlipping; + } + return {}; } PlacementResult placeGlyphsAlongLine(const PlacedSymbol& symbol, @@ -236,7 +289,8 @@ namespace mbgl { const mat4& labelPlaneMatrix, const mat4& glCoordMatrix, gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray, - const Point<float>& projectedAnchorPoint) { + const Point<float>& projectedAnchorPoint, + const float aspectRatio) { const float fontScale = fontSize / 24.0; const float lineOffsetX = symbol.lineOffset[0] * fontSize; const float lineOffsetY = symbol.lineOffset[1] * fontSize; @@ -244,33 +298,30 @@ namespace mbgl { std::vector<PlacedGlyph> placedGlyphs; if (symbol.glyphOffsets.size() > 1) { - const float firstGlyphOffset = symbol.glyphOffsets.front(); - const float lastGlyphOffset = symbol.glyphOffsets.back(); - - optional<PlacedGlyph> firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); - if (!firstPlacedGlyph) - return PlacementResult::NotEnoughRoom; - - optional<PlacedGlyph> lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); - if (!lastPlacedGlyph) + const optional<std::pair<PlacedGlyph, PlacedGlyph>> firstAndLastGlyph = + placeFirstAndLastGlyph(fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol, labelPlaneMatrix, false); + if (!firstAndLastGlyph) { return PlacementResult::NotEnoughRoom; + } - const Point<float> firstPoint = project(firstPlacedGlyph->point, glCoordMatrix).first; - const Point<float> lastPoint = project(lastPlacedGlyph->point, glCoordMatrix).first; + const Point<float> firstPoint = project(firstAndLastGlyph->first.point, glCoordMatrix).first; + const Point<float> lastPoint = project(firstAndLastGlyph->second.point, glCoordMatrix).first; - if (keepUpright && !flip && - (symbol.useVerticalMode ? firstPoint.y < lastPoint.y : firstPoint.x > lastPoint.x)) { - return PlacementResult::NeedsFlipping; + if (keepUpright && !flip) { + auto orientationChange = requiresOrientationChange(symbol.writingModes, firstPoint, lastPoint, aspectRatio); + if (orientationChange) { + return *orientationChange; + } } - placedGlyphs.push_back(*firstPlacedGlyph); + placedGlyphs.push_back(firstAndLastGlyph->first); for (size_t glyphIndex = 1; glyphIndex < symbol.glyphOffsets.size() - 1; glyphIndex++) { const float glyphOffsetX = symbol.glyphOffsets[glyphIndex]; // Since first and last glyph fit on the line, we're sure that the rest of the glyphs can be placed - auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); + auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, false); placedGlyphs.push_back(*placedGlyph); } - placedGlyphs.push_back(*lastPlacedGlyph); + placedGlyphs.push_back(firstAndLastGlyph->second); } else if (symbol.glyphOffsets.size() == 1) { // Only a single glyph to place // So, determine whether to flip based on projected angle of the line segment it's on @@ -285,13 +336,14 @@ namespace mbgl { projectedVertex.first : projectTruncatedLineSegment(symbol.anchorPoint,tileSegmentEnd, a, 1, posMatrix); - if (symbol.useVerticalMode ? b.y > a.y : b.x < a.x) { - return PlacementResult::NeedsFlipping; + auto orientationChange = requiresOrientationChange(symbol.writingModes, a, b, aspectRatio); + if (orientationChange) { + return *orientationChange; } } const float glyphOffsetX = symbol.glyphOffsets.front(); optional<PlacedGlyph> singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, - symbol.line, labelPlaneMatrix); + symbol.line, symbol.tileDistances, labelPlaneMatrix, false); if (!singleGlyph) return PlacementResult::NotEnoughRoom; @@ -301,7 +353,7 @@ namespace mbgl { // The number of placedGlyphs must equal the number of glyphOffsets, which must correspond to the number of glyph vertices // There may be 0 glyphs here, if a label consists entirely of glyphs that have 0x0 dimensions for (auto& placedGlyph : placedGlyphs) { - addDynamicAttributes(placedGlyph.point, placedGlyph.angle, symbol.placementZoom, dynamicVertexArray); + addDynamicAttributes(placedGlyph.point, placedGlyph.angle, dynamicVertexArray); } return PlacementResult::OK; @@ -310,7 +362,7 @@ namespace mbgl { void reprojectLineLabels(gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray, const std::vector<PlacedSymbol>& placedSymbols, const mat4& posMatrix, const style::SymbolPropertyValues& values, - const RenderTile& tile, const SymbolSizeBinder& sizeBinder, const TransformState& state, const FrameHistory& frameHistory) { + const RenderTile& tile, const SymbolSizeBinder& sizeBinder, const TransformState& state) { const ZoomEvaluatedSize partiallyEvaluatedSize = sizeBinder.evaluateForZoom(state.getZoom()); @@ -326,19 +378,31 @@ namespace mbgl { const mat4 glCoordMatrix = getGlCoordMatrix(posMatrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits); dynamicVertexArray.clear(); + + bool useVertical = false; for (auto& placedSymbol : placedSymbols) { + // Don't do calculations for vertical glyphs unless the previous symbol was horizontal + // and we determined that vertical glyphs were necessary. + // Also don't do calculations for symbols that are collided and fully faded out + if (placedSymbol.hidden || (placedSymbol.writingModes == WritingModeType::Vertical && !useVertical)) { + hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray); + continue; + } + // Awkward... but we're counting on the paired "vertical" symbol coming immediately after its horizontal counterpart + useVertical = false; + vec4 anchorPos = {{ placedSymbol.anchorPoint.x, placedSymbol.anchorPoint.y, 0, 1 }}; matrix::transformMat4(anchorPos, anchorPos, posMatrix); // Don't bother calculating the correct point for invisible labels. - if (!isVisible(anchorPos, placedSymbol.placementZoom, clippingBuffer, frameHistory)) { + if (!isVisible(anchorPos, clippingBuffer)) { hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray); continue; } const float cameraToAnchorDistance = anchorPos[3]; - const float perspectiveRatio = 1 + 0.5 * ((cameraToAnchorDistance / state.getCameraToCenterDistance()) - 1.0); + const float perspectiveRatio = 0.5 + 0.5 * (cameraToAnchorDistance / state.getCameraToCenterDistance()); const float fontSize = evaluateSizeForFeature(partiallyEvaluatedSize, placedSymbol); const float pitchScaledFontSize = values.pitchAlignment == style::AlignmentType::Map ? @@ -347,11 +411,13 @@ namespace mbgl { const Point<float> anchorPoint = project(placedSymbol.anchorPoint, labelPlaneMatrix).first; - PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint); + PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio()); + + useVertical = placeUnflipped == PlacementResult::UseVertical; - if (placeUnflipped == PlacementResult::NotEnoughRoom || + if (placeUnflipped == PlacementResult::NotEnoughRoom || useVertical || (placeUnflipped == PlacementResult::NeedsFlipping && - placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint) == PlacementResult::NotEnoughRoom)) { + placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio()) == PlacementResult::NotEnoughRoom)) { hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray); } } diff --git a/src/mbgl/layout/symbol_projection.hpp b/src/mbgl/layout/symbol_projection.hpp index 2652fe7ace..3e57d162fd 100644 --- a/src/mbgl/layout/symbol_projection.hpp +++ b/src/mbgl/layout/symbol_projection.hpp @@ -8,18 +8,56 @@ namespace mbgl { class TransformState; class RenderTile; - class FrameHistory; class SymbolSizeBinder; class PlacedSymbol; namespace style { class SymbolPropertyValues; } // end namespace style + + struct TileDistance { + TileDistance(float prevTileDistance_, float lastSegmentViewportDistance_) + : prevTileDistance(prevTileDistance_), lastSegmentViewportDistance(lastSegmentViewportDistance_) + {} + float prevTileDistance; + float lastSegmentViewportDistance; + }; + + struct PlacedGlyph { + PlacedGlyph() = default; + PlacedGlyph(Point<float> point_, float angle_, optional<TileDistance> tileDistance_) + : point(point_), angle(angle_), tileDistance(std::move(tileDistance_)) + {} + PlacedGlyph(PlacedGlyph&& other) noexcept + : point(std::move(other.point)), angle(other.angle), tileDistance(std::move(other.tileDistance)) + {} + PlacedGlyph(const PlacedGlyph& other) + : point(std::move(other.point)), angle(other.angle), tileDistance(std::move(other.tileDistance)) + {} + Point<float> point; + float angle; + optional<TileDistance> tileDistance; + }; + + float evaluateSizeForFeature(const ZoomEvaluatedSize& zoomEvaluatedSize, const PlacedSymbol& placedSymbol); mat4 getLabelPlaneMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits); mat4 getGlCoordMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits); + + using PointAndCameraDistance = std::pair<Point<float>,float>; + PointAndCameraDistance project(const Point<float>& point, const mat4& matrix); void reprojectLineLabels(gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>&, const std::vector<PlacedSymbol>&, const mat4& posMatrix, const style::SymbolPropertyValues&, - const RenderTile&, const SymbolSizeBinder& sizeBinder, const TransformState&, const FrameHistory& frameHistory); + const RenderTile&, const SymbolSizeBinder& sizeBinder, const TransformState&); + + optional<std::pair<PlacedGlyph, PlacedGlyph>> placeFirstAndLastGlyph(const float fontScale, + const float lineOffsetX, + const float lineOffsetY, + const bool flip, + const Point<float>& anchorPoint, + const Point<float>& tileAnchorPoint, + const PlacedSymbol& symbol, + const mat4& labelPlaneMatrix, + const bool returnTileDistance); } // end namespace mbgl |