[core] Switch from background to foreground placement
- Background placement code now just generates static symbol buffers - Don't render GeometryTiles until their symbols are loaded. This is necessary for the CrossTileSymbolIndex to successfully prevent flicker. - SymbolInstances are transferred to SymbolBucket for use on foreground during collision detection - Symbols are sorted on foreground by sorting their index buffer but leaving vertex buffers intact (only works within one segment) - Vertical glyphs are generated at same time as horizontal glyphs. `reprojectLineLabels` chooses which one to use at render time and hides the other. - Icons are now always represented with a single collision box, even if they're placed along a line (this means their rotation alignment may be wrong, but the approach of representing them with multiple collision boxes wasn't very accurate either). - Generate vertices for new debug collision boxes and collision circles - Only add symbols within tile boundaries (reduces work, avoids double-draw) - Update symbol_projection.cpp to support line label projection calls from CollisionIndex.
This commit is contained in:
@@ -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,11 +18,12 @@ 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_),
|
||||
@@ -31,25 +31,22 @@ SymbolInstance::SymbolInstance(Anchor& anchor_,
|
||||
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, SymbolPlacementType::Point, 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);
|
||||
}
|
||||
|
||||
if (shapedTextOrientations.first && shapedTextOrientations.second) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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()),
|
||||
: bucketName(layers.at(0)->getID()),
|
||||
sourceLayer(std::move(sourceLayer_)),
|
||||
overscaling(parameters.tileID.overscaleFactor()),
|
||||
zoom(parameters.tileID.overscaledZ),
|
||||
mode(parameters.mode),
|
||||
@@ -179,7 +178,8 @@ bool SymbolLayout::hasSymbolInstances() const {
|
||||
}
|
||||
|
||||
void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyphPositions,
|
||||
const ImageMap& imageMap, const ImagePositions& imagePositions) {
|
||||
const ImageMap& imageMap, const ImagePositions& imagePositions,
|
||||
const OverscaledTileID& tileID, const std::string& sourceID) {
|
||||
const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map &&
|
||||
layout.get<SymbolPlacement>() == SymbolPlacementType::Line;
|
||||
|
||||
@@ -248,7 +248,7 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph
|
||||
|
||||
// if either shapedText or icon position is present, add the feature
|
||||
if (shapedTextOrientations.first || shapedIcon) {
|
||||
addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap);
|
||||
addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap, tileID, sourceID);
|
||||
}
|
||||
|
||||
feature.geometry.clear();
|
||||
@@ -261,7 +261,9 @@ void SymbolLayout::addFeature(const std::size_t index,
|
||||
const SymbolFeature& feature,
|
||||
const std::pair<Shaping, Shaping>& shapedTextOrientations,
|
||||
optional<PositionedIcon> shapedIcon,
|
||||
const GlyphPositionMap& glyphPositionMap) {
|
||||
const GlyphPositionMap& glyphPositionMap,
|
||||
const OverscaledTileID& tileID,
|
||||
const std::string& sourceID) {
|
||||
const float minScale = 0.5f;
|
||||
const float glyphSize = 24.0f;
|
||||
|
||||
@@ -288,12 +290,10 @@ 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(), bucketName, symbolInstances.size(),
|
||||
sourceID, tileID.canonical.z, tileID.canonical.x, tileID.canonical.y);
|
||||
|
||||
auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) {
|
||||
// https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers
|
||||
@@ -314,14 +314,16 @@ 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);
|
||||
// TODO set this to make api-gl work
|
||||
const bool singleTileMode = false && mode == MapMode::Still;
|
||||
if (singleTileMode || 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 ? *feature.text : std::u16string{}, overscaling);
|
||||
}
|
||||
};
|
||||
|
||||
const auto& type = feature.getType();
|
||||
@@ -392,108 +394,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, 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 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();
|
||||
|
||||
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);
|
||||
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 +490,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 +511,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 +527,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 +549,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));
|
||||
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));
|
||||
|
||||
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);
|
||||
// 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;
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
namespace mbgl {
|
||||
|
||||
class BucketParameters;
|
||||
class CollisionTile;
|
||||
class SymbolBucket;
|
||||
class Anchor;
|
||||
class RenderLayer;
|
||||
@@ -35,42 +34,44 @@ public:
|
||||
GlyphDependencies&);
|
||||
|
||||
void prepare(const GlyphMap&, const GlyphPositions&,
|
||||
const ImageMap&, const ImagePositions&);
|
||||
const ImageMap&, const ImagePositions&,
|
||||
const OverscaledTileID&, const std::string&);
|
||||
|
||||
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 bucketName;
|
||||
std::vector<SymbolInstance> symbolInstances;
|
||||
|
||||
private:
|
||||
void addFeature(const size_t,
|
||||
const SymbolFeature&,
|
||||
const std::pair<Shaping, Shaping>& shapedTextOrientations,
|
||||
optional<PositionedIcon> shapedIcon,
|
||||
const GlyphPositionMap&);
|
||||
const GlyphPositionMap&,
|
||||
const OverscaledTileID&,
|
||||
const std::string&);
|
||||
|
||||
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 +88,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
|
||||
|
||||
@@ -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,62 @@ 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) {
|
||||
|
||||
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 +285,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 +294,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)
|
||||
const optional<std::pair<PlacedGlyph,PlacedGlyph>> firstAndLastGlyph =
|
||||
placeFirstAndLastGlyph(fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol, labelPlaneMatrix, false);
|
||||
if (!firstAndLastGlyph) {
|
||||
return PlacementResult::NotEnoughRoom;
|
||||
|
||||
optional<PlacedGlyph> lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix);
|
||||
if (!lastPlacedGlyph)
|
||||
return PlacementResult::NotEnoughRoom;
|
||||
|
||||
const Point<float> firstPoint = project(firstPlacedGlyph->point, glCoordMatrix).first;
|
||||
const Point<float> lastPoint = project(lastPlacedGlyph->point, glCoordMatrix).first;
|
||||
|
||||
if (keepUpright && !flip &&
|
||||
(symbol.useVerticalMode ? firstPoint.y < lastPoint.y : firstPoint.x > lastPoint.x)) {
|
||||
return PlacementResult::NeedsFlipping;
|
||||
}
|
||||
|
||||
placedGlyphs.push_back(*firstPlacedGlyph);
|
||||
const Point<float> firstPoint = project(firstAndLastGlyph->first.point, glCoordMatrix).first;
|
||||
const Point<float> lastPoint = project(firstAndLastGlyph->second.point, glCoordMatrix).first;
|
||||
|
||||
if (keepUpright && !flip) {
|
||||
auto orientationChange = requiresOrientationChange(symbol.writingModes, firstPoint, lastPoint, aspectRatio);
|
||||
if (orientationChange) {
|
||||
return *orientationChange;
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
// Only a single glyph to place
|
||||
// So, determine whether to flip based on projected angle of the line segment it's on
|
||||
@@ -285,14 +332,15 @@ 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;
|
||||
}
|
||||
}
|
||||
assert(symbol.glyphOffsets.size() == 1); // We are relying on SymbolInstance.hasText filtering out symbols without any glyphs at all
|
||||
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;
|
||||
|
||||
@@ -300,7 +348,7 @@ namespace mbgl {
|
||||
}
|
||||
|
||||
for (auto& placedGlyph : placedGlyphs) {
|
||||
addDynamicAttributes(placedGlyph.point, placedGlyph.angle, symbol.placementZoom, dynamicVertexArray);
|
||||
addDynamicAttributes(placedGlyph.point, placedGlyph.angle, dynamicVertexArray);
|
||||
}
|
||||
|
||||
return PlacementResult::OK;
|
||||
@@ -309,7 +357,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());
|
||||
|
||||
@@ -325,19 +373,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 ?
|
||||
@@ -346,11 +406,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,18 +8,48 @@ 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(Point<float> point_, float angle_, optional<TileDistance> tileDistance_)
|
||||
: point(point_), angle(angle_), tileDistance(std::move(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
|
||||
|
||||
@@ -16,10 +16,14 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo
|
||||
const style::DataDrivenPropertyValue<float>& iconSize,
|
||||
float zoom,
|
||||
bool sdfIcons_,
|
||||
bool iconsNeedLinear_)
|
||||
bool iconsNeedLinear_,
|
||||
bool sortFeaturesByY_,
|
||||
const std::vector<SymbolInstance>&& symbolInstances_)
|
||||
: layout(std::move(layout_)),
|
||||
sdfIcons(sdfIcons_),
|
||||
iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()),
|
||||
sortFeaturesByY(sortFeaturesByY_),
|
||||
symbolInstances(std::move(symbolInstances_)),
|
||||
textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())),
|
||||
iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())) {
|
||||
|
||||
@@ -36,28 +40,84 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo
|
||||
|
||||
void SymbolBucket::upload(gl::Context& context) {
|
||||
if (hasTextData()) {
|
||||
text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices));
|
||||
text.dynamicVertexBuffer = context.createVertexBuffer(std::move(text.dynamicVertices), gl::BufferUsage::StreamDraw);
|
||||
text.indexBuffer = context.createIndexBuffer(std::move(text.triangles));
|
||||
if (!staticUploaded) {
|
||||
text.indexBuffer = context.createIndexBuffer(std::move(text.triangles), sortFeaturesByY ? gl::BufferUsage::StreamDraw : gl::BufferUsage::StaticDraw);
|
||||
text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices));
|
||||
} else if (!sortUploaded) {
|
||||
context.updateIndexBuffer(*text.indexBuffer, std::move(text.triangles));
|
||||
}
|
||||
|
||||
if (!dynamicUploaded) {
|
||||
text.dynamicVertexBuffer = context.createVertexBuffer(std::move(text.dynamicVertices), gl::BufferUsage::StreamDraw);
|
||||
}
|
||||
if (!placementChangesUploaded) {
|
||||
if (!text.opacityVertexBuffer) {
|
||||
text.opacityVertexBuffer = context.createVertexBuffer(std::move(text.opacityVertices), gl::BufferUsage::StreamDraw);
|
||||
} else {
|
||||
context.updateVertexBuffer(*text.opacityVertexBuffer, std::move(text.opacityVertices));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasIconData()) {
|
||||
icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices));
|
||||
icon.dynamicVertexBuffer = context.createVertexBuffer(std::move(icon.dynamicVertices), gl::BufferUsage::StreamDraw);
|
||||
icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles));
|
||||
if (!staticUploaded) {
|
||||
icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles), sortFeaturesByY ? gl::BufferUsage::StreamDraw : gl::BufferUsage::StaticDraw);
|
||||
icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices));
|
||||
} else if (!sortUploaded) {
|
||||
context.updateIndexBuffer(*icon.indexBuffer, std::move(icon.triangles));
|
||||
}
|
||||
if (!dynamicUploaded) {
|
||||
icon.dynamicVertexBuffer = context.createVertexBuffer(std::move(icon.dynamicVertices), gl::BufferUsage::StreamDraw);
|
||||
}
|
||||
if (!placementChangesUploaded) {
|
||||
if (!icon.opacityVertexBuffer) {
|
||||
icon.opacityVertexBuffer = context.createVertexBuffer(std::move(icon.opacityVertices), gl::BufferUsage::StreamDraw);
|
||||
} else {
|
||||
context.updateVertexBuffer(*icon.opacityVertexBuffer, std::move(icon.opacityVertices));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!collisionBox.vertices.empty()) {
|
||||
collisionBox.vertexBuffer = context.createVertexBuffer(std::move(collisionBox.vertices));
|
||||
collisionBox.indexBuffer = context.createIndexBuffer(std::move(collisionBox.lines));
|
||||
if (hasCollisionBoxData()) {
|
||||
if (!staticUploaded) {
|
||||
collisionBox.indexBuffer = context.createIndexBuffer(std::move(collisionBox.lines));
|
||||
collisionBox.vertexBuffer = context.createVertexBuffer(std::move(collisionBox.vertices));
|
||||
}
|
||||
if (!placementChangesUploaded) {
|
||||
if (!collisionBox.dynamicVertexBuffer) {
|
||||
collisionBox.dynamicVertexBuffer = context.createVertexBuffer(std::move(collisionBox.dynamicVertices), gl::BufferUsage::StreamDraw);
|
||||
} else {
|
||||
context.updateVertexBuffer(*collisionBox.dynamicVertexBuffer, std::move(collisionBox.dynamicVertices));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasCollisionCircleData()) {
|
||||
if (!staticUploaded) {
|
||||
collisionCircle.indexBuffer = context.createIndexBuffer(std::move(collisionCircle.triangles));
|
||||
collisionCircle.vertexBuffer = context.createVertexBuffer(std::move(collisionCircle.vertices));
|
||||
}
|
||||
if (!placementChangesUploaded) {
|
||||
if (!collisionCircle.dynamicVertexBuffer) {
|
||||
collisionCircle.dynamicVertexBuffer = context.createVertexBuffer(std::move(collisionCircle.dynamicVertices), gl::BufferUsage::StreamDraw);
|
||||
} else {
|
||||
context.updateVertexBuffer(*collisionCircle.dynamicVertexBuffer, std::move(collisionCircle.dynamicVertices));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& pair : paintPropertyBinders) {
|
||||
pair.second.first.upload(context);
|
||||
pair.second.second.upload(context);
|
||||
if (!staticUploaded) {
|
||||
for (auto& pair : paintPropertyBinders) {
|
||||
pair.second.first.upload(context);
|
||||
pair.second.second.upload(context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uploaded = true;
|
||||
staticUploaded = true;
|
||||
placementChangesUploaded = true;
|
||||
dynamicUploaded = true;
|
||||
sortUploaded = true;
|
||||
}
|
||||
|
||||
bool SymbolBucket::hasData() const {
|
||||
@@ -76,4 +136,82 @@ bool SymbolBucket::hasCollisionBoxData() const {
|
||||
return !collisionBox.segments.empty();
|
||||
}
|
||||
|
||||
bool SymbolBucket::hasCollisionCircleData() const {
|
||||
return !collisionCircle.segments.empty();
|
||||
}
|
||||
|
||||
void SymbolBucket::updateOpacity() {
|
||||
placementChangesUploaded = false;
|
||||
uploaded = false;
|
||||
}
|
||||
|
||||
void addPlacedSymbol(gl::IndexVector<gl::Triangles>& triangles, const PlacedSymbol& placedSymbol) {
|
||||
auto endIndex = placedSymbol.vertexStartIndex + placedSymbol.glyphOffsets.size() * 4;
|
||||
for (auto vertexIndex = placedSymbol.vertexStartIndex; vertexIndex < endIndex; vertexIndex += 4) {
|
||||
triangles.emplace_back(vertexIndex + 0, vertexIndex + 1, vertexIndex + 2);
|
||||
triangles.emplace_back(vertexIndex + 1, vertexIndex + 2, vertexIndex + 3);
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolBucket::sortFeatures(const float angle) {
|
||||
if (!sortFeaturesByY) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sortedAngle && *sortedAngle == angle) {
|
||||
return;
|
||||
}
|
||||
|
||||
sortedAngle = angle;
|
||||
|
||||
// The current approach to sorting doesn't sort across segments so don't try.
|
||||
// Sorting within segments separately seemed not to be worth the complexity.
|
||||
if (text.segments.size() > 1 || icon.segments.size() > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
sortUploaded = false;
|
||||
uploaded = false;
|
||||
|
||||
// If the symbols are allowed to overlap sort them by their vertical screen position.
|
||||
// The index array buffer is rewritten to reference the (unchanged) vertices in the
|
||||
// sorted order.
|
||||
|
||||
// To avoid sorting the actual symbolInstance array we sort an array of indexes.
|
||||
std::vector<size_t> symbolInstanceIndexes;
|
||||
for (size_t i = 0; i < symbolInstances.size(); i++) {
|
||||
symbolInstanceIndexes.push_back(i);
|
||||
}
|
||||
|
||||
const float sin = std::sin(angle);
|
||||
const float cos = std::cos(angle);
|
||||
|
||||
std::sort(symbolInstanceIndexes.begin(), symbolInstanceIndexes.end(), [sin, cos, this](size_t &aIndex, size_t &bIndex) {
|
||||
const SymbolInstance& a = symbolInstances[aIndex];
|
||||
const SymbolInstance& b = symbolInstances[bIndex];
|
||||
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;
|
||||
});
|
||||
|
||||
text.triangles.clear();
|
||||
icon.triangles.clear();
|
||||
|
||||
for (auto i : symbolInstanceIndexes) {
|
||||
const SymbolInstance& symbolInstance = symbolInstances[i];
|
||||
|
||||
if (symbolInstance.placedTextIndex) {
|
||||
addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedTextIndex]);
|
||||
}
|
||||
if (symbolInstance.placedVerticalTextIndex) {
|
||||
addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedVerticalTextIndex]);
|
||||
}
|
||||
if (symbolInstance.placedIconIndex) {
|
||||
addPlacedSymbol(icon.triangles, icon.placedSymbols[*symbolInstance.placedIconIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <mbgl/text/glyph_range.hpp>
|
||||
#include <mbgl/style/layers/symbol_layer_properties.hpp>
|
||||
#include <mbgl/layout/symbol_feature.hpp>
|
||||
#include <mbgl/layout/symbol_instance.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -18,18 +19,22 @@ namespace mbgl {
|
||||
class PlacedSymbol {
|
||||
public:
|
||||
PlacedSymbol(Point<float> anchorPoint_, uint16_t segment_, float lowerSize_, float upperSize_,
|
||||
std::array<float, 2> lineOffset_, float placementZoom_, bool useVerticalMode_, GeometryCoordinates line_) :
|
||||
std::array<float, 2> lineOffset_, WritingModeType writingModes_, GeometryCoordinates line_, std::vector<float> tileDistances_) :
|
||||
anchorPoint(anchorPoint_), segment(segment_), lowerSize(lowerSize_), upperSize(upperSize_),
|
||||
lineOffset(lineOffset_), placementZoom(placementZoom_), useVerticalMode(useVerticalMode_), line(std::move(line_)) {}
|
||||
lineOffset(lineOffset_), writingModes(writingModes_), line(std::move(line_)), tileDistances(std::move(tileDistances_)), hidden(false), vertexStartIndex(0)
|
||||
{
|
||||
}
|
||||
Point<float> anchorPoint;
|
||||
uint16_t segment;
|
||||
float lowerSize;
|
||||
float upperSize;
|
||||
std::array<float, 2> lineOffset;
|
||||
float placementZoom;
|
||||
bool useVerticalMode;
|
||||
WritingModeType writingModes;
|
||||
GeometryCoordinates line;
|
||||
std::vector<float> tileDistances;
|
||||
std::vector<float> glyphOffsets;
|
||||
bool hidden;
|
||||
size_t vertexStartIndex;
|
||||
};
|
||||
|
||||
class SymbolBucket : public Bucket {
|
||||
@@ -40,17 +45,33 @@ public:
|
||||
const style::DataDrivenPropertyValue<float>& iconSize,
|
||||
float zoom,
|
||||
bool sdfIcons,
|
||||
bool iconsNeedLinear);
|
||||
bool iconsNeedLinear,
|
||||
bool sortFeaturesByY,
|
||||
const std::vector<SymbolInstance>&&);
|
||||
|
||||
void upload(gl::Context&) override;
|
||||
bool hasData() const override;
|
||||
bool hasTextData() const;
|
||||
bool hasIconData() const;
|
||||
bool hasCollisionBoxData() const;
|
||||
bool hasCollisionCircleData() const;
|
||||
|
||||
void updateOpacity();
|
||||
void sortFeatures(const float angle);
|
||||
|
||||
const style::SymbolLayoutProperties::PossiblyEvaluated layout;
|
||||
const bool sdfIcons;
|
||||
const bool iconsNeedLinear;
|
||||
const bool sortFeaturesByY;
|
||||
|
||||
optional<float> sortedAngle;
|
||||
|
||||
bool staticUploaded = false;
|
||||
bool placementChangesUploaded = false;
|
||||
bool dynamicUploaded = false;
|
||||
bool sortUploaded = false;
|
||||
|
||||
std::vector<SymbolInstance> symbolInstances;
|
||||
|
||||
std::map<std::string, std::pair<
|
||||
SymbolIconProgram::PaintPropertyBinders,
|
||||
@@ -61,12 +82,14 @@ public:
|
||||
struct TextBuffer {
|
||||
gl::VertexVector<SymbolLayoutVertex> vertices;
|
||||
gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> dynamicVertices;
|
||||
gl::VertexVector<SymbolOpacityAttributes::Vertex> opacityVertices;
|
||||
gl::IndexVector<gl::Triangles> triangles;
|
||||
SegmentVector<SymbolTextAttributes> segments;
|
||||
std::vector<PlacedSymbol> placedSymbols;
|
||||
|
||||
optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer;
|
||||
optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer;
|
||||
optional<gl::VertexBuffer<SymbolOpacityAttributes::Vertex>> opacityVertexBuffer;
|
||||
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
|
||||
} text;
|
||||
|
||||
@@ -75,6 +98,7 @@ public:
|
||||
struct IconBuffer {
|
||||
gl::VertexVector<SymbolLayoutVertex> vertices;
|
||||
gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> dynamicVertices;
|
||||
gl::VertexVector<SymbolOpacityAttributes::Vertex> opacityVertices;
|
||||
gl::IndexVector<gl::Triangles> triangles;
|
||||
SegmentVector<SymbolIconAttributes> segments;
|
||||
std::vector<PlacedSymbol> placedSymbols;
|
||||
@@ -82,18 +106,30 @@ public:
|
||||
|
||||
optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer;
|
||||
optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer;
|
||||
optional<gl::VertexBuffer<SymbolOpacityAttributes::Vertex>> opacityVertexBuffer;
|
||||
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
|
||||
} icon;
|
||||
|
||||
struct CollisionBoxBuffer {
|
||||
gl::VertexVector<CollisionBoxVertex> vertices;
|
||||
gl::IndexVector<gl::Lines> lines;
|
||||
SegmentVector<CollisionBoxAttributes> segments;
|
||||
struct CollisionBuffer {
|
||||
gl::VertexVector<CollisionBoxLayoutAttributes::Vertex> vertices;
|
||||
gl::VertexVector<CollisionBoxDynamicAttributes::Vertex> dynamicVertices;
|
||||
SegmentVector<CollisionBoxProgram::Attributes> segments;
|
||||
|
||||
optional<gl::VertexBuffer<CollisionBoxVertex>> vertexBuffer;
|
||||
optional<gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>> dynamicVertexBuffer;
|
||||
optional<gl::VertexBuffer<CollisionBoxLayoutAttributes::Vertex>> vertexBuffer;
|
||||
optional<gl::VertexBuffer<CollisionBoxDynamicAttributes::Vertex>> dynamicVertexBuffer;
|
||||
};
|
||||
|
||||
struct CollisionBoxBuffer : public CollisionBuffer {
|
||||
gl::IndexVector<gl::Lines> lines;
|
||||
optional<gl::IndexBuffer<gl::Lines>> indexBuffer;
|
||||
} collisionBox;
|
||||
|
||||
struct CollisionCircleBuffer : public CollisionBuffer {
|
||||
gl::IndexVector<gl::Triangles> triangles;
|
||||
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
|
||||
} collisionCircle;
|
||||
|
||||
uint32_t bucketInstanceId = 0;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -74,6 +74,8 @@ public:
|
||||
uint32_t currentLayer;
|
||||
float depthRangeSize;
|
||||
const float depthEpsilon = 1.0f / (1 << 16);
|
||||
|
||||
float symbolFadeChange;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -81,14 +81,13 @@ public:
|
||||
|
||||
friend std::string layoutKey(const RenderLayer&);
|
||||
|
||||
// Stores current set of tiles to be rendered for this layer.
|
||||
std::vector<std::reference_wrapper<RenderTile>> renderTiles;
|
||||
|
||||
protected:
|
||||
// Stores what render passes this layer is currently enabled for. This depends on the
|
||||
// evaluated StyleProperties object and is updated accordingly.
|
||||
RenderPass passes = RenderPass::None;
|
||||
|
||||
//Stores current set of tiles to be rendered for this layer.
|
||||
std::vector<std::reference_wrapper<RenderTile>> renderTiles;
|
||||
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <mbgl/style/source_impl.hpp>
|
||||
#include <mbgl/style/transition_options.hpp>
|
||||
#include <mbgl/text/glyph_manager.hpp>
|
||||
#include <mbgl/text/cross_tile_symbol_index.hpp>
|
||||
#include <mbgl/tile/tile.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
@@ -56,7 +57,9 @@ Renderer::Impl::Impl(RendererBackend& backend_,
|
||||
, imageImpls(makeMutable<std::vector<Immutable<style::Image::Impl>>>())
|
||||
, sourceImpls(makeMutable<std::vector<Immutable<style::Source::Impl>>>())
|
||||
, layerImpls(makeMutable<std::vector<Immutable<style::Layer::Impl>>>())
|
||||
, renderLight(makeMutable<Light::Impl>()) {
|
||||
, renderLight(makeMutable<Light::Impl>())
|
||||
, crossTileSymbolIndex(std::make_unique<CrossTileSymbolIndex>())
|
||||
, placement(std::make_unique<Placement>(TransformState{}, MapMode::Still)) {
|
||||
glyphManager->setObserver(this);
|
||||
}
|
||||
|
||||
@@ -348,26 +351,6 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We're not clipping symbol layers, so when we have both parents and children of symbol
|
||||
// layers, we drop all children in favor of their parent to avoid duplicate labels.
|
||||
// See https://github.com/mapbox/mapbox-gl-native/issues/2482
|
||||
if (symbolLayer) {
|
||||
bool skip = false;
|
||||
// Look back through the buckets we decided to render to find out whether there is
|
||||
// already a bucket from this layer that is a parent of this tile. Tiles are ordered
|
||||
// by zoom level when we obtain them from getTiles().
|
||||
for (auto it = sortedTilesForInsertion.rbegin();
|
||||
it != sortedTilesForInsertion.rend(); ++it) {
|
||||
if (tile.tile.id.isChildOf(it->get().tile.id)) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (skip) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto bucket = tile.tile.getBucket(*layer->baseImpl);
|
||||
if (bucket) {
|
||||
sortedTilesForInsertion.emplace_back(tile);
|
||||
@@ -388,6 +371,45 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
|
||||
frameHistory.record(parameters.timePoint,
|
||||
parameters.state.getZoom(),
|
||||
parameters.mapMode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Milliseconds(0));
|
||||
bool symbolBucketsChanged = false;
|
||||
if (parameters.mapMode == MapMode::Still) {
|
||||
// TODO: Think about right way for symbol index to handle still rendering
|
||||
crossTileSymbolIndex->reset();
|
||||
}
|
||||
for (auto it = order.rbegin(); it != order.rend(); ++it) {
|
||||
if (it->layer.is<RenderSymbolLayer>()) {
|
||||
if (crossTileSymbolIndex->addLayer(*it->layer.as<RenderSymbolLayer>())) symbolBucketsChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool placementChanged = false;
|
||||
if (!placement->stillRecent(parameters.timePoint)) {
|
||||
auto newPlacement = std::make_unique<Placement>(parameters.state, parameters.mapMode);
|
||||
for (auto it = order.rbegin(); it != order.rend(); ++it) {
|
||||
if (it->layer.is<RenderSymbolLayer>()) {
|
||||
newPlacement->placeLayer(*it->layer.as<RenderSymbolLayer>(), parameters.projMatrix, parameters.debugOptions & MapDebugOptions::Collision);
|
||||
}
|
||||
}
|
||||
|
||||
placementChanged = newPlacement->commit(*placement, parameters.timePoint);
|
||||
if (placementChanged || symbolBucketsChanged) {
|
||||
placement = std::move(newPlacement);
|
||||
}
|
||||
|
||||
placement->setRecent(parameters.timePoint);
|
||||
} else {
|
||||
placement->setStale();
|
||||
}
|
||||
|
||||
parameters.symbolFadeChange = placement->symbolFadeChange(parameters.timePoint);
|
||||
|
||||
if (placementChanged || symbolBucketsChanged) {
|
||||
for (auto it = order.rbegin(); it != order.rend(); ++it) {
|
||||
if (it->layer.is<RenderSymbolLayer>()) {
|
||||
placement->updateLayerOpacities(*it->layer.as<RenderSymbolLayer>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// - UPLOAD PASS -------------------------------------------------------------------------------
|
||||
// Uploads all required buffers and images before we do any actual rendering.
|
||||
@@ -607,7 +629,7 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
|
||||
|
||||
observer->onDidFinishRenderingFrame(
|
||||
loaded ? RendererObserver::RenderMode::Full : RendererObserver::RenderMode::Partial,
|
||||
updateParameters.mode == MapMode::Continuous && (hasTransitions() || frameHistory.needsAnimation(util::DEFAULT_TRANSITION_DURATION))
|
||||
updateParameters.mode == MapMode::Continuous && (hasTransitions(parameters.timePoint))
|
||||
);
|
||||
|
||||
if (!loaded) {
|
||||
@@ -647,7 +669,7 @@ std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin
|
||||
std::unordered_map<std::string, std::vector<Feature>> resultsByLayer;
|
||||
for (const auto& sourceID : sourceIDs) {
|
||||
if (RenderSource* renderSource = getRenderSource(sourceID)) {
|
||||
auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options);
|
||||
auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options, placement->getCollisionIndex());
|
||||
std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
|
||||
}
|
||||
}
|
||||
@@ -727,7 +749,7 @@ RenderSource* Renderer::Impl::getRenderSource(const std::string& id) const {
|
||||
return it != renderSources.end() ? it->second.get() : nullptr;
|
||||
}
|
||||
|
||||
bool Renderer::Impl::hasTransitions() const {
|
||||
bool Renderer::Impl::hasTransitions(TimePoint timePoint) const {
|
||||
if (renderLight.hasTransition()) {
|
||||
return true;
|
||||
}
|
||||
@@ -738,6 +760,10 @@ bool Renderer::Impl::hasTransitions() const {
|
||||
}
|
||||
}
|
||||
|
||||
if (placement->hasTransitions(timePoint)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <mbgl/map/transform_state.hpp>
|
||||
#include <mbgl/map/zoom_history.hpp>
|
||||
#include <mbgl/text/glyph_manager_observer.hpp>
|
||||
#include <mbgl/text/placement.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -31,6 +32,7 @@ class Scheduler;
|
||||
class GlyphManager;
|
||||
class ImageManager;
|
||||
class LineAtlas;
|
||||
class CrossTileSymbolIndex;
|
||||
|
||||
class Renderer::Impl : public GlyphManagerObserver,
|
||||
public RenderSourceObserver{
|
||||
@@ -56,7 +58,7 @@ public:
|
||||
|
||||
private:
|
||||
bool isLoaded() const;
|
||||
bool hasTransitions() const;
|
||||
bool hasTransitions(TimePoint) const;
|
||||
|
||||
RenderSource* getRenderSource(const std::string& id) const;
|
||||
|
||||
@@ -108,6 +110,9 @@ private:
|
||||
std::unordered_map<std::string, std::unique_ptr<RenderLayer>> renderLayers;
|
||||
RenderLight renderLight;
|
||||
|
||||
std::unique_ptr<CrossTileSymbolIndex> crossTileSymbolIndex;
|
||||
std::unique_ptr<Placement> placement;
|
||||
|
||||
bool contextLost = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <mbgl/renderer/tile_parameters.hpp>
|
||||
#include <mbgl/renderer/query.hpp>
|
||||
#include <mbgl/map/transform.hpp>
|
||||
#include <mbgl/text/placement_config.hpp>
|
||||
#include <mbgl/math/clamp.hpp>
|
||||
#include <mbgl/util/tile_cover.hpp>
|
||||
#include <mbgl/util/enum.hpp>
|
||||
@@ -120,6 +119,7 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
|
||||
// use because they're still loading. In addition to that, we also need to retain all tiles that
|
||||
// we're actively using, e.g. as a replacement for tile that aren't loaded yet.
|
||||
std::set<OverscaledTileID> retain;
|
||||
std::set<UnwrappedTileID> rendered;
|
||||
|
||||
auto retainTileFn = [&](Tile& tile, TileNecessity necessity) -> void {
|
||||
if (retain.emplace(tile.id).second) {
|
||||
@@ -150,6 +150,7 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
|
||||
};
|
||||
auto renderTileFn = [&](const UnwrappedTileID& tileID, Tile& tile) {
|
||||
renderTiles.emplace_back(tileID, tile);
|
||||
rendered.emplace(tileID);
|
||||
};
|
||||
|
||||
renderTiles.clear();
|
||||
@@ -177,6 +178,13 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
|
||||
auto tilesIt = tiles.begin();
|
||||
auto retainIt = retain.begin();
|
||||
while (tilesIt != tiles.end()) {
|
||||
auto renderedIt = rendered.find(tilesIt->first.toUnwrapped());
|
||||
if (renderedIt == rendered.end()) {
|
||||
// Since this tile isn't in the render set, crossTileIDs won't be kept
|
||||
// updated by CrossTileSymbolIndex. We need to reset the stored crossTileIDs
|
||||
// so they're not reused if/when this tile is re-added to the render set
|
||||
tilesIt->second->resetCrossTileIDs();
|
||||
}
|
||||
if (retainIt == retain.end() || tilesIt->first < *retainIt) {
|
||||
if (!needsRelayout) {
|
||||
tilesIt->second->setNecessity(TileNecessity::Optional);
|
||||
@@ -193,13 +201,7 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
|
||||
}
|
||||
|
||||
for (auto& pair : tiles) {
|
||||
const PlacementConfig config { parameters.transformState.getAngle(),
|
||||
parameters.transformState.getPitch(),
|
||||
parameters.transformState.getCameraToCenterDistance(),
|
||||
parameters.transformState.getCameraToTileDistance(pair.first.toUnwrapped()),
|
||||
parameters.debugOptions & MapDebugOptions::Collision };
|
||||
|
||||
pair.second->setPlacementConfig(config);
|
||||
pair.second->setShowCollisionBoxes(parameters.debugOptions & MapDebugOptions::Collision);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,9 @@ CollisionFeature::CollisionFeature(const GeometryCoordinates& line,
|
||||
const float padding,
|
||||
const style::SymbolPlacementType placement,
|
||||
IndexedSubfeature indexedFeature_,
|
||||
const AlignmentType alignment)
|
||||
: indexedFeature(std::move(indexedFeature_)) {
|
||||
const float overscaling)
|
||||
: indexedFeature(std::move(indexedFeature_))
|
||||
, alongLine(placement == style::SymbolPlacementType::Line) {
|
||||
if (top == 0 && bottom == 0 && left == 0 && right == 0) return;
|
||||
|
||||
const float y1 = top * boxScale - padding;
|
||||
@@ -22,7 +23,7 @@ CollisionFeature::CollisionFeature(const GeometryCoordinates& line,
|
||||
const float x1 = left * boxScale - padding;
|
||||
const float x2 = right * boxScale + padding;
|
||||
|
||||
if (placement == style::SymbolPlacementType::Line) {
|
||||
if (alongLine) {
|
||||
float height = y2 - y1;
|
||||
const double length = x2 - x1;
|
||||
|
||||
@@ -31,29 +32,26 @@ CollisionFeature::CollisionFeature(const GeometryCoordinates& line,
|
||||
height = std::max(10.0f * boxScale, height);
|
||||
|
||||
GeometryCoordinate anchorPoint = convertPoint<int16_t>(anchor.point);
|
||||
|
||||
if (alignment == AlignmentType::Straight) {
|
||||
// used for icon labels that are aligned with the line, but don't curve along it
|
||||
const GeometryCoordinate vector = convertPoint<int16_t>(util::unit(convertPoint<double>(line[anchor.segment + 1] - line[anchor.segment])) * length);
|
||||
const GeometryCoordinates newLine({ anchorPoint - vector, anchorPoint + vector });
|
||||
bboxifyLabel(newLine, anchorPoint, 0, length, height);
|
||||
} else {
|
||||
// used for text labels that curve along a line
|
||||
bboxifyLabel(line, anchorPoint, anchor.segment, length, height);
|
||||
}
|
||||
bboxifyLabel(line, anchorPoint, anchor.segment, length, height, overscaling);
|
||||
} else {
|
||||
boxes.emplace_back(anchor.point, Point<float>{ 0, 0 }, x1, y1, x2, y2, std::numeric_limits<float>::infinity());
|
||||
boxes.emplace_back(anchor.point, Point<float>{ 0, 0 }, x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoordinate& anchorPoint,
|
||||
const int segment, const float labelLength, const float boxSize) {
|
||||
const int segment, const float labelLength, const float boxSize, const float overscaling) {
|
||||
const float step = boxSize / 2;
|
||||
const int nBoxes = std::floor(labelLength / step);
|
||||
// We calculate line collision boxes out to 300% of what would normally be our
|
||||
// We calculate line collision circles out to 300% of what would normally be our
|
||||
// max size, to allow collision detection to work on labels that expand as
|
||||
// they move into the distance
|
||||
const int nPitchPaddingBoxes = std::floor(nBoxes / 2);
|
||||
// Vertically oriented labels in the distant field can extend past this padding
|
||||
// This is a noticeable problem in overscaled tiles where the pitch 0-based
|
||||
// symbol spacing will put labels very close together in a pitched map.
|
||||
// To reduce the cost of adding extra collision circles, we slowly increase
|
||||
// them for overscaled tiles.
|
||||
const float overscalingPaddingFactor = 1 + .4 * std::log(overscaling) / std::log(2);
|
||||
const int nPitchPaddingBoxes = std::floor(nBoxes * overscalingPaddingFactor / 2);
|
||||
|
||||
// offset the center of the first box by half a box so that the edge of the
|
||||
// box is at the edge of the label.
|
||||
@@ -124,47 +122,18 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo
|
||||
p0.x + segmentBoxDistance / segmentLength * (p1.x - p0.x),
|
||||
p0.y + segmentBoxDistance / segmentLength * (p1.y - p0.y)
|
||||
};
|
||||
|
||||
// If the box is within boxSize of the anchor, force the box to be used
|
||||
// (so even 0-width labels use at least one box)
|
||||
// Otherwise, the .8 multiplication gives us a little bit of conservative
|
||||
// padding in choosing which boxes to use (see CollisionIndex#placedCollisionCircles)
|
||||
const float paddedAnchorDistance = std::abs(boxDistanceToAnchor - firstBoxOffset) < step ?
|
||||
0 :
|
||||
(boxDistanceToAnchor - firstBoxOffset) * 0.8;
|
||||
|
||||
// Distance from label anchor point to inner (towards center) edge of this box
|
||||
// The tricky thing here is that box positioning doesn't change with scale,
|
||||
// but box size does change with scale.
|
||||
// Technically, distanceToInnerEdge should be:
|
||||
// Math.max(Math.abs(boxDistanceToAnchor - firstBoxOffset) - (step / scale), 0);
|
||||
// But using that formula would make solving for maxScale more difficult, so we
|
||||
// approximate with scale=2.
|
||||
// This makes our calculation spot-on at scale=2, and on the conservative side for
|
||||
// lower scales
|
||||
const float distanceToInnerEdge = std::max(std::fabs(boxDistanceToAnchor - firstBoxOffset) - step / 2, 0.0f);
|
||||
float maxScale = util::division(labelLength / 2, distanceToInnerEdge, std::numeric_limits<float>::infinity());
|
||||
|
||||
// The box maxScale calculations are designed to be conservative on collisions in the scale range
|
||||
// [1,2]. At scale=1, each box has 50% overlap, and at scale=2, the boxes are lined up edge
|
||||
// to edge (beyond scale 2, gaps start to appear, which could potentially allow missed collisions).
|
||||
// We add "pitch padding" boxes to the left and right to handle effective underzooming
|
||||
// (scale < 1) when labels are in the distance. The overlap approximation could cause us to use
|
||||
// these boxes when the scale is greater than 1, but we prevent that because we know
|
||||
// they're only necessary for scales less than one.
|
||||
// This preserves the pre-pitch-padding behavior for unpitched maps.
|
||||
if (i < 0 || i >= nBoxes) {
|
||||
maxScale = std::min(maxScale, 0.99f);
|
||||
}
|
||||
|
||||
boxes.emplace_back(boxAnchor, boxAnchor - convertPoint<float>(anchorPoint), -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, maxScale);
|
||||
boxes.emplace_back(boxAnchor, boxAnchor - convertPoint<float>(anchorPoint), -boxSize / 2, -boxSize / 2, boxSize / 2, boxSize / 2, paddedAnchorDistance, boxSize / 2);
|
||||
}
|
||||
}
|
||||
|
||||
float CollisionBox::adjustedMaxScale(const std::array<float, 4>& rotationMatrix, const float yStretch) const {
|
||||
// When the map is pitched the distance covered by a line changes.
|
||||
// Adjust the max scale by (approximatePitchedLength / approximateRegularLength)
|
||||
// to compensate for this.
|
||||
const Point<float> rotatedOffset = util::matrixMultiply(rotationMatrix, offset);
|
||||
const float xSqr = rotatedOffset.x * rotatedOffset.x;
|
||||
const float ySqr = rotatedOffset.y * rotatedOffset.y;
|
||||
const float yStretchSqr = ySqr * yStretch * yStretch;
|
||||
const float adjustmentFactor = xSqr + ySqr != 0 ?
|
||||
std::sqrt((xSqr + yStretchSqr) / (xSqr + ySqr)) :
|
||||
1.0f;
|
||||
return maxScale * adjustmentFactor;
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -11,10 +11,8 @@ namespace mbgl {
|
||||
|
||||
class CollisionBox {
|
||||
public:
|
||||
CollisionBox(Point<float> _anchor, Point<float> _offset, float _x1, float _y1, float _x2, float _y2, float _maxScale) :
|
||||
anchor(std::move(_anchor)), offset(_offset), x1(_x1), y1(_y1), x2(_x2), y2(_y2), maxScale(_maxScale) {}
|
||||
|
||||
float adjustedMaxScale(const std::array<float, 4>& rotationMatrix, const float yStretch) const;
|
||||
CollisionBox(Point<float> _anchor, Point<float> _offset, float _x1, float _y1, float _x2, float _y2, float _signedDistanceFromAnchor = 0, float _radius = 0) :
|
||||
anchor(std::move(_anchor)), offset(_offset), x1(_x1), y1(_y1), x2(_x2), y2(_y2), used(true), signedDistanceFromAnchor(_signedDistanceFromAnchor), radius(_radius) {}
|
||||
|
||||
// the box is centered around the anchor point
|
||||
Point<float> anchor;
|
||||
@@ -28,20 +26,23 @@ public:
|
||||
float x2;
|
||||
float y2;
|
||||
|
||||
// the box is only valid for scales < maxScale.
|
||||
// The box does not block other boxes at scales >= maxScale;
|
||||
float maxScale;
|
||||
// Projected box geometry: generated/updated at placement time
|
||||
float px1;
|
||||
float py1;
|
||||
float px2;
|
||||
float py2;
|
||||
|
||||
// Projected circle geometry: generated/updated at placement time
|
||||
float px;
|
||||
float py;
|
||||
bool used;
|
||||
|
||||
// the scale at which the label can first be shown
|
||||
float placementScale = 0.0f;
|
||||
float signedDistanceFromAnchor;
|
||||
float radius;
|
||||
};
|
||||
|
||||
class CollisionFeature {
|
||||
public:
|
||||
enum class AlignmentType : bool {
|
||||
Straight = false,
|
||||
Curved
|
||||
};
|
||||
|
||||
// for text
|
||||
CollisionFeature(const GeometryCoordinates& line,
|
||||
@@ -50,8 +51,9 @@ public:
|
||||
const float boxScale,
|
||||
const float padding,
|
||||
const style::SymbolPlacementType placement,
|
||||
const IndexedSubfeature& indexedFeature_)
|
||||
: CollisionFeature(line, anchor, shapedText.top, shapedText.bottom, shapedText.left, shapedText.right, boxScale, padding, placement, indexedFeature_, AlignmentType::Curved) {}
|
||||
const IndexedSubfeature& indexedFeature_,
|
||||
const float overscaling)
|
||||
: CollisionFeature(line, anchor, shapedText.top, shapedText.bottom, shapedText.left, shapedText.right, boxScale, padding, placement, indexedFeature_, overscaling) {}
|
||||
|
||||
// for icons
|
||||
CollisionFeature(const GeometryCoordinates& line,
|
||||
@@ -66,7 +68,7 @@ public:
|
||||
(shapedIcon ? shapedIcon->bottom() : 0),
|
||||
(shapedIcon ? shapedIcon->left() : 0),
|
||||
(shapedIcon ? shapedIcon->right() : 0),
|
||||
boxScale, padding, placement, indexedFeature_, AlignmentType::Straight) {}
|
||||
boxScale, padding, placement, indexedFeature_, 1) {}
|
||||
|
||||
CollisionFeature(const GeometryCoordinates& line,
|
||||
const Anchor&,
|
||||
@@ -78,14 +80,15 @@ public:
|
||||
const float padding,
|
||||
const style::SymbolPlacementType,
|
||||
IndexedSubfeature,
|
||||
const AlignmentType);
|
||||
const float overscaling);
|
||||
|
||||
std::vector<CollisionBox> boxes;
|
||||
IndexedSubfeature indexedFeature;
|
||||
bool alongLine;
|
||||
|
||||
private:
|
||||
void bboxifyLabel(const GeometryCoordinates& line, GeometryCoordinate& anchorPoint,
|
||||
const int segment, const float length, const float height);
|
||||
const int segment, const float length, const float height, const float overscaling);
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -62,13 +62,13 @@ void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatri
|
||||
state.matrixFor(posMatrix, renderTile.id);
|
||||
matrix::multiply(posMatrix, projMatrix, posMatrix);
|
||||
|
||||
mat4 textLabelPlaneMatrix = getLabelPlaneMatrix(renderTile.matrix,
|
||||
mat4 textLabelPlaneMatrix = getLabelPlaneMatrix(posMatrix,
|
||||
layout.get<TextPitchAlignment>() == style::AlignmentType::Map,
|
||||
layout.get<TextRotationAlignment>() == style::AlignmentType::Map,
|
||||
state,
|
||||
pixelsToTileUnits);
|
||||
|
||||
mat4 iconLabelPlaneMatrix = getLabelPlaneMatrix(renderTile.matrix,
|
||||
mat4 iconLabelPlaneMatrix = getLabelPlaneMatrix(posMatrix,
|
||||
layout.get<IconPitchAlignment>() == style::AlignmentType::Map,
|
||||
layout.get<IconRotationAlignment>() == style::AlignmentType::Map,
|
||||
state,
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <mbgl/renderer/image_atlas.hpp>
|
||||
#include <mbgl/storage/file_source.hpp>
|
||||
#include <mbgl/geometry/feature_index.hpp>
|
||||
#include <mbgl/text/collision_tile.hpp>
|
||||
#include <mbgl/map/transform_state.hpp>
|
||||
#include <mbgl/style/filter_evaluator.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
@@ -33,7 +32,7 @@ using namespace style;
|
||||
|
||||
GeometryTile's 'correlationID' is used for ensuring the tile will be flagged
|
||||
as non-pending only when the placement coming from the last operation (as in
|
||||
'setData', 'setLayers', 'setPlacementConfig') occurs. This is important for
|
||||
'setData', 'setLayers', 'setShowCollisionBoxes') occurs. This is important for
|
||||
still mode rendering as we want to render only when all layout and placement
|
||||
operations are completed.
|
||||
|
||||
@@ -52,13 +51,15 @@ GeometryTile::GeometryTile(const OverscaledTileID& id_,
|
||||
worker(parameters.workerScheduler,
|
||||
ActorRef<GeometryTile>(*this, mailbox),
|
||||
id_,
|
||||
sourceID,
|
||||
obsolete,
|
||||
parameters.mode,
|
||||
parameters.pixelRatio),
|
||||
parameters.pixelRatio,
|
||||
parameters.debugOptions & MapDebugOptions::Collision),
|
||||
glyphManager(parameters.glyphManager),
|
||||
imageManager(parameters.imageManager),
|
||||
lastYStretch(1.0f),
|
||||
mode(parameters.mode) {
|
||||
mode(parameters.mode),
|
||||
showCollisionBoxes(parameters.debugOptions & MapDebugOptions::Collision) {
|
||||
}
|
||||
|
||||
GeometryTile::~GeometryTile() {
|
||||
@@ -89,25 +90,6 @@ void GeometryTile::setData(std::unique_ptr<const GeometryTileData> data_) {
|
||||
worker.invoke(&GeometryTileWorker::setData, std::move(data_), correlationID);
|
||||
}
|
||||
|
||||
void GeometryTile::setPlacementConfig(const PlacementConfig& desiredConfig) {
|
||||
if (requestedConfig == desiredConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark the tile as pending again if it was complete before to prevent signaling a complete
|
||||
// state despite pending parse operations.
|
||||
pending = true;
|
||||
|
||||
++correlationID;
|
||||
requestedConfig = desiredConfig;
|
||||
invokePlacement();
|
||||
}
|
||||
|
||||
void GeometryTile::invokePlacement() {
|
||||
if (requestedConfig) {
|
||||
worker.invoke(&GeometryTileWorker::setPlacementConfig, *requestedConfig, correlationID);
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryTile::setLayers(const std::vector<Immutable<Layer::Impl>>& layers) {
|
||||
// Mark the tile as pending again if it was complete before to prevent signaling a complete
|
||||
@@ -134,14 +116,22 @@ void GeometryTile::setLayers(const std::vector<Immutable<Layer::Impl>>& layers)
|
||||
worker.invoke(&GeometryTileWorker::setLayers, std::move(impls), correlationID);
|
||||
}
|
||||
|
||||
void GeometryTile::setShowCollisionBoxes(const bool showCollisionBoxes_) {
|
||||
if (showCollisionBoxes != showCollisionBoxes_) {
|
||||
showCollisionBoxes = showCollisionBoxes_;
|
||||
++correlationID;
|
||||
worker.invoke(&GeometryTileWorker::setShowCollisionBoxes, showCollisionBoxes, correlationID);
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelationID) {
|
||||
loaded = true;
|
||||
renderable = true;
|
||||
// Don't mark ourselves loaded or renderable until the first successful placement
|
||||
// TODO: Ideally we'd render this tile without symbols as long as this tile wasn't
|
||||
// replacing a tile at a different zoom that _did_ have symbols.
|
||||
(void)resultCorrelationID;
|
||||
nonSymbolBuckets = std::move(result.nonSymbolBuckets);
|
||||
featureIndex = std::move(result.featureIndex);
|
||||
data = std::move(result.tileData);
|
||||
collisionTile.reset();
|
||||
observer->onTileChanged(*this);
|
||||
}
|
||||
|
||||
@@ -152,16 +142,13 @@ void GeometryTile::onPlacement(PlacementResult result, const uint64_t resultCorr
|
||||
pending = false;
|
||||
}
|
||||
symbolBuckets = std::move(result.symbolBuckets);
|
||||
collisionTile = std::move(result.collisionTile);
|
||||
if (result.glyphAtlasImage) {
|
||||
glyphAtlasImage = std::move(*result.glyphAtlasImage);
|
||||
}
|
||||
if (result.iconAtlasImage) {
|
||||
iconAtlasImage = std::move(*result.iconAtlasImage);
|
||||
}
|
||||
if (collisionTile.get()) {
|
||||
lastYStretch = collisionTile->yStretch;
|
||||
}
|
||||
|
||||
observer->onTileChanged(*this);
|
||||
}
|
||||
|
||||
@@ -231,7 +218,8 @@ void GeometryTile::queryRenderedFeatures(
|
||||
const GeometryCoordinates& queryGeometry,
|
||||
const TransformState& transformState,
|
||||
const std::vector<const RenderLayer*>& layers,
|
||||
const RenderedQueryOptions& options) {
|
||||
const RenderedQueryOptions& options,
|
||||
const CollisionIndex& collisionIndex) {
|
||||
|
||||
if (!featureIndex || !data) return;
|
||||
|
||||
@@ -251,9 +239,10 @@ void GeometryTile::queryRenderedFeatures(
|
||||
std::pow(2, transformState.getZoom() - id.overscaledZ),
|
||||
options,
|
||||
*data,
|
||||
id.canonical,
|
||||
id.toUnwrapped(),
|
||||
sourceID,
|
||||
layers,
|
||||
collisionTile.get(),
|
||||
collisionIndex,
|
||||
additionalRadius);
|
||||
}
|
||||
|
||||
@@ -293,11 +282,16 @@ void GeometryTile::querySourceFeatures(
|
||||
}
|
||||
}
|
||||
|
||||
float GeometryTile::yStretch() const {
|
||||
// collisionTile gets reset in onLayout but we don't clear the symbolBuckets
|
||||
// until a new placement result comes along, so keep the yStretch value in
|
||||
// case we need to render them.
|
||||
return lastYStretch;
|
||||
void GeometryTile::resetCrossTileIDs() {
|
||||
for (auto& bucket : symbolBuckets) {
|
||||
auto symbolBucket = dynamic_cast<SymbolBucket*>(bucket.second.get());
|
||||
if (symbolBucket && symbolBucket->bucketInstanceId) {
|
||||
symbolBucket->bucketInstanceId = 0;
|
||||
for (auto& symbolInstance : symbolBucket->symbolInstances) {
|
||||
symbolInstance.crossTileID = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -4,12 +4,11 @@
|
||||
#include <mbgl/tile/geometry_tile_worker.hpp>
|
||||
#include <mbgl/renderer/image_manager.hpp>
|
||||
#include <mbgl/text/glyph_manager.hpp>
|
||||
#include <mbgl/text/placement_config.hpp>
|
||||
#include <mbgl/text/collision_tile.hpp>
|
||||
#include <mbgl/util/feature.hpp>
|
||||
#include <mbgl/util/throttler.hpp>
|
||||
#include <mbgl/actor/actor.hpp>
|
||||
#include <mbgl/geometry/feature_index.hpp>
|
||||
#include <mbgl/layout/symbol_layout.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
@@ -36,9 +35,9 @@ public:
|
||||
void setError(std::exception_ptr);
|
||||
void setData(std::unique_ptr<const GeometryTileData>);
|
||||
|
||||
void setPlacementConfig(const PlacementConfig&) override;
|
||||
void setLayers(const std::vector<Immutable<style::Layer::Impl>>&) override;
|
||||
|
||||
void setShowCollisionBoxes(const bool showCollisionBoxes) override;
|
||||
|
||||
void onGlyphsAvailable(GlyphMap) override;
|
||||
void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) override;
|
||||
|
||||
@@ -56,7 +55,8 @@ public:
|
||||
const GeometryCoordinates& queryGeometry,
|
||||
const TransformState&,
|
||||
const std::vector<const RenderLayer*>& layers,
|
||||
const RenderedQueryOptions& options) override;
|
||||
const RenderedQueryOptions& options,
|
||||
const CollisionIndex& collisionIndex) override;
|
||||
|
||||
void querySourceFeatures(
|
||||
std::vector<Feature>& result,
|
||||
@@ -82,16 +82,13 @@ public:
|
||||
class PlacementResult {
|
||||
public:
|
||||
std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets;
|
||||
std::unique_ptr<CollisionTile> collisionTile;
|
||||
optional<AlphaImage> glyphAtlasImage;
|
||||
optional<PremultipliedImage> iconAtlasImage;
|
||||
|
||||
PlacementResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets_,
|
||||
std::unique_ptr<CollisionTile> collisionTile_,
|
||||
optional<AlphaImage> glyphAtlasImage_,
|
||||
optional<PremultipliedImage> iconAtlasImage_)
|
||||
: symbolBuckets(std::move(symbolBuckets_)),
|
||||
collisionTile(std::move(collisionTile_)),
|
||||
glyphAtlasImage(std::move(glyphAtlasImage_)),
|
||||
iconAtlasImage(std::move(iconAtlasImage_)) {}
|
||||
};
|
||||
@@ -99,7 +96,7 @@ public:
|
||||
|
||||
void onError(std::exception_ptr, uint64_t correlationID);
|
||||
|
||||
float yStretch() const override;
|
||||
void resetCrossTileIDs() override;
|
||||
|
||||
protected:
|
||||
const GeometryTileData* getData() {
|
||||
@@ -108,7 +105,6 @@ protected:
|
||||
|
||||
private:
|
||||
void markObsolete();
|
||||
void invokePlacement();
|
||||
|
||||
const std::string sourceID;
|
||||
|
||||
@@ -122,7 +118,6 @@ private:
|
||||
ImageManager& imageManager;
|
||||
|
||||
uint64_t correlationID = 0;
|
||||
optional<PlacementConfig> requestedConfig;
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets;
|
||||
std::unique_ptr<FeatureIndex> featureIndex;
|
||||
@@ -132,10 +127,11 @@ private:
|
||||
optional<PremultipliedImage> iconAtlasImage;
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets;
|
||||
std::unique_ptr<CollisionTile> collisionTile;
|
||||
std::unordered_map<std::string, std::unique_ptr<SymbolLayout>> symbolLayouts;
|
||||
|
||||
float lastYStretch;
|
||||
const MapMode mode;
|
||||
|
||||
bool showCollisionBoxes;
|
||||
|
||||
public:
|
||||
optional<gl::Texture> glyphAtlasTexture;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <mbgl/tile/geometry_tile_worker.hpp>
|
||||
#include <mbgl/tile/geometry_tile_data.hpp>
|
||||
#include <mbgl/tile/geometry_tile.hpp>
|
||||
#include <mbgl/text/collision_tile.hpp>
|
||||
#include <mbgl/layout/symbol_layout.hpp>
|
||||
#include <mbgl/renderer/bucket_parameters.hpp>
|
||||
#include <mbgl/renderer/group_by_layout.hpp>
|
||||
@@ -24,15 +23,19 @@ using namespace style;
|
||||
GeometryTileWorker::GeometryTileWorker(ActorRef<GeometryTileWorker> self_,
|
||||
ActorRef<GeometryTile> parent_,
|
||||
OverscaledTileID id_,
|
||||
const std::string& sourceID_,
|
||||
const std::atomic<bool>& obsolete_,
|
||||
const MapMode mode_,
|
||||
const float pixelRatio_)
|
||||
const float pixelRatio_,
|
||||
const bool showCollisionBoxes_)
|
||||
: self(std::move(self_)),
|
||||
parent(std::move(parent_)),
|
||||
id(std::move(id_)),
|
||||
sourceID(sourceID_),
|
||||
obsolete(obsolete_),
|
||||
mode(mode_),
|
||||
pixelRatio(pixelRatio_) {
|
||||
pixelRatio(pixelRatio_),
|
||||
showCollisionBoxes(showCollisionBoxes_) {
|
||||
}
|
||||
|
||||
GeometryTileWorker::~GeometryTileWorker() = default;
|
||||
@@ -116,9 +119,9 @@ void GeometryTileWorker::setLayers(std::vector<Immutable<Layer::Impl>> layers_,
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryTileWorker::setPlacementConfig(PlacementConfig placementConfig_, uint64_t correlationID_) {
|
||||
void GeometryTileWorker::setShowCollisionBoxes(bool showCollisionBoxes_, uint64_t correlationID_) {
|
||||
try {
|
||||
placementConfig = std::move(placementConfig_);
|
||||
showCollisionBoxes = showCollisionBoxes_;
|
||||
correlationID = correlationID_;
|
||||
|
||||
switch (state) {
|
||||
@@ -372,7 +375,7 @@ bool GeometryTileWorker::hasPendingSymbolDependencies() const {
|
||||
}
|
||||
|
||||
void GeometryTileWorker::attemptPlacement() {
|
||||
if (!data || !layers || !placementConfig || hasPendingSymbolDependencies()) {
|
||||
if (!data || !layers || hasPendingSymbolDependencies()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -392,13 +395,13 @@ void GeometryTileWorker::attemptPlacement() {
|
||||
}
|
||||
|
||||
symbolLayout->prepare(glyphMap, glyphAtlas.positions,
|
||||
imageMap, imageAtlas.positions);
|
||||
imageMap, imageAtlas.positions,
|
||||
id, sourceID);
|
||||
}
|
||||
|
||||
symbolLayoutsNeedPreparation = false;
|
||||
}
|
||||
|
||||
auto collisionTile = std::make_unique<CollisionTile>(*placementConfig);
|
||||
std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
|
||||
|
||||
for (auto& symbolLayout : symbolLayouts) {
|
||||
@@ -410,7 +413,7 @@ void GeometryTileWorker::attemptPlacement() {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::shared_ptr<Bucket> bucket = symbolLayout->place(*collisionTile);
|
||||
std::shared_ptr<Bucket> bucket = symbolLayout->place(showCollisionBoxes);
|
||||
for (const auto& pair : symbolLayout->layerPaintProperties) {
|
||||
buckets.emplace(pair.first, bucket);
|
||||
}
|
||||
@@ -418,7 +421,6 @@ void GeometryTileWorker::attemptPlacement() {
|
||||
|
||||
parent.invoke(&GeometryTile::onPlacement, GeometryTile::PlacementResult {
|
||||
std::move(buckets),
|
||||
std::move(collisionTile),
|
||||
std::move(glyphAtlasImage),
|
||||
std::move(iconAtlasImage),
|
||||
}, correlationID);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <mbgl/tile/tile_id.hpp>
|
||||
#include <mbgl/style/image_impl.hpp>
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/text/placement_config.hpp>
|
||||
#include <mbgl/actor/actor_ref.hpp>
|
||||
#include <mbgl/util/optional.hpp>
|
||||
#include <mbgl/util/immutable.hpp>
|
||||
@@ -28,14 +27,16 @@ public:
|
||||
GeometryTileWorker(ActorRef<GeometryTileWorker> self,
|
||||
ActorRef<GeometryTile> parent,
|
||||
OverscaledTileID,
|
||||
const std::string&,
|
||||
const std::atomic<bool>&,
|
||||
const MapMode,
|
||||
const float pixelRatio);
|
||||
const float pixelRatio,
|
||||
const bool showCollisionBoxes_);
|
||||
~GeometryTileWorker();
|
||||
|
||||
void setLayers(std::vector<Immutable<style::Layer::Impl>>, uint64_t correlationID);
|
||||
void setData(std::unique_ptr<const GeometryTileData>, uint64_t correlationID);
|
||||
void setPlacementConfig(PlacementConfig, uint64_t correlationID);
|
||||
void setShowCollisionBoxes(bool showCollisionBoxes_, uint64_t correlationID_);
|
||||
|
||||
void onGlyphsAvailable(GlyphMap glyphs);
|
||||
void onImagesAvailable(ImageMap images, uint64_t imageCorrelationID);
|
||||
@@ -57,6 +58,7 @@ private:
|
||||
ActorRef<GeometryTile> parent;
|
||||
|
||||
const OverscaledTileID id;
|
||||
const std::string sourceID;
|
||||
const std::atomic<bool>& obsolete;
|
||||
const MapMode mode;
|
||||
const float pixelRatio;
|
||||
@@ -75,7 +77,6 @@ private:
|
||||
// Outer optional indicates whether we've received it or not.
|
||||
optional<std::vector<Immutable<style::Layer::Impl>>> layers;
|
||||
optional<std::unique_ptr<const GeometryTileData>> data;
|
||||
optional<PlacementConfig> placementConfig;
|
||||
|
||||
bool symbolLayoutsNeedPreparation = false;
|
||||
std::vector<std::unique_ptr<SymbolLayout>> symbolLayouts;
|
||||
@@ -83,6 +84,8 @@ private:
|
||||
ImageDependencies pendingImageDependencies;
|
||||
GlyphMap glyphMap;
|
||||
ImageMap imageMap;
|
||||
|
||||
bool showCollisionBoxes;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -23,7 +23,6 @@ namespace mbgl {
|
||||
class DebugBucket;
|
||||
class TransformState;
|
||||
class TileObserver;
|
||||
class PlacementConfig;
|
||||
class RenderLayer;
|
||||
class RenderedQueryOptions;
|
||||
class SourceQueryOptions;
|
||||
@@ -47,7 +46,7 @@ public:
|
||||
virtual void upload(gl::Context&) = 0;
|
||||
virtual Bucket* getBucket(const style::Layer::Impl&) const = 0;
|
||||
|
||||
virtual void setPlacementConfig(const PlacementConfig&) {}
|
||||
virtual void setShowCollisionBoxes(const bool) {}
|
||||
virtual void setLayers(const std::vector<Immutable<style::Layer::Impl>>&) {}
|
||||
virtual void setMask(TileMask&&) {}
|
||||
|
||||
@@ -92,6 +91,8 @@ public:
|
||||
bool isComplete() const {
|
||||
return loaded && !pending;
|
||||
}
|
||||
|
||||
virtual void resetCrossTileIDs() {};
|
||||
|
||||
void dumpDebugLogs() const;
|
||||
|
||||
@@ -101,8 +102,6 @@ public:
|
||||
|
||||
// Contains the tile ID string for painting debug information.
|
||||
std::unique_ptr<DebugBucket> debugBucket;
|
||||
|
||||
virtual float yStretch() const { return 1.0f; }
|
||||
|
||||
protected:
|
||||
bool triedOptional = false;
|
||||
|
||||
Reference in New Issue
Block a user