diff --git a/include/mbgl/style/style_bucket.hpp b/include/mbgl/style/style_bucket.hpp index 11b87d849..58d4d705d 100644 --- a/include/mbgl/style/style_bucket.hpp +++ b/include/mbgl/style/style_bucket.hpp @@ -40,6 +40,7 @@ public: PlacementType placement = PlacementType::Point; float min_distance = 250.0f; + bool avoid_edges = false; struct { bool allow_overlap = false; diff --git a/include/mbgl/text/collision.hpp b/include/mbgl/text/collision.hpp index 87ebdb279..6771765c1 100644 --- a/include/mbgl/text/collision.hpp +++ b/include/mbgl/text/collision.hpp @@ -3,15 +3,35 @@ #include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#ifndef __clang__ +#pragma GCC diagnostic ignored "-Wunused-local-typedefs" +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif +#include +#include +#include +#include +#pragma GCC diagnostic pop + namespace mbgl { +namespace bg = boost::geometry; +namespace bgm = bg::model; +namespace bgi = bg::index; +typedef bgm::point Point; +typedef bgm::box Box; +typedef std::pair PlacementValue; +typedef bgi::rtree> Tree; + class Collision { public: Collision(float zoom, float tileExtent, float tileSize, float placementDepth = 1); ~Collision(); - float getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale); + float getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale, bool avoidEdges); PlacementRange getPlacementRange(const GlyphBoxes &glyphs, float placementScale, bool horizontal); void insert(const GlyphBoxes &glyphs, const CollisionAnchor &anchor, float placementScale, @@ -20,6 +40,10 @@ public: private: void *hTree; void *cTree; + PlacementValue leftEdge; + PlacementValue topEdge; + PlacementValue rightEdge; + PlacementValue bottomEdge; public: const float tilePixelRatio; diff --git a/src/renderer/painter_symbol.cpp b/src/renderer/painter_symbol.cpp index 491b848e1..66961b318 100644 --- a/src/renderer/painter_symbol.cpp +++ b/src/renderer/painter_symbol.cpp @@ -18,6 +18,7 @@ void Painter::renderSymbol(SymbolBucket &bucket, std::shared_ptr lay const SymbolProperties &properties = layer_desc->getProperties(); + glDisable(GL_STENCIL_TEST); if (bucket.hasTextData()) { mat4 exMatrix; @@ -213,5 +214,7 @@ void Painter::renderSymbol(SymbolBucket &bucket, std::shared_ptr lay depthRange(strata, 1.0f); bucket.drawIcons(*iconShader); } + + glEnable(GL_STENCIL_TEST); } } diff --git a/src/renderer/symbol_bucket.cpp b/src/renderer/symbol_bucket.cpp index 7f79455a1..f10d44bb4 100644 --- a/src/renderer/symbol_bucket.cpp +++ b/src/renderer/symbol_bucket.cpp @@ -212,6 +212,7 @@ void SymbolBucket::addFeature(const std::vector &line, const Shaping const float iconBoxScale = collision.tilePixelRatio * properties.icon.max_size; const bool iconWithoutText = properties.text.optional || !shaping.size(); const bool textWithoutIcon = properties.icon.optional || !image; + const bool avoidEdges = properties.avoid_edges && properties.placement != PlacementType::Line; Anchors anchors; @@ -239,6 +240,9 @@ void SymbolBucket::addFeature(const std::vector &line, const Shaping Placement iconPlacement; float glyphScale = 0; float iconScale = 0; + bool inside = !(anchor.x < 0 || anchor.x > 4096 || anchor.y < 0 || anchor.y > 4096); + + if (avoidEdges && !inside) continue; if (shaping.size()) { glyphPlacement = Placement::getGlyphs(anchor, origin, shaping, face, textBoxScale, @@ -246,7 +250,7 @@ void SymbolBucket::addFeature(const std::vector &line, const Shaping glyphScale = properties.text.allow_overlap ? glyphPlacement.minScale - : collision.getPlacementScale(glyphPlacement.boxes, glyphPlacement.minScale); + : collision.getPlacementScale(glyphPlacement.boxes, glyphPlacement.minScale, avoidEdges); if (!glyphScale && !iconWithoutText) continue; } @@ -256,7 +260,7 @@ void SymbolBucket::addFeature(const std::vector &line, const Shaping iconScale = properties.icon.allow_overlap ? iconPlacement.minScale - : collision.getPlacementScale(iconPlacement.boxes, iconPlacement.minScale); + : collision.getPlacementScale(iconPlacement.boxes, iconPlacement.minScale, avoidEdges); if (!iconScale && !textWithoutIcon) continue; } @@ -297,14 +301,14 @@ void SymbolBucket::addFeature(const std::vector &line, const Shaping collision.insert(glyphPlacement.boxes, anchor, glyphScale, glyphRange, horizontalText); } - addSymbols(text, glyphPlacement.shapes, glyphScale, glyphRange); + if (inside) addSymbols(text, glyphPlacement.shapes, glyphScale, glyphRange); } if (iconScale) { if (!properties.icon.ignore_placement) { collision.insert(iconPlacement.boxes, anchor, iconScale, iconRange, horizontalIcon); } - addSymbols(icon, iconPlacement.shapes, iconScale, iconRange); + if (inside) addSymbols(icon, iconPlacement.shapes, iconScale, iconRange); } } } diff --git a/src/style/style_parser.cpp b/src/style/style_parser.cpp index 43790e21b..02a783535 100644 --- a/src/style/style_parser.cpp +++ b/src/style/style_parser.cpp @@ -862,6 +862,7 @@ void StyleParser::parseRender(JSVal value, std::shared_ptr &layer) { } parseRenderProperty(value, render.min_distance, "symbol-min-distance"); + parseRenderProperty(value, render.avoid_edges, "symbol-avoid-edges"); parseRenderProperty(value, render.icon.allow_overlap, "icon-allow-overlap"); parseRenderProperty(value, render.icon.ignore_placement, "icon-ignore-placement"); diff --git a/src/text/collision.cpp b/src/text/collision.cpp index 462142356..f34aeb8b1 100644 --- a/src/text/collision.cpp +++ b/src/text/collision.cpp @@ -2,30 +2,23 @@ #include #include -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#ifndef __clang__ -#pragma GCC diagnostic ignored "-Wunused-local-typedefs" -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#endif -#include -#include -#include -#include -#pragma GCC diagnostic pop - -namespace mbgl { -namespace bg = boost::geometry; -namespace bgm = bg::model; -namespace bgi = bg::index; -typedef bgm::point Point; -typedef bgm::box Box; -typedef std::pair PlacementValue; -typedef bgi::rtree> Tree; -} using namespace mbgl; +Box getBox(const CollisionAnchor &anchor, const CollisionRect &bbox, float minScale, + float maxScale) { + return Box{ + Point{ + anchor.x + util::min(bbox.tl.x / minScale, bbox.tl.x / maxScale), + anchor.y + util::min(bbox.tl.y / minScale, bbox.tl.y / maxScale), + }, + Point{ + anchor.x + util::max(bbox.br.x / minScale, bbox.br.x / maxScale), + anchor.y + util::max(bbox.br.y / minScale, bbox.br.y / maxScale), + }, + }; +}; + Collision::~Collision() { delete ((Tree *)cTree); delete ((Tree *)hTree); @@ -52,32 +45,51 @@ Collision::Collision(float zoom, float tileExtent, float tileSize, float placeme const float m = 4096; const float edge = m * tilePixelRatio * 2; - // Hack to prevent cross-tile labels - insert( - /* glyphs */ { - GlyphBox{/* box */ CollisionRect{CollisionPoint{-edge, -edge}, CollisionPoint{0, edge}}, - /* minScale */ 0, - /* padding */ 2}, - GlyphBox{/* box */ CollisionRect{CollisionPoint{-edge, -edge}, CollisionPoint{edge, 0}}, - /* minScale */ 0, - /* padding */ 2}}, - /* anchor */ CollisionAnchor(0, 0), - /* placementScale */ 1, - /* placementRange */ {{M_PI * 2, 0}}, - /* horizontal */ false); - insert( - /* glyphs */ { - GlyphBox{/* box*/ CollisionRect{CollisionPoint{-edge, 0}, CollisionPoint{edge, edge}}, - /* minScale */ 0, - /* padding */ 2}, - GlyphBox{/* box */ CollisionRect{CollisionPoint{0, -edge}, CollisionPoint{edge, edge}}, - /* minScale */ 0, - /* padding */ 2}}, - /* anchor */ CollisionAnchor(m, m), - /* placementScale */ 1, - /* placementRange */ {{M_PI * 2, 0}}, - /* horizontal */ false); + PlacementBox left; + left.anchor = CollisionAnchor{ 0.0f, 0.0f }; + left.box = CollisionRect{CollisionPoint{-edge, -edge}, CollisionPoint{0, edge}}; + left.placementRange = {{ 2.0f * M_PI, 0.0f }}; + left.placementScale = 0.5f; + left.maxScale = std::numeric_limits::infinity(); + left.padding = 0.0f; + leftEdge = PlacementValue{ + getBox(left.anchor, left.box, left.placementScale, left.maxScale), + left}; + + PlacementBox top; + top.anchor = CollisionAnchor{ 0.0f, 0.0f }; + top.box = CollisionRect{CollisionPoint{-edge, -edge}, CollisionPoint{edge, 0}}; + top.placementRange = {{ 2.0f * M_PI, 0.0f }}; + top.placementScale = 0.5f; + top.maxScale = std::numeric_limits::infinity(); + top.padding = 0.0f; + topEdge = PlacementValue{ + getBox(top.anchor, top.box, top.placementScale, top.maxScale), + top}; + + PlacementBox bottom; + bottom.anchor = CollisionAnchor{ m, m }; + bottom.box = CollisionRect{CollisionPoint{-edge, 0}, CollisionPoint{edge, edge}}; + bottom.placementRange = {{ 2.0f * M_PI, 0.0f }}; + bottom.placementScale = 0.5f; + bottom.maxScale = std::numeric_limits::infinity(); + bottom.padding = 0.0f; + bottomEdge = PlacementValue{ + getBox(bottom.anchor, bottom.box, bottom.placementScale, bottom.maxScale), + bottom}; + + PlacementBox right; + right.anchor = CollisionAnchor{ m, m }; + right.box = CollisionRect{CollisionPoint{0, -edge}, CollisionPoint{edge, edge}}; + right.placementRange = {{ 2.0f * M_PI, 0.0f }}; + right.placementScale = 0.5f; + right.maxScale = std::numeric_limits::infinity(); + right.padding = 0.0f; + rightEdge = PlacementValue{ + getBox(right.anchor, right.box, right.placementScale, right.maxScale), + right}; + } GlyphBox getMergedGlyphs(const GlyphBoxes &boxes, const CollisionAnchor &anchor) { @@ -99,21 +111,7 @@ GlyphBox getMergedGlyphs(const GlyphBoxes &boxes, const CollisionAnchor &anchor) return mergedGlyphs; } -Box getBox(const CollisionAnchor &anchor, const CollisionRect &bbox, float minScale, - float maxScale) { - return Box{ - Point{ - anchor.x + util::min(bbox.tl.x / minScale, bbox.tl.x / maxScale), - anchor.y + util::min(bbox.tl.y / minScale, bbox.tl.y / maxScale), - }, - Point{ - anchor.x + util::max(bbox.br.x / minScale, bbox.br.x / maxScale), - anchor.y + util::max(bbox.br.y / minScale, bbox.br.y / maxScale), - }, - }; -}; - -float Collision::getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale) { +float Collision::getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale, bool avoidEdges) { for (const GlyphBox &glyph : glyphs) { const CollisionRect &box = glyph.box; @@ -121,6 +119,7 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs, float minPlacementS const CollisionAnchor &anchor = glyph.anchor; const float pad = glyph.padding; + if (anchor.x < 0 || anchor.x > 4096 || anchor.y < 0 || anchor.y > 4096) { return 0; } @@ -139,6 +138,13 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs, float minPlacementS ((Tree *)hTree)->query(bgi::intersects(searchBox), std::back_inserter(blocking)); ((Tree *)cTree)->query(bgi::intersects(searchBox), std::back_inserter(blocking)); + if (avoidEdges) { + if (searchBox.min_corner().get<0>() < 0) blocking.emplace_back(leftEdge); + if (searchBox.min_corner().get<1>() < 0) blocking.emplace_back(topEdge); + if (searchBox.max_corner().get<0>() >= 4096) blocking.emplace_back(rightEdge); + if (searchBox.max_corner().get<1>() >= 4096) blocking.emplace_back(bottomEdge); + } + if (blocking.size()) { const CollisionAnchor &na = anchor; // new anchor const CollisionRect &nb = box; // new box