sort overlapping symbols in the y direction

fixes #333
fixes #988

-js: 6514de77be2ff3df4267785f42f5e53c63233cac
This commit is contained in:
Ansis Brammanis
2015-05-29 12:58:06 -04:00
parent 520eddb794
commit 36212ea3c6
3 changed files with 72 additions and 27 deletions

View File

@@ -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);
}
}

View File

@@ -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 ?

View File

@@ -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;
};