sort overlapping symbols in the y direction
fixes #333 fixes #988 -js: 6514de77be2ff3df4267785f42f5e53c63233cac
This commit is contained in:
@@ -119,10 +119,36 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c
|
||||
const auto &properties = layer_desc.getProperties<SymbolProperties>();
|
||||
const auto &layout = bucket.layout;
|
||||
|
||||
config.stencilTest = false;
|
||||
// TODO remove the `|| true` when #1673 is implemented
|
||||
const bool drawAcrossEdges = !(layout.text.allow_overlap || layout.icon.allow_overlap ||
|
||||
layout.text.ignore_placement || layout.icon.ignore_placement) || true;
|
||||
|
||||
// Disable the stencil test so that labels aren't clipped to tile boundaries.
|
||||
//
|
||||
// Layers with features that may be drawn overlapping aren't clipped. These
|
||||
// layers are sorted in the y direction, and to draw the correct ordering near
|
||||
// tile edges the icons are included in both tiles and clipped when drawing.
|
||||
config.stencilTest = drawAcrossEdges ? false : true;
|
||||
config.depthTest = true;
|
||||
config.depthMask = GL_FALSE;
|
||||
|
||||
if (bucket.hasCollisionBoxData() && (
|
||||
(bucket.hasIconData() && properties.icon.opacity) ||
|
||||
(bucket.hasTextData() && properties.text.opacity))) {
|
||||
config.stencilTest = true;
|
||||
|
||||
useProgram(collisionBoxShader->program);
|
||||
collisionBoxShader->u_matrix = matrix;
|
||||
collisionBoxShader->u_scale = std::pow(2, state.getNormalizedZoom() - id.z);
|
||||
collisionBoxShader->u_zoom = state.getNormalizedZoom() * 10;
|
||||
collisionBoxShader->u_maxzoom = (id.z + 1) * 10;
|
||||
lineWidth(3.0f);
|
||||
|
||||
config.depthRange = { strata, 1.0f };
|
||||
bucket.drawCollisionBoxes(*collisionBoxShader);
|
||||
|
||||
}
|
||||
|
||||
if (bucket.hasIconData()) {
|
||||
bool sdf = bucket.sdfIcons;
|
||||
|
||||
@@ -193,21 +219,4 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c
|
||||
&SymbolBucket::drawGlyphs);
|
||||
}
|
||||
|
||||
if (bucket.hasCollisionBoxData() && (
|
||||
(bucket.hasIconData() && properties.icon.opacity) ||
|
||||
(bucket.hasTextData() && properties.text.opacity))) {
|
||||
config.stencilTest = true;
|
||||
|
||||
useProgram(collisionBoxShader->program);
|
||||
collisionBoxShader->u_matrix = matrix;
|
||||
collisionBoxShader->u_scale = std::pow(2, state.getNormalizedZoom() - id.z);
|
||||
collisionBoxShader->u_zoom = state.getNormalizedZoom() * 10;
|
||||
collisionBoxShader->u_maxzoom = (id.z + 1) * 10;
|
||||
lineWidth(3.0f);
|
||||
|
||||
config.depthRange = { strata, 1.0f };
|
||||
bucket.drawCollisionBoxes(*collisionBoxShader);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,20 +32,22 @@ namespace mbgl {
|
||||
|
||||
SymbolInstance::SymbolInstance(Anchor &anchor, const std::vector<Coordinate> &line,
|
||||
const Shaping &shapedText, const PositionedIcon &shapedIcon,
|
||||
const StyleLayoutSymbol &layout, const bool inside,
|
||||
const StyleLayoutSymbol &layout, const bool addToBuffers,
|
||||
const float textBoxScale, const float textPadding, const float textAlongLine,
|
||||
const float iconBoxScale, const float iconPadding, const float iconAlongLine,
|
||||
const GlyphPositions &face) :
|
||||
x(anchor.x),
|
||||
y(anchor.y),
|
||||
hasText(shapedText),
|
||||
hasIcon(shapedIcon),
|
||||
|
||||
// Create the quads used for rendering the glyphs.
|
||||
glyphQuads(inside && shapedText ?
|
||||
glyphQuads(addToBuffers && shapedText ?
|
||||
getGlyphQuads(anchor, shapedText, textBoxScale, line, layout, textAlongLine, face) :
|
||||
SymbolQuads()),
|
||||
|
||||
// Create the quad used for rendering the icon.
|
||||
iconQuads(inside && shapedIcon ?
|
||||
iconQuads(addToBuffers && shapedIcon ?
|
||||
getIconQuads(anchor, shapedIcon, line, layout, iconAlongLine) :
|
||||
SymbolQuads()),
|
||||
|
||||
@@ -288,6 +290,8 @@ void SymbolBucket::addFeature(const std::vector<std::vector<Coordinate>> &lines,
|
||||
const bool iconAlongLine =
|
||||
layout.icon.rotation_alignment == RotationAlignmentType::Map &&
|
||||
layout.placement == PlacementType::Line;
|
||||
const bool mayOverlap = layout.text.allow_overlap || layout.icon.allow_overlap ||
|
||||
layout.text.ignore_placement || layout.icon.ignore_placement;
|
||||
|
||||
auto& clippedLines = layout.placement == PlacementType::Line ?
|
||||
util::clipLines(lines, 0, 0, 4096, 4096) :
|
||||
@@ -309,7 +313,20 @@ void SymbolBucket::addFeature(const std::vector<std::vector<Coordinate>> &lines,
|
||||
|
||||
if (avoidEdges && !inside) continue;
|
||||
|
||||
symbolInstances.emplace_back(anchor, line, shapedText, shapedIcon, layout, inside,
|
||||
// Normally symbol layers are drawn across tile boundaries. Only symbols
|
||||
// with their anchors within the tile boundaries are added to the buffers
|
||||
// to prevent symbols from being drawn twice.
|
||||
//
|
||||
// Symbols in layers with overlap are sorted in the y direction so that
|
||||
// symbols lower on the canvas are drawn on top of symbols near the top.
|
||||
// To preserve this order across tile boundaries these symbols can't
|
||||
// be drawn across tile boundaries. Instead they need to be included in
|
||||
// the buffers for both tiles and clipped to tile boundaries at draw time.
|
||||
//
|
||||
// TODO remove the `&& false` when is #1673 implemented
|
||||
const bool addToBuffers = inside || (mayOverlap && false);
|
||||
|
||||
symbolInstances.emplace_back(anchor, line, shapedText, shapedIcon, layout, addToBuffers,
|
||||
textBoxScale, textPadding, textAlongLine,
|
||||
iconBoxScale, iconPadding, iconAlongLine,
|
||||
face);
|
||||
@@ -335,6 +352,24 @@ void SymbolBucket::placeFeatures(bool swapImmediately) {
|
||||
layout.icon.rotation_alignment == RotationAlignmentType::Map &&
|
||||
layout.placement == PlacementType::Line;
|
||||
|
||||
const bool mayOverlap = layout.text.allow_overlap || layout.icon.allow_overlap ||
|
||||
layout.text.ignore_placement || layout.icon.ignore_placement;
|
||||
|
||||
// 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) {
|
||||
float sin = std::sin(collision.angle);
|
||||
float cos = std::cos(collision.angle);
|
||||
|
||||
std::sort(symbolInstances.begin(), symbolInstances.end(), [sin, cos](SymbolInstance &a, SymbolInstance &b) {
|
||||
const float aRotated = sin * a.x + cos * a.y;
|
||||
const float bRotated = sin * b.x + cos * b.y;
|
||||
return aRotated < bRotated;
|
||||
});
|
||||
}
|
||||
|
||||
for (SymbolInstance &symbolInstance : symbolInstances) {
|
||||
|
||||
const bool hasText = symbolInstance.hasText;
|
||||
@@ -343,7 +378,6 @@ void SymbolBucket::placeFeatures(bool swapImmediately) {
|
||||
const bool iconWithoutText = layout.text.optional || !hasText;
|
||||
const bool textWithoutIcon = layout.icon.optional || !hasIcon;
|
||||
|
||||
|
||||
// Calculate the scales at which the text and icon can be placed without collision.
|
||||
|
||||
float glyphScale = hasText && !layout.text.allow_overlap ?
|
||||
|
||||
@@ -48,10 +48,12 @@ class SymbolInstance {
|
||||
const float textBoxScale, const float textPadding, const float textAlongLine,
|
||||
const float iconBoxScale, const float iconPadding, const float iconAlongLine,
|
||||
const GlyphPositions &face);
|
||||
const bool hasText;
|
||||
const bool hasIcon;
|
||||
const SymbolQuads glyphQuads;
|
||||
const SymbolQuads iconQuads;
|
||||
float x;
|
||||
float y;
|
||||
bool hasText;
|
||||
bool hasIcon;
|
||||
SymbolQuads glyphQuads;
|
||||
SymbolQuads iconQuads;
|
||||
CollisionFeature textCollisionFeature;
|
||||
CollisionFeature iconCollisionFeature;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user