From 6c4597250282f4e051ba52ad6447e7fe039fb5fa Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Wed, 13 Aug 2014 15:43:25 -0700 Subject: [PATCH 1/2] allow crosstile labels and add `symbol-avoid-edges` --- include/mbgl/style/style_bucket.hpp | 1 + include/mbgl/text/collision.hpp | 26 +++++- src/renderer/painter_symbol.cpp | 3 + src/renderer/symbol_bucket.cpp | 12 ++- src/style/style_parser.cpp | 1 + src/text/collision.cpp | 128 +++++++++++++++------------- 6 files changed, 105 insertions(+), 66 deletions(-) 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 From 937f82fcda03d382b3f2011d4ad4d60cb9d8d8e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Thu, 14 Aug 2014 13:21:36 +0200 Subject: [PATCH 2/2] use pimpl for collision in tile_parser --- include/mbgl/map/tile_parser.hpp | 5 +++-- include/mbgl/text/collision.hpp | 5 ++--- src/map/tile_parser.cpp | 14 +++++++++++--- src/renderer/symbol_bucket.cpp | 2 +- src/text/collision.cpp | 22 +++++++--------------- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/include/mbgl/map/tile_parser.hpp b/include/mbgl/map/tile_parser.hpp index f011d47a3..fa64dad6d 100644 --- a/include/mbgl/map/tile_parser.hpp +++ b/include/mbgl/map/tile_parser.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -28,6 +27,7 @@ class StyleBucketLine; class StyleBucketSymbol; class StyleLayerGroup; class VectorTileData; +class Collision; class TileParser { public: @@ -37,6 +37,7 @@ public: const std::shared_ptr &glyphStore, const std::shared_ptr &spriteAtlas, const std::shared_ptr &sprite); + ~TileParser(); public: void parse(); @@ -65,7 +66,7 @@ private: std::shared_ptr sprite; std::shared_ptr texturePool; - Collision collision; + std::unique_ptr collision; }; } diff --git a/include/mbgl/text/collision.hpp b/include/mbgl/text/collision.hpp index 6771765c1..e2e2414ba 100644 --- a/include/mbgl/text/collision.hpp +++ b/include/mbgl/text/collision.hpp @@ -29,7 +29,6 @@ class Collision { public: Collision(float zoom, float tileExtent, float tileSize, float placementDepth = 1); - ~Collision(); float getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale, bool avoidEdges); PlacementRange getPlacementRange(const GlyphBoxes &glyphs, float placementScale, @@ -38,8 +37,8 @@ public: const PlacementRange &placementRange, bool horizontal); private: - void *hTree; - void *cTree; + Tree hTree; + Tree cTree; PlacementValue leftEdge; PlacementValue topEdge; PlacementValue rightEdge; diff --git a/src/map/tile_parser.cpp b/src/map/tile_parser.cpp index 6069ad82c..6923c1a42 100644 --- a/src/map/tile_parser.cpp +++ b/src/map/tile_parser.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -29,7 +30,12 @@ namespace regex_impl = boost; namespace regex_impl = std; #endif -using namespace mbgl; +namespace mbgl { + +// Note: This destructor is seemingly empty, but we need to declare it anyway +// because this object has a std::unique_ptr<> of a forward-declare type in +// its header file. +TileParser::~TileParser() = default; TileParser::TileParser(const std::string &data, VectorTileData &tile, const std::shared_ptr &style, @@ -44,7 +50,7 @@ TileParser::TileParser(const std::string &data, VectorTileData &tile, glyphStore(glyphStore), spriteAtlas(spriteAtlas), sprite(sprite), - collision(tile.id.z, 4096, tile.source.tile_size, tile.depth) { + collision(std::make_unique(tile.id.z, 4096, tile.source.tile_size, tile.depth)) { } void TileParser::parse() { @@ -163,7 +169,9 @@ std::unique_ptr TileParser::createLineBucket(const VectorTileLayer& laye } std::unique_ptr TileParser::createSymbolBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketSymbol &symbol) { - std::unique_ptr bucket = std::make_unique(symbol, collision); + std::unique_ptr bucket = std::make_unique(symbol, *collision); bucket->addFeatures(layer, filter, tile.id, *spriteAtlas, *sprite, *glyphAtlas, *glyphStore); return obsolete() ? nullptr : std::move(bucket); } + +} diff --git a/src/renderer/symbol_bucket.cpp b/src/renderer/symbol_bucket.cpp index f10d44bb4..30110faf0 100644 --- a/src/renderer/symbol_bucket.cpp +++ b/src/renderer/symbol_bucket.cpp @@ -240,7 +240,7 @@ 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); + const bool inside = !(anchor.x < 0 || anchor.x > 4096 || anchor.y < 0 || anchor.y > 4096); if (avoidEdges && !inside) continue; diff --git a/src/text/collision.cpp b/src/text/collision.cpp index f34aeb8b1..89e91d684 100644 --- a/src/text/collision.cpp +++ b/src/text/collision.cpp @@ -19,17 +19,9 @@ Box getBox(const CollisionAnchor &anchor, const CollisionRect &bbox, float minSc }; }; -Collision::~Collision() { - delete ((Tree *)cTree); - delete ((Tree *)hTree); -} - Collision::Collision(float zoom, float tileExtent, float tileSize, float placementDepth) - : hTree(new Tree()), // tree for horizontal labels - cTree(new Tree()), // tree for glyphs from curved labels - // tile pixels per screen pixels at the tile's zoom level - tilePixelRatio(tileExtent / tileSize), + : tilePixelRatio(tileExtent / tileSize), zoom(zoom), @@ -135,8 +127,8 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs, float minPlacementS const Box searchBox = getBox(anchor, bbox, minScale, maxScale); std::vector blocking; - ((Tree *)hTree)->query(bgi::intersects(searchBox), std::back_inserter(blocking)); - ((Tree *)cTree)->query(bgi::intersects(searchBox), std::back_inserter(blocking)); + hTree.query(bgi::intersects(searchBox), std::back_inserter(blocking)); + cTree.query(bgi::intersects(searchBox), std::back_inserter(blocking)); if (avoidEdges) { if (searchBox.min_corner().get<0>() < 0) blocking.emplace_back(leftEdge); @@ -218,10 +210,10 @@ PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs, float plac Box query_box{Point{minPlacedX, minPlacedY}, Point{maxPlacedX, maxPlacedY}}; std::vector blocking; - ((Tree *)hTree)->query(bgi::intersects(query_box), std::back_inserter(blocking)); + hTree.query(bgi::intersects(query_box), std::back_inserter(blocking)); if (horizontal) { - ((Tree *)cTree)->query(bgi::intersects(query_box), std::back_inserter(blocking)); + cTree.query(bgi::intersects(query_box), std::back_inserter(blocking)); } for (const PlacementValue &value : blocking) { @@ -298,8 +290,8 @@ void Collision::insert(const GlyphBoxes &glyphs, const CollisionAnchor &anchor, // Bulk-insert all glyph boxes if (horizontal) { - ((Tree *)hTree)->insert(allBounds.begin(), allBounds.end()); + hTree.insert(allBounds.begin(), allBounds.end()); } else { - ((Tree *)cTree)->insert(allBounds.begin(), allBounds.end()); + cTree.insert(allBounds.begin(), allBounds.end()); } }