allow crosstile labels and add symbol-avoid-edges
This commit is contained in:
committed by
Konstantin Käfer
parent
69f71e9431
commit
6c45972502
@@ -40,6 +40,7 @@ public:
|
||||
|
||||
PlacementType placement = PlacementType::Point;
|
||||
float min_distance = 250.0f;
|
||||
bool avoid_edges = false;
|
||||
|
||||
struct {
|
||||
bool allow_overlap = false;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user