allow crosstile labels and add symbol-avoid-edges

This commit is contained in:
Ansis Brammanis
2014-08-13 15:43:25 -07:00
committed by Konstantin Käfer
parent 69f71e9431
commit 6c45972502
6 changed files with 105 additions and 66 deletions

View File

@@ -40,6 +40,7 @@ public:
PlacementType placement = PlacementType::Point;
float min_distance = 250.0f;
bool avoid_edges = false;
struct {
bool allow_overlap = false;

View File

@@ -3,15 +3,35 @@
#include <mbgl/text/types.hpp>
#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 <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/index/rtree.hpp>
#pragma GCC diagnostic pop
namespace mbgl {
namespace bg = boost::geometry;
namespace bgm = bg::model;
namespace bgi = bg::index;
typedef bgm::point<float, 2, bg::cs::cartesian> Point;
typedef bgm::box<Point> Box;
typedef std::pair<Box, PlacementBox> PlacementValue;
typedef bgi::rtree<PlacementValue, bgi::rstar<16>> 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;

View File

@@ -18,6 +18,7 @@ void Painter::renderSymbol(SymbolBucket &bucket, std::shared_ptr<StyleLayer> lay
const SymbolProperties &properties = layer_desc->getProperties<SymbolProperties>();
glDisable(GL_STENCIL_TEST);
if (bucket.hasTextData()) {
mat4 exMatrix;
@@ -213,5 +214,7 @@ void Painter::renderSymbol(SymbolBucket &bucket, std::shared_ptr<StyleLayer> lay
depthRange(strata, 1.0f);
bucket.drawIcons(*iconShader);
}
glEnable(GL_STENCIL_TEST);
}
}

View File

@@ -212,6 +212,7 @@ void SymbolBucket::addFeature(const std::vector<Coordinate> &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<Coordinate> &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<Coordinate> &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<Coordinate> &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<Coordinate> &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);
}
}
}

View File

@@ -862,6 +862,7 @@ void StyleParser::parseRender(JSVal value, std::shared_ptr<StyleLayer> &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");

View File

@@ -2,30 +2,23 @@
#include <mbgl/text/rotation_range.hpp>
#include <mbgl/util/math.hpp>
#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 <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/index/rtree.hpp>
#pragma GCC diagnostic pop
namespace mbgl {
namespace bg = boost::geometry;
namespace bgm = bg::model;
namespace bgi = bg::index;
typedef bgm::point<float, 2, bg::cs::cartesian> Point;
typedef bgm::box<Point> Box;
typedef std::pair<Box, PlacementBox> PlacementValue;
typedef bgi::rtree<PlacementValue, bgi::rstar<16>> 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<float>::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<float>::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<float>::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<float>::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