summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2014-08-04 18:24:22 +0200
committerKonstantin Käfer <mail@kkaefer.com>2014-08-04 18:24:22 +0200
commit36181b0922bf4b5bed5b5ca68679338d22dfcf31 (patch)
treebb4d78934e41b59eb80842414908846b2789dc4c
parent9dd50a29e1e8b975e09172b19b303063a96ba20f (diff)
downloadqtlocation-mapboxgl-36181b0922bf4b5bed5b5ca68679338d22dfcf31.tar.gz
update symbol placement to be more like js
-rw-r--r--include/mbgl/geometry/interpolate.hpp2
-rw-r--r--include/mbgl/map/tile_parser.hpp4
-rw-r--r--include/mbgl/map/vector_tile.hpp2
-rw-r--r--include/mbgl/renderer/symbol_bucket.hpp38
-rw-r--r--include/mbgl/style/style_bucket.hpp1
-rw-r--r--include/mbgl/text/collision.hpp17
-rw-r--r--include/mbgl/text/glyph.hpp9
-rw-r--r--include/mbgl/text/placement.hpp32
-rw-r--r--include/mbgl/text/types.hpp29
-rw-r--r--src/geometry/interpolate.cpp27
-rw-r--r--src/map/tile_parser.cpp4
-rw-r--r--src/map/vector_tile_data.cpp1
-rw-r--r--src/renderer/painter_symbol.cpp50
-rw-r--r--src/renderer/symbol_bucket.cpp403
-rw-r--r--src/style/style_parser.cpp1
-rw-r--r--src/text/collision.cpp184
-rw-r--r--src/text/glyph_store.cpp6
-rw-r--r--src/text/placement.cpp335
18 files changed, 574 insertions, 571 deletions
diff --git a/include/mbgl/geometry/interpolate.hpp b/include/mbgl/geometry/interpolate.hpp
index cebbcc7028..998cba31cd 100644
--- a/include/mbgl/geometry/interpolate.hpp
+++ b/include/mbgl/geometry/interpolate.hpp
@@ -7,7 +7,7 @@
namespace mbgl {
Anchors interpolate(const std::vector<Coordinate> &vertices, float spacing,
- float minScale = 0.0f, int start = 0);
+ float minScale, float maxScale, float tilePixelRatio, int start = 0);
}
#endif
diff --git a/include/mbgl/map/tile_parser.hpp b/include/mbgl/map/tile_parser.hpp
index b407502434..9aa0f4dc66 100644
--- a/include/mbgl/map/tile_parser.hpp
+++ b/include/mbgl/map/tile_parser.hpp
@@ -4,7 +4,7 @@
#include <mbgl/map/vector_tile.hpp>
#include <mbgl/style/filter_expression.hpp>
#include <mbgl/text/glyph.hpp>
-#include <mbgl/text/placement.hpp>
+#include <mbgl/text/collision.hpp>
#include <cstdint>
#include <iosfwd>
@@ -61,7 +61,7 @@ private:
std::shared_ptr<SpriteAtlas> spriteAtlas;
std::shared_ptr<Sprite> sprite;
- Placement placement;
+ Collision collision;
};
}
diff --git a/include/mbgl/map/vector_tile.hpp b/include/mbgl/map/vector_tile.hpp
index 96123c154b..1bbe645307 100644
--- a/include/mbgl/map/vector_tile.hpp
+++ b/include/mbgl/map/vector_tile.hpp
@@ -86,7 +86,7 @@ private:
const FilterExpression& filterExpression;
};
-std::ostream& operator<<(std::ostream&, const GlyphPlacement& placement);
+std::ostream& operator<<(std::ostream&, const PositionedGlyph& placement);
class VectorTileLayer {
public:
diff --git a/include/mbgl/renderer/symbol_bucket.hpp b/include/mbgl/renderer/symbol_bucket.hpp
index c2a8b14f38..c003230cae 100644
--- a/include/mbgl/renderer/symbol_bucket.hpp
+++ b/include/mbgl/renderer/symbol_bucket.hpp
@@ -21,7 +21,7 @@ class Style;
class TextShader;
class IconShader;
class DotShader;
-class Placement;
+class Collision;
class SpriteAtlas;
class Sprite;
class GlyphAtlas;
@@ -35,13 +35,23 @@ public:
std::string sprite;
};
+
+class Symbol {
+public:
+ vec2<float> tl, tr, bl, br;
+ Rect<uint16_t> tex;
+ float angle;
+ float minScale = 0.0f;
+ float maxScale = std::numeric_limits<float>::infinity();
+ CollisionAnchor anchor;
+};
+
+typedef std::vector<Symbol> Symbols;
+
+
class SymbolBucket : public Bucket {
public:
- SymbolBucket(
- TextVertexBuffer &textVertexBuffer,
- IconVertexBuffer& iconVertexBuffer,
- TriangleElementsBuffer &triangleElementsBuffer,
- const StyleBucketSymbol &properties, Placement &placement);
+ SymbolBucket(const StyleBucketSymbol &properties, Collision &collision);
virtual void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID &id);
virtual bool hasData() const;
@@ -55,29 +65,31 @@ public:
void addGlyphs(const PlacedGlyphs &glyphs, float placementZoom, PlacementRange placementRange,
float zoom);
-
void drawGlyphs(TextShader &shader);
void drawIcons(IconShader& shader);
- void drawIcons(DotShader& shader);
private:
std::vector<SymbolFeature> processFeatures(const VectorTileLayer &layer, const FilterExpression &filter, GlyphStore &glyphStore, const Sprite &sprite);
- void addFeature(const pbf &geom_pbf, const Shaping &shaping, const Rect<uint16_t> &image);
- void addFeature(const std::vector<Coordinate> &line, const Shaping &shaping, const Rect<uint16_t> &image);
+ void addFeature(const pbf &geom_pbf, const Shaping &shaping, const GlyphPositions &face, const Rect<uint16_t> &image);
+ void addFeature(const std::vector<Coordinate> &line, const Shaping &shaping, const GlyphPositions &face, const Rect<uint16_t> &image);
+
+ // Adds placed items to the buffer.
+ template <typename Buffer>
+ void addSymbols(Buffer &buffer, const PlacedGlyphs &symbols, float scale, PlacementRange placementRange);
- void addGlyph(uint64_t tileid, const std::string stackname, const std::u32string &string,
+ // Adds glyphs to the glyph atlas so that they have a left/top/width/height coordinates associated to them that we can use for writing to a buffer.
+ void addGlyphsToAtlas(uint64_t tileid, const std::string stackname, const std::u32string &string,
const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face);
- void addFeature(const pbf &geom_pbf, const GlyphPositions &face, const Shaping &shaping);
public:
const StyleBucketSymbol &properties;
private:
- Placement &placement;
+ Collision &collision;
struct {
TextVertexBuffer vertices;
diff --git a/include/mbgl/style/style_bucket.hpp b/include/mbgl/style/style_bucket.hpp
index af76650e56..a868659ad7 100644
--- a/include/mbgl/style/style_bucket.hpp
+++ b/include/mbgl/style/style_bucket.hpp
@@ -48,6 +48,7 @@ public:
RotationAlignmentType rotation_alignment = RotationAlignmentType::Viewport;
float max_size = 1.0f;
std::string image;
+ float rotate = 0.0f;
float padding = 2.0f;
bool keep_upright = false;
vec2<float> offset = {0, 0};
diff --git a/include/mbgl/text/collision.hpp b/include/mbgl/text/collision.hpp
index 8911b3b4c6..87ebdb279e 100644
--- a/include/mbgl/text/collision.hpp
+++ b/include/mbgl/text/collision.hpp
@@ -11,20 +11,17 @@ public:
Collision(float zoom, float tileExtent, float tileSize, float placementDepth = 1);
~Collision();
- PlacementProperty place(const GlyphBoxes &boxes, const CollisionAnchor &anchor,
- float minPlacementScale, float maxPlacementScale, float padding,
- bool horizontal, bool allowOverlap, bool ignorePlacement);
- float getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale,
- float maxPlacementScale, float pad);
- PlacementRange getPlacementRange(const GlyphBoxes &glyphs,
- float placementScale, bool horizontal);
- void insert(const GlyphBoxes &glyphs, const CollisionAnchor &anchor,
- float placementScale, const PlacementRange &placementRange,
- bool horizontal, float padding);
+ float getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale);
+ PlacementRange getPlacementRange(const GlyphBoxes &glyphs, float placementScale,
+ bool horizontal);
+ void insert(const GlyphBoxes &glyphs, const CollisionAnchor &anchor, float placementScale,
+ const PlacementRange &placementRange, bool horizontal);
private:
void *hTree;
void *cTree;
+
+public:
const float tilePixelRatio;
const float zoom;
const float maxPlacementScale;
diff --git a/include/mbgl/text/glyph.hpp b/include/mbgl/text/glyph.hpp
index 899c8fffee..4ad2e93679 100644
--- a/include/mbgl/text/glyph.hpp
+++ b/include/mbgl/text/glyph.hpp
@@ -44,18 +44,17 @@ struct Glyph {
typedef std::map<uint32_t, Glyph> GlyphPositions;
-class GlyphPlacement {
+class PositionedGlyph {
public:
- inline explicit GlyphPlacement(uint32_t face, uint32_t glyph, uint32_t x, uint32_t y)
- : face(face), glyph(glyph), x(x), y(y) {}
+ inline explicit PositionedGlyph(uint32_t glyph, uint32_t x, uint32_t y)
+ : glyph(glyph), x(x), y(y) {}
- uint32_t face = 0;
uint32_t glyph = 0;
int32_t x = 0;
int32_t y = 0;
};
-typedef std::vector<GlyphPlacement> Shaping;
+typedef std::vector<PositionedGlyph> Shaping;
}
#endif
diff --git a/include/mbgl/text/placement.hpp b/include/mbgl/text/placement.hpp
index 7700f32f0a..28eb8d5317 100644
--- a/include/mbgl/text/placement.hpp
+++ b/include/mbgl/text/placement.hpp
@@ -1,36 +1,30 @@
#ifndef MBGL_TEXT_PLACEMENT
#define MBGL_TEXT_PLACEMENT
-#include <mbgl/text/collision.hpp>
-#include <mbgl/geometry/geometry.hpp>
-#include <mbgl/util/vec.hpp>
+#include <mbgl/text/types.hpp>
#include <mbgl/text/glyph.hpp>
-#include <vector>
+
+#include <mbgl/util/vec.hpp>
namespace mbgl {
-class SymbolBucket;
+struct Anchor;
class StyleBucketSymbol;
class Placement {
public:
- Placement(int8_t zoom, float placementDepth);
+ static Placement getIcon(Anchor &anchor, const Rect<uint16_t> &image, float iconBoxScale,
+ const std::vector<Coordinate> &line, const StyleBucketSymbol &props);
- void addFeature(SymbolBucket &bucket, const std::vector<Coordinate> &line,
- const StyleBucketSymbol &info,
- const GlyphPositions &face,
- const Shaping &shaping);
+ static Placement getGlyphs(Anchor &anchor, const vec2<float> &origin, const Shaping &shaping,
+ const GlyphPositions &face, float boxScale, bool horizontal,
+ const std::vector<Coordinate> &line, const StyleBucketSymbol &props);
-private:
- const int8_t zoom;
- Collision collision;
- const float zOffset;
- const float maxPlacementScale;
+ static const float globalMinScale;
-public:
- static const int tileExtent;
- static const int glyphSize;
- static const float minScale;
+ GlyphBoxes boxes;
+ PlacedGlyphs shapes;
+ float minScale;
};
}
diff --git a/include/mbgl/text/types.hpp b/include/mbgl/text/types.hpp
index 027df6d637..e2539bff62 100644
--- a/include/mbgl/text/types.hpp
+++ b/include/mbgl/text/types.hpp
@@ -37,16 +37,23 @@ struct CollisionRect {
// These are the glyph boxes that we want to have placed.
struct GlyphBox {
explicit GlyphBox() {}
- explicit GlyphBox(const CollisionRect &box, float minScale) : box(box), minScale(minScale) {}
- explicit GlyphBox(const CollisionRect &box, float minScale, float maxScale,
- const CollisionAnchor &anchor)
- : anchor(anchor), box(box), minScale(minScale), maxScale(maxScale) {}
+ explicit GlyphBox(const CollisionRect &box,
+ const CollisionAnchor &anchor,
+ float minScale,
+ float maxScale,
+ float padding)
+ : box(box), anchor(anchor), minScale(minScale), maxScale(maxScale), padding(padding) {}
+ explicit GlyphBox(const CollisionRect &box,
+ float minScale,
+ float padding)
+ : box(box), minScale(minScale), padding(padding) {}
- CollisionAnchor anchor;
CollisionRect box;
- boost::optional<CollisionRect> hBox;
+ CollisionAnchor anchor;
float minScale = 0.0f;
float maxScale = std::numeric_limits<float>::infinity();
+ float padding = 0.0f;
+ boost::optional<CollisionRect> hBox;
};
typedef std::vector<GlyphBox> GlyphBoxes;
@@ -56,19 +63,23 @@ typedef std::vector<GlyphBox> GlyphBoxes;
struct PlacedGlyph {
explicit PlacedGlyph(const vec2<float> &tl, const vec2<float> &tr,
const vec2<float> &bl, const vec2<float> &br,
- const Rect<uint16_t> &tex, float angle, const GlyphBox &glyphBox)
+ const Rect<uint16_t> &tex, float angle, const vec2<float> &anchor,
+ float minScale, float maxScale)
: tl(tl),
tr(tr),
bl(bl),
br(br),
tex(tex),
angle(angle),
- glyphBox(glyphBox) {}
+ anchor(anchor),
+ minScale(minScale),
+ maxScale(maxScale) {}
vec2<float> tl, tr, bl, br;
Rect<uint16_t> tex;
float angle;
- GlyphBox glyphBox;
+ vec2<float> anchor;
+ float minScale, maxScale;
};
typedef std::vector<PlacedGlyph> PlacedGlyphs;
diff --git a/src/geometry/interpolate.cpp b/src/geometry/interpolate.cpp
index d9cfdfa1db..af62bd921c 100644
--- a/src/geometry/interpolate.cpp
+++ b/src/geometry/interpolate.cpp
@@ -1,9 +1,30 @@
#include <mbgl/geometry/interpolate.hpp>
+#include <mbgl/util/math.hpp>
+
+#include <cmath>
+
namespace mbgl {
+const float minScale = 0.5f;
+const std::array<std::vector<float>, 4> minScaleArrays = {{
+ /*1:*/ { minScale },
+ /*2:*/ { minScale, 2 },
+ /*4:*/ { minScale, 4, 2, 4 },
+ /*8:*/ { minScale, 8, 4, 8, 2, 8, 4, 8 }
+}};
+
+
Anchors interpolate(const std::vector<Coordinate> &vertices, float spacing,
- float minScale, int start) {
+ const float minScale, float maxScale, const float tilePixelRatio,
+ const int start) {
+
+ maxScale = std::round(std::fmax(std::fmin(8.0f, maxScale / 2.0f), 1.0f));
+ spacing *= tilePixelRatio / maxScale;
+ const size_t index = util::clamp<size_t>(std::floor(std::log(maxScale) / std::log(2)), 0, minScaleArrays.size() - 1);
+ const std::vector<float> &minScales = minScaleArrays[index];
+ const size_t len = minScales.size();
+
float distance = 0.0f;
float markedDistance = 0.0f;
int added = start;
@@ -23,9 +44,7 @@ Anchors interpolate(const std::vector<Coordinate> &vertices, float spacing,
float t = (markedDistance - distance) / segmentDist,
x = util::interp(a.x, b.x, t), y = util::interp(a.y, b.y, t),
- s = added % 8 == 0 ? minScale : added % 4 == 0
- ? 2
- : added % 2 == 0 ? 4 : 8;
+ s = minScales[added % len];
if (x >= 0 && x < 4096 && y >= 0 && y < 4096) {
points.emplace_back(x, y, angle, s, i);
diff --git a/src/map/tile_parser.cpp b/src/map/tile_parser.cpp
index 99ee676237..89ca4a41f3 100644
--- a/src/map/tile_parser.cpp
+++ b/src/map/tile_parser.cpp
@@ -43,7 +43,7 @@ TileParser::TileParser(const std::string &data, VectorTileData &tile,
glyphStore(glyphStore),
spriteAtlas(spriteAtlas),
sprite(sprite),
- placement(tile.id.z, tile.id.z >= tile.source.max_zoom ? tile.source.max_zoom - tile.id.z : 1) {
+ collision(tile.id.z, 4096, tile.source.tile_size, tile.id.z >= tile.source.max_zoom ? tile.source.max_zoom - tile.id.z : 1) {
}
void TileParser::parse() {
@@ -154,7 +154,7 @@ std::unique_ptr<Bucket> TileParser::createLineBucket(const VectorTileLayer& laye
}
std::unique_ptr<Bucket> TileParser::createSymbolBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketSymbol &symbol) {
- std::unique_ptr<SymbolBucket> bucket = std::make_unique<SymbolBucket>(tile.textVertexBuffer, tile.iconVertexBuffer, tile.triangleElementsBuffer, symbol, placement);
+ std::unique_ptr<SymbolBucket> bucket = std::make_unique<SymbolBucket>(symbol, collision);
bucket->addFeatures(layer, filter, tile.id, *spriteAtlas, *sprite, *glyphAtlas, *glyphStore);
return obsolete() ? nullptr : std::move(bucket);
}
diff --git a/src/map/vector_tile_data.cpp b/src/map/vector_tile_data.cpp
index 99c521b8ab..cc4133e453 100644
--- a/src/map/vector_tile_data.cpp
+++ b/src/map/vector_tile_data.cpp
@@ -20,6 +20,7 @@ VectorTileData::~VectorTileData() {
}
void VectorTileData::beforeParse() {
+
parser = std::make_unique<TileParser>(data, *this, map.getStyle(), map.getGlyphAtlas(), map.getGlyphStore(), map.getSpriteAtlas(), map.getSprite());
}
diff --git a/src/renderer/painter_symbol.cpp b/src/renderer/painter_symbol.cpp
index 8201e4b7f1..ee55cfe36a 100644
--- a/src/renderer/painter_symbol.cpp
+++ b/src/renderer/painter_symbol.cpp
@@ -172,31 +172,31 @@ void Painter::renderSymbol(SymbolBucket &bucket, std::shared_ptr<StyleLayer> lay
}
if (bucket.hasIconData()) {
- SpriteAtlas &spriteAtlas = *map.getSpriteAtlas();
-
- useProgram(iconShader->program);
- iconShader->setMatrix(matrix);
-
- // TODO: update
- iconShader->setColor({{1, 1, 1, 1}});
- iconShader->setImage(0);
- iconShader->setRatio(map.getState().getPixelRatio());
- iconShader->setDimension({{
- spriteAtlas.getTextureWidth(), spriteAtlas.getTextureHeight(),
- }});
-
- spriteAtlas.bind(map.getState().isChanging());
-
- // TODO: remove hardcoded icon size.
- const float iconSize = 12 * map.getState().getPixelRatio();
- iconShader->setSize(iconSize);
-#ifndef GL_ES_VERSION_2_0
- glPointSize(iconSize);
- glEnable(GL_POINT_SPRITE);
-#endif
-
- glDepthRange(strata, 1.0f);
- bucket.drawIcons(*iconShader);
+// SpriteAtlas &spriteAtlas = *map.getSpriteAtlas();
+//
+// useProgram(iconShader->program);
+// iconShader->setMatrix(matrix);
+//
+// // TODO: update
+// iconShader->setColor({{1, 1, 1, 1}});
+// iconShader->setImage(0);
+// iconShader->setRatio(map.getState().getPixelRatio());
+// iconShader->setDimension({{
+// spriteAtlas.getTextureWidth(), spriteAtlas.getTextureHeight(),
+// }});
+//
+// spriteAtlas.bind(map.getState().isChanging());
+//
+// // TODO: remove hardcoded icon size.
+// const float iconSize = 12 * map.getState().getPixelRatio();
+// iconShader->setSize(iconSize);
+//#ifndef GL_ES_VERSION_2_0
+// glPointSize(iconSize);
+// glEnable(GL_POINT_SPRITE);
+//#endif
+//
+// glDepthRange(strata, 1.0f);
+// bucket.drawIcons(*iconShader);
}
}
}
diff --git a/src/renderer/symbol_bucket.cpp b/src/renderer/symbol_bucket.cpp
index 03e5c91f26..f165515aff 100644
--- a/src/renderer/symbol_bucket.cpp
+++ b/src/renderer/symbol_bucket.cpp
@@ -4,60 +4,54 @@
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
#include <mbgl/geometry/geometry.hpp>
+#include <mbgl/geometry/anchor.hpp>
+#include <mbgl/geometry/interpolate.hpp>
#include <mbgl/renderer/painter.hpp>
#include <mbgl/text/glyph_store.hpp>
#include <mbgl/text/placement.hpp>
#include <mbgl/platform/log.hpp>
+#include <mbgl/text/collision.hpp>
#include <mbgl/map/sprite.hpp>
#include <mbgl/util/utf.hpp>
#include <mbgl/util/token.hpp>
#include <mbgl/util/math.hpp>
-
namespace mbgl {
-SymbolBucket::SymbolBucket(TextVertexBuffer &textVertexBuffer, IconVertexBuffer &iconVertexBuffer,
- TriangleElementsBuffer &triangleElementsBuffer,
- const StyleBucketSymbol &properties, Placement &placement)
- : properties(properties),
- placement(placement)
- {}
+SymbolBucket::SymbolBucket(const StyleBucketSymbol &properties, Collision &collision)
+ : properties(properties), collision(collision) {}
void SymbolBucket::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc,
- const Tile::ID &id) {
- painter.renderSymbol(*this, layer_desc, id);
+ const Tile::ID &id) {
+ painter.renderSymbol(*this, layer_desc, id);
}
-bool SymbolBucket::hasData() const {
- return hasTextData() || hasIconData();
-}
+bool SymbolBucket::hasData() const { return hasTextData() || hasIconData(); }
+bool SymbolBucket::hasTextData() const { return !text.groups.empty(); }
-bool SymbolBucket::hasTextData() const {
- return !text.groups.empty();
-}
-
-bool SymbolBucket::hasIconData() const {
- return !icon.groups.empty() > 0;
-}
+bool SymbolBucket::hasIconData() const { return !icon.groups.empty() > 0; }
-void SymbolBucket::addGlyph(uint64_t tileid, const std::string stackname,
- const std::u32string &string, const FontStack &fontStack,
- GlyphAtlas &glyphAtlas, GlyphPositions &face) {
+void SymbolBucket::addGlyphsToAtlas(uint64_t tileid, const std::string stackname,
+ const std::u32string &string, const FontStack &fontStack,
+ GlyphAtlas &glyphAtlas, GlyphPositions &face) {
const std::map<uint32_t, SDFGlyph> &sdfs = fontStack.getSDFs();
// Loop through all characters and add glyph to atlas, positions.
for (uint32_t chr : string) {
auto sdf_it = sdfs.find(chr);
if (sdf_it != sdfs.end()) {
- const SDFGlyph& sdf = sdf_it->second;
+ const SDFGlyph &sdf = sdf_it->second;
const Rect<uint16_t> rect = glyphAtlas.addGlyph(tileid, stackname, sdf);
face.emplace(chr, Glyph{rect, sdf.metrics});
}
}
}
-std::vector<SymbolFeature> SymbolBucket::processFeatures(const VectorTileLayer &layer, const FilterExpression &filter, GlyphStore &glyphStore, const Sprite &sprite) {
+std::vector<SymbolFeature> SymbolBucket::processFeatures(const VectorTileLayer &layer,
+ const FilterExpression &filter,
+ GlyphStore &glyphStore,
+ const Sprite &sprite) {
const bool text = properties.text.field.size();
const bool icon = properties.icon.image.size();
@@ -74,14 +68,14 @@ std::vector<SymbolFeature> SymbolBucket::processFeatures(const VectorTileLayer &
FilteredVectorTileLayer filtered_layer(layer, filter);
for (const pbf &feature_pbf : filtered_layer) {
- const VectorTileFeature feature {feature_pbf, layer};
+ const VectorTileFeature feature{feature_pbf, layer};
SymbolFeature ft;
if (text) {
std::string u8string = util::replaceTokens(properties.text.field, feature.properties);
- auto& convert = std::use_facet<std::ctype<char>>(std::locale());
+ auto &convert = std::use_facet<std::ctype<char>>(std::locale());
if (properties.text.transform == TextTransformType::Uppercase) {
convert.toupper(&u8string[0], &u8string[0] + u8string.size());
} else if (properties.text.transform == TextTransformType::Lowercase) {
@@ -114,33 +108,42 @@ std::vector<SymbolFeature> SymbolBucket::processFeatures(const VectorTileLayer &
return features;
}
-
-
-
void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpression &filter,
- const Tile::ID &id, SpriteAtlas &spriteAtlas, Sprite &sprite, GlyphAtlas &glyphAtlas,
- GlyphStore &glyphStore) {
+ const Tile::ID &id, SpriteAtlas &spriteAtlas, Sprite &sprite,
+ GlyphAtlas &glyphAtlas, GlyphStore &glyphStore) {
const std::vector<SymbolFeature> features = processFeatures(layer, filter, glyphStore, sprite);
-
float horizontalAlign = 0.5;
- if (properties.text.horizontal_align == TextHorizontalAlignType::Right) horizontalAlign = 1;
- else if (properties.text.horizontal_align == TextHorizontalAlignType::Left) horizontalAlign = 0;
+ if (properties.text.horizontal_align == TextHorizontalAlignType::Right)
+ horizontalAlign = 1;
+ else if (properties.text.horizontal_align == TextHorizontalAlignType::Left)
+ horizontalAlign = 0;
float verticalAlign = 0.5;
- if (properties.text.vertical_align == TextVerticalAlignType::Bottom) verticalAlign = 1;
- else if (properties.text.vertical_align == TextVerticalAlignType::Top) verticalAlign = 0;
+ if (properties.text.vertical_align == TextVerticalAlignType::Bottom)
+ verticalAlign = 1;
+ else if (properties.text.vertical_align == TextVerticalAlignType::Top)
+ verticalAlign = 0;
const FontStack &fontStack = glyphStore.getFontStack(properties.text.font);
for (const SymbolFeature &feature : features) {
Shaping shaping;
Rect<uint16_t> image;
+ GlyphPositions face;
// if feature has text, shape the text
if (feature.label.length()) {
- shaping = fontStack.getShaping(feature.label, properties.text.max_width, properties.text.line_height, horizontalAlign, verticalAlign, properties.text.letter_spacing);
+ shaping = fontStack.getShaping(feature.label, properties.text.max_width,
+ properties.text.line_height, horizontalAlign,
+ verticalAlign, properties.text.letter_spacing);
+
+ // Add the glyphs we need for this label to the glyph atlas.
+ if (shaping.size()) {
+ addGlyphsToAtlas(id.to_uint64(), properties.text.font, feature.label, fontStack,
+ glyphAtlas, face);
+ }
}
// if feature has icon, get sprite atlas position
@@ -150,12 +153,13 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress
// if either shaping or icon position is present, add the feature
if (shaping.size() || image) {
- addFeature(feature.geometry, shaping, image);
+ addFeature(feature.geometry, shaping, face, image);
}
}
}
-void SymbolBucket::addFeature(const pbf &geom_pbf, const Shaping &shaping, const Rect<uint16_t> &image) {
+void SymbolBucket::addFeature(const pbf &geom_pbf, const Shaping &shaping,
+ const GlyphPositions &face, const Rect<uint16_t> &image) {
// Decode all lines.
std::vector<Coordinate> line;
Geometry::command cmd;
@@ -165,167 +169,155 @@ void SymbolBucket::addFeature(const pbf &geom_pbf, const Shaping &shaping, const
Geometry geometry(geom);
int32_t x, y;
while ((cmd = geometry.next(x, y)) != Geometry::end) {
- if (cmd == Geometry::move_to) {
- if (!line.empty()) {
- addFeature(line, shaping, image);
- line.clear();
- }
- }
- line.emplace_back(x, y);
+ if (cmd == Geometry::move_to) {
+ if (!line.empty()) {
+ addFeature(line, shaping, face, image);
+ line.clear();
+ }
+ }
+ line.emplace_back(x, y);
}
if (line.size()) {
- addFeature(line, shaping, image);
+ addFeature(line, shaping, face, image);
}
}
-void SymbolBucket::addFeature(const std::vector<Coordinate> &line, const Shaping &shaping, const Rect<uint16_t> &image) {
- const float minScale = 0.5f;
- const float glyphSize = 24.0f;
+bool byScale(const Anchor &a, const Anchor &b) { return a.scale < b.scale; }
+const PlacementRange fullRange{{2 * M_PI, 0}};
- const bool horizontalText = properties.text.rotation_alignment == RotationAlignmentType::Viewport;
- const bool horizontalIcon = properties.icon.rotation_alignment == RotationAlignmentType::Viewport;
- const float fontScale = properties.text.max_size / glyphSize;
- const float textBoxScale = placement.collision.tilePixelRatio * fontScale,
- iconBoxScale = collision.tilePixelRatio * info['icon-max-size'],
- iconWithoutText = info['text-optional'] || !shaping,
- textWithoutIcon = info['icon-optional'] || !image;
-}
+void SymbolBucket::addFeature(const std::vector<Coordinate> &line, const Shaping &shaping,
+ const GlyphPositions &face, const Rect<uint16_t> &image) {
+ assert(line.size());
+ const float minScale = 0.5f;
+ const float glyphSize = 24.0f;
-//
-// GlyphPositions face;
-//
-//
-// // Shape and place all labels.
-// for (const std::pair<std::u32string, pbf> &label : labels) {
-//
-// // Shape labels.
-// const Shaping shaping = fontStack.getShaping(label.first, properties.text.max_width,
-// properties.text.line_height, horizontalAlign,
-// verticalAlign, properties.text.letter_spacing);
-//
-// addGlyph(id.to_uint64(), properties.text.font, label.first, fontStack, glyphAtlas, face);
-//
-// // Place labels.
-// addFeature(label.second, face, shaping);
-// }
-}
-
-void SymbolBucket::addFeature(const pbf &geom_pbf, const GlyphPositions &face,
- const Shaping &shaping) {
- // Decode all lines.
- std::vector<Coordinate> line;
- Geometry::command cmd;
-
- Coordinate coord;
- pbf geom(geom_pbf);
- Geometry geometry(geom);
- int32_t x, y;
- while ((cmd = geometry.next(x, y)) != Geometry::end) {
- if (cmd == Geometry::move_to) {
- if (!line.empty()) {
- placement.addFeature(*this, line, properties, face, shaping);
- line.clear();
- }
- }
- line.emplace_back(x, y);
- }
- if (line.size()) {
- placement.addFeature(*this, line, properties, face, shaping);
- }
-}
-
+ const bool horizontalText =
+ properties.text.rotation_alignment == RotationAlignmentType::Viewport;
+ const bool horizontalIcon =
+ properties.icon.rotation_alignment == RotationAlignmentType::Viewport;
+ const float fontScale = properties.text.max_size / glyphSize;
+ const float textBoxScale = collision.tilePixelRatio * fontScale;
+ const float iconBoxScale = collision.tilePixelRatio * properties.icon.max_size;
+ const bool iconWithoutText = properties.text.optional || !shaping.size();
+ const bool textWithoutIcon = properties.icon.optional || !image;
-class Symbol {
-public:
- vec2<float> tl, tr, bl, br;
- Rect<uint16_t> tex;
- float angle;
- float minScale = 0.0f;
- float maxScale = std::numeric_limits<float>::infinity();
- CollisionAnchor anchor;
-};
+ Anchors anchors;
-typedef std::vector<Symbol> Symbols;
+ if (properties.placement == PlacementType::Line) {
+ // Line labels
+ anchors = interpolate(line, properties.min_distance, minScale, collision.maxPlacementScale,
+ collision.tilePixelRatio);
-template <typename Buffer>
-void SymbolBucket::addSymbols(Buffer &buffer, const Symbols &symbols, float placementZoom, PlacementRange placementRange, float zoom) {
- placementZoom += zoom;
+ // Sort anchors by segment so that we can start placement with the
+ // anchors that can be shown at the lowest zoom levels.
+ std::sort(anchors.begin(), anchors.end(), byScale);
- for (const Symbol &symbol : symbols) {
- const auto &tl = symbol.tl;
- const auto &tr = symbol.tr;
- const auto &bl = symbol.bl;
- const auto &br = symbol.br;
- const auto &tex = symbol.tex;
- const auto &angle = symbol.angle;
-
- float minZoom = util::max(static_cast<float>(zoom + log(symbol.minScale) / log(2)),
- placementZoom);
- float maxZoom =
- util::min(static_cast<float>(zoom + log(symbol.maxScale) / log(2)), 25.0f);
- const auto &glyphAnchor = symbol.anchor;
-
- if (maxZoom <= minZoom)
- continue;
+ } else {
+ // Point labels
+ anchors = {Anchor{float(line[0].x), float(line[0].y), 0, minScale}};
+ }
- // Lower min zoom so that while fading out the label
- // it can be shown outside of collision-free zoom levels
- if (minZoom == placementZoom) {
- minZoom = 0;
+ // TODO: figure out correct ascender height.
+ const vec2<float> origin = {0, -17};
+
+ for (Anchor &anchor : anchors) {
+
+ // Calculate the scales at which the text and icons can be first shown without overlap
+ Placement glyphPlacement;
+ Placement iconPlacement;
+ float glyphScale = 0;
+ float iconScale = 0;
+
+ if (shaping.size()) {
+ glyphPlacement = Placement::getGlyphs(anchor, origin, shaping, face, textBoxScale,
+ horizontalText, line, properties);
+ glyphScale =
+ properties.text.allow_overlap
+ ? glyphPlacement.minScale
+ : collision.getPlacementScale(glyphPlacement.boxes, glyphPlacement.minScale);
+ if (!glyphScale && !iconWithoutText)
+ continue;
}
- const int glyph_vertex_length = 4;
-
- if (!triangleGroups.size() ||
- (triangleGroups.back().vertex_length + glyph_vertex_length > 65535)) {
- // Move to a new group because the old one can't hold the geometry.
- triangleGroups.emplace_back();
+ if (image) {
+ iconPlacement = Placement::getIcon(anchor, image, iconBoxScale, line, properties);
+ iconScale =
+ properties.icon.allow_overlap
+ ? iconPlacement.minScale
+ : collision.getPlacementScale(iconPlacement.boxes, iconPlacement.minScale);
+ if (!iconScale && !textWithoutIcon)
+ continue;
}
- // We're generating triangle fans, so we always start with the first
- // coordinate in this polygon.
- triangle_group_type &triangleGroup = triangleGroups.back();
- uint32_t triangleIndex = triangleGroup.vertex_length;
+ if (!iconWithoutText && !textWithoutIcon) {
+ iconScale = glyphScale = util::max(iconScale, glyphScale);
+ } else if (!textWithoutIcon && glyphScale) {
+ glyphScale = util::max(iconScale, glyphScale);
+ } else if (!iconWithoutText && iconScale) {
+ iconScale = util::max(iconScale, glyphScale);
+ }
- // coordinates (2 triangles)
- buffer.add(glyphAnchor.x, glyphAnchor.y, tl.x, tl.y, tex.x, tex.y, angle, minZoom,
- placementRange, maxZoom, placementZoom);
- buffer.add(glyphAnchor.x, glyphAnchor.y, tr.x, tr.y, tex.x + tex.w, tex.y, angle,
- minZoom, placementRange, maxZoom, placementZoom);
- buffer.add(glyphAnchor.x, glyphAnchor.y, bl.x, bl.y, tex.x, tex.y + tex.h, angle,
- minZoom, placementRange, maxZoom, placementZoom);
- buffer.add(glyphAnchor.x, glyphAnchor.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h,
- angle, minZoom, placementRange, maxZoom, placementZoom);
+ // Get the rotation ranges it is safe to show the glyphs
+ PlacementRange glyphRange =
+ (!glyphScale || properties.text.allow_overlap)
+ ? fullRange
+ : collision.getPlacementRange(glyphPlacement.boxes, glyphScale, horizontalText);
+ PlacementRange iconRange =
+ (!iconScale || properties.icon.allow_overlap)
+ ? fullRange
+ : collision.getPlacementRange(iconPlacement.boxes, iconScale, horizontalIcon);
+
+ const PlacementRange maxRange = {{
+ util::min(iconRange[0], glyphRange[0]), util::max(iconRange[1], glyphRange[1]),
+ }};
+
+ if (!iconWithoutText && !textWithoutIcon) {
+ iconRange = glyphRange = maxRange;
+ } else if (!textWithoutIcon) {
+ glyphRange = maxRange;
+ } else if (!iconWithoutText) {
+ iconRange = maxRange;
+ }
- // add the two triangles, referencing the four coordinates we just inserted.
- triangleElementsBuffer.add(triangleIndex + 0, triangleIndex + 1, triangleIndex + 2);
- triangleElementsBuffer.add(triangleIndex + 1, triangleIndex + 2, triangleIndex + 3);
+ // Insert final placement into collision tree and add glyphs/icons to buffers
+ if (glyphScale) {
+ if (!properties.text.ignore_placement) {
+ collision.insert(glyphPlacement.boxes, anchor, glyphScale, glyphRange,
+ horizontalText);
+ }
+ addSymbols(text, glyphPlacement.shapes, glyphScale, glyphRange);
+ }
- triangleGroup.vertex_length += glyph_vertex_length;
- triangleGroup.elements_length += 2;
+ if (iconScale) {
+ if (!properties.icon.ignore_placement) {
+ collision.insert(iconPlacement.boxes, anchor, iconScale, iconRange, horizontalIcon);
+ }
+ addSymbols(icon, iconPlacement.shapes, iconScale, iconRange);
+ }
}
}
+template <typename Buffer>
+void SymbolBucket::addSymbols(Buffer &buffer, const PlacedGlyphs &symbols, float scale,
+ PlacementRange placementRange) {
+ const float zoom = collision.zoom;
-void SymbolBucket::addGlyphs(const PlacedGlyphs &glyphs, float placementZoom,
- PlacementRange placementRange, float zoom) {
- placementZoom += zoom;
+ const float placementZoom = std::log(scale) / std::log(2) + zoom;
- for (const PlacedGlyph &glyph : glyphs) {
- const auto &tl = glyph.tl;
- const auto &tr = glyph.tr;
- const auto &bl = glyph.bl;
- const auto &br = glyph.br;
- const auto &tex = glyph.tex;
- const auto &angle = glyph.angle;
+ for (const PlacedGlyph &symbol : symbols) {
+ const auto &tl = symbol.tl;
+ const auto &tr = symbol.tr;
+ const auto &bl = symbol.bl;
+ const auto &br = symbol.br;
+ const auto &tex = symbol.tex;
+ const auto &angle = symbol.angle;
- float minZoom = util::max(static_cast<float>(zoom + log(glyph.glyphBox.minScale) / log(2)),
- placementZoom);
- float maxZoom =
- util::min(static_cast<float>(zoom + log(glyph.glyphBox.maxScale) / log(2)), 25.0f);
- const auto &glyphAnchor = glyph.glyphBox.anchor;
+ float minZoom =
+ util::max(static_cast<float>(zoom + log(symbol.minScale) / log(2)), placementZoom);
+ float maxZoom = util::min(static_cast<float>(zoom + log(symbol.maxScale) / log(2)), 25.0f);
+ const auto &glyphAnchor = symbol.anchor;
if (maxZoom <= minZoom)
continue;
@@ -338,30 +330,30 @@ void SymbolBucket::addGlyphs(const PlacedGlyphs &glyphs, float placementZoom,
const int glyph_vertex_length = 4;
- if (!triangleGroups.size() ||
- (triangleGroups.back().vertex_length + glyph_vertex_length > 65535)) {
+ if (!buffer.groups.size() ||
+ (buffer.groups.back().vertex_length + glyph_vertex_length > 65535)) {
// Move to a new group because the old one can't hold the geometry.
- triangleGroups.emplace_back();
+ buffer.groups.emplace_back();
}
// We're generating triangle fans, so we always start with the first
// coordinate in this polygon.
- triangle_group_type &triangleGroup = triangleGroups.back();
+ ElementGroup &triangleGroup = buffer.groups.back();
uint32_t triangleIndex = triangleGroup.vertex_length;
// coordinates (2 triangles)
- textVertexBuffer.add(glyphAnchor.x, glyphAnchor.y, tl.x, tl.y, tex.x, tex.y, angle, minZoom,
- placementRange, maxZoom, placementZoom);
- textVertexBuffer.add(glyphAnchor.x, glyphAnchor.y, tr.x, tr.y, tex.x + tex.w, tex.y, angle,
- minZoom, placementRange, maxZoom, placementZoom);
- textVertexBuffer.add(glyphAnchor.x, glyphAnchor.y, bl.x, bl.y, tex.x, tex.y + tex.h, angle,
- minZoom, placementRange, maxZoom, placementZoom);
- textVertexBuffer.add(glyphAnchor.x, glyphAnchor.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h,
- angle, minZoom, placementRange, maxZoom, placementZoom);
+ buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, tl.x, tl.y, tex.x, tex.y, angle, minZoom,
+ placementRange, maxZoom, placementZoom);
+ buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, tr.x, tr.y, tex.x + tex.w, tex.y, angle,
+ minZoom, placementRange, maxZoom, placementZoom);
+ buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, bl.x, bl.y, tex.x, tex.y + tex.h, angle,
+ minZoom, placementRange, maxZoom, placementZoom);
+ buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h,
+ angle, minZoom, placementRange, maxZoom, placementZoom);
// add the two triangles, referencing the four coordinates we just inserted.
- triangleElementsBuffer.add(triangleIndex + 0, triangleIndex + 1, triangleIndex + 2);
- triangleElementsBuffer.add(triangleIndex + 1, triangleIndex + 2, triangleIndex + 3);
+ buffer.triangles.add(triangleIndex + 0, triangleIndex + 1, triangleIndex + 2);
+ buffer.triangles.add(triangleIndex + 1, triangleIndex + 2, triangleIndex + 3);
triangleGroup.vertex_length += glyph_vertex_length;
triangleGroup.elements_length += 2;
@@ -369,27 +361,24 @@ void SymbolBucket::addGlyphs(const PlacedGlyphs &glyphs, float placementZoom,
}
void SymbolBucket::drawGlyphs(TextShader &shader) {
- char *vertex_index = BUFFER_OFFSET(text_vertex_start * textVertexBuffer.itemSize);
- char *elements_index =
- BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize);
- for (triangle_group_type &group : triangleGroups) {
- group.array.bind(shader, textVertexBuffer, triangleElementsBuffer, vertex_index);
- glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index);
- vertex_index += group.vertex_length * textVertexBuffer.itemSize;
- elements_index += group.elements_length * triangleElementsBuffer.itemSize;
- }
-}
-
-void SymbolBucket::drawIcons(IconShader& shader) {
- char *vertex_index = BUFFER_OFFSET(icon_vertex_start * iconVertexBuffer.itemSize);
- array.bind(shader, iconVertexBuffer, vertex_index);
- glDrawArrays(GL_POINTS, 0, (GLsizei)(icon_vertex_end - icon_vertex_start));
+ char *vertex_index = BUFFER_OFFSET(0);
+ char *elements_index = BUFFER_OFFSET(0);
+ for (ElementGroup &group : text.groups) {
+ group.array.bind(shader, text.vertices, text.triangles, vertex_index);
+ glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index);
+ vertex_index += group.vertex_length * text.vertices.itemSize;
+ elements_index += group.elements_length * text.triangles.itemSize;
+ }
}
-void SymbolBucket::drawIcons(DotShader& shader) {
- char *vertex_index = BUFFER_OFFSET(icon_vertex_start * iconVertexBuffer.itemSize);
- array.bind(shader, iconVertexBuffer, vertex_index);
- glDrawArrays(GL_POINTS, 0, (GLsizei)(icon_vertex_end - icon_vertex_start));
+void SymbolBucket::drawIcons(IconShader &shader) {
+ char *vertex_index = BUFFER_OFFSET(0);
+ char *elements_index = BUFFER_OFFSET(0);
+ for (ElementGroup &group : icon.groups) {
+ group.array.bind(shader, icon.vertices, icon.triangles, vertex_index);
+ glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index);
+ vertex_index += group.vertex_length * icon.vertices.itemSize;
+ elements_index += group.elements_length * icon.triangles.itemSize;
+ }
}
-
}
diff --git a/src/style/style_parser.cpp b/src/style/style_parser.cpp
index 3377185d32..27deca689e 100644
--- a/src/style/style_parser.cpp
+++ b/src/style/style_parser.cpp
@@ -885,6 +885,7 @@ void StyleParser::parseRender(JSVal value, std::shared_ptr<StyleLayer> &layer) {
parseRenderProperty<RotationAlignmentTypeClass>(value, render.icon.rotation_alignment, "icon-rotation-alignment");
parseRenderProperty(value, render.icon.max_size, "icon-max-size");
parseRenderProperty(value, render.icon.image, "icon-image");
+ parseRenderProperty(value, render.icon.rotate, "icon-rotate");
parseRenderProperty(value, render.icon.padding, "icon-padding");
parseRenderProperty(value, render.icon.keep_upright, "icon-keep-upright");
parseRenderProperty(value, render.icon.offset, "icon-offset");
diff --git a/src/text/collision.cpp b/src/text/collision.cpp
index 362ac92e27..70712f0651 100644
--- a/src/text/collision.cpp
+++ b/src/text/collision.cpp
@@ -48,24 +48,26 @@ Collision::Collision(float zoom, float tileExtent, float tileSize, float placeme
// We don't want to place labels all the way to 25.5. This lets too many
// glyphs be placed, slowing down collision checking. Only place labels if
// they will show up within the intended zoom range of the tile.
- maxPlacementScale(std::exp(std::log(2) * util::min(3.0f, placementDepth, 25.5f - zoom)))
-{
+ maxPlacementScale(std::exp(std::log(2) * util::min(3.0f, placementDepth, 25.5f - zoom))) {
const float m = 4096;
const float edge = m * tilePixelRatio * 2;
// Hack to prevent cross-tile labels
- insert(
- {{GlyphBox{
- CollisionRect{CollisionPoint{0, 0}, CollisionPoint{0, m * 8}}, 0},
- GlyphBox{
- CollisionRect{CollisionPoint{0, 0}, CollisionPoint{m * 8, 0}}, 0}}},
- CollisionAnchor(0, 0), 1, {{M_PI * 2, 0}}, false, 2);
-
- insert({{GlyphBox{
- CollisionRect{CollisionPoint{-m * 8, 0}, CollisionPoint{0, 0}}, 0},
- GlyphBox{
- CollisionRect{CollisionPoint{0, -m * 8}, CollisionPoint{0, 0}}, 0}}},
- CollisionAnchor{m, m}, 1, {{M_PI * 2, 0}}, false, 2);
+ insert({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}},
+ CollisionAnchor(0, 0), 1, {{M_PI * 2, 0}}, false);
+
+ insert({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}},
+ CollisionAnchor(m, m), 1, {{M_PI * 2, 0}}, false);
}
GlyphBox getMergedGlyphs(const GlyphBoxes &boxes, const CollisionAnchor &anchor) {
@@ -81,99 +83,51 @@ GlyphBox getMergedGlyphs(const GlyphBoxes &boxes, const CollisionAnchor &anchor)
box.tl.y = util::min(box.tl.y, gbox.tl.y);
box.br.x = util::max(box.br.x, gbox.br.x);
box.br.y = util::max(box.br.y, gbox.br.y);
- mergedGlyphs.minScale =
- util::max(mergedGlyphs.minScale, glyph.minScale);
+ mergedGlyphs.minScale = util::max(mergedGlyphs.minScale, glyph.minScale);
}
return mergedGlyphs;
}
-PlacementProperty Collision::place(const GlyphBoxes &boxes, const CollisionAnchor &anchor,
- float minPlacementScale, float maxPlacementScale, float padding,
- bool horizontal, bool allowOverlap, bool ignorePlacement) {
- float minScale = std::numeric_limits<float>::infinity();
- for (const GlyphBox &glyphBox : boxes) {
- minScale = util::min(minScale, glyphBox.minScale);
- }
- minPlacementScale = util::max(minPlacementScale, minScale);
-
- GlyphBoxes glyphs;
- if (horizontal) {
- // Collision checks between rotating and fixed labels are relatively expensive,
- // so we use one box per label, not per glyph for horizontal labels.
- glyphs.push_back(getMergedGlyphs(boxes, anchor));
-
- // for all horizontal labels, calculate bbox covering all rotated positions
- const CollisionRect &box = glyphs.front().box;
- float x12 = box.tl.x * box.tl.x,
- y12 = box.tl.y * box.tl.y,
- x22 = box.br.x * box.br.x,
- y22 = box.br.y * box.br.y,
- diag = std::sqrt(util::max(x12 + y12, x12 + y22, x22 + y12, x22 + y22));
-
- glyphs.front().hBox = CollisionRect{
- {-diag, -diag},
- {diag, diag}
- };
- }
-
- // Calculate the minimum scale the entire label can be shown without
- // collisions
- float scale =
- allowOverlap ? minPlacementScale : getPlacementScale(glyphs, minPlacementScale,
- maxPlacementScale, padding);
-
- // Return if the label can never be placed without collision
- if (scale < 0 || scale == std::numeric_limits<float>::infinity()) {
- return PlacementProperty{};
- }
-
- // Calculate the range it is safe to rotate all glyphs
- PlacementRange rotationRange = allowOverlap ? PlacementRange{{2 * M_PI, 0.0f}}
- : getPlacementRange(glyphs, scale, horizontal);
-
- if (!ignorePlacement) insert(glyphs, anchor, scale, rotationRange, horizontal, padding);
-
- float zoom = log(scale) / log(2);
-
- return PlacementProperty{zoom, rotationRange};
-}
+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 maxPlacementScale, float pad) {
+float Collision::getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale) {
for (const GlyphBox &glyph : glyphs) {
const CollisionRect &box = glyph.box;
const CollisionRect &bbox = glyph.hBox ? glyph.hBox.get() : glyph.box;
const CollisionAnchor &anchor = glyph.anchor;
+ const float pad = glyph.padding;
- if (anchor.x < 0 || anchor.x > 4096 || anchor.y < 0 ||
- anchor.y > 4096) {
+ if (anchor.x < 0 || anchor.x > 4096 || anchor.y < 0 || anchor.y > 4096) {
return -1;
}
float minScale = std::fmax(minPlacementScale, glyph.minScale);
- float maxScale = glyph.maxScale;
+ float maxScale = glyph.maxScale || std::numeric_limits<float>::infinity();
if (minScale >= maxScale) {
continue;
}
// Compute the scaled bounding box of the unrotated glyph
- float minPlacedX = anchor.x + bbox.tl.x / minScale;
- float minPlacedY = anchor.y + bbox.tl.y / minScale;
- float maxPlacedX = anchor.x + bbox.br.x / minScale;
- float maxPlacedY = anchor.y + bbox.br.y / minScale;
-
- Box query_box{Point{minPlacedX, minPlacedY},
- Point{maxPlacedX, maxPlacedY}};
+ const Box searchBox = getBox(anchor, bbox, minScale, maxScale);
std::vector<PlacementValue> blocking;
- ((Tree *)cTree)
- ->query(bgi::intersects(query_box), std::back_inserter(blocking));
- ((Tree *)hTree)
- ->query(bgi::intersects(query_box), std::back_inserter(blocking));
+ ((Tree *)cTree)->query(bgi::intersects(searchBox), std::back_inserter(blocking));
+ ((Tree *)hTree)->query(bgi::intersects(searchBox), std::back_inserter(blocking));
if (blocking.size()) {
const CollisionAnchor &na = anchor; // new anchor
@@ -195,10 +149,14 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs,
float padding = std::fmax(pad, placement.padding) * 8.0f;
// Original algorithm:
- float s1 = (ob.tl.x - nb.br.x - padding) / (na.x - oa.x); // scale at which new box is to the left of old box
- float s2 = (ob.br.x - nb.tl.x + padding) / (na.x - oa.x); // scale at which new box is to the right of old box
- float s3 = (ob.tl.y - nb.br.y - padding) / (na.y - oa.y); // scale at which new box is to the top of old box
- float s4 = (ob.br.y - nb.tl.y + padding) / (na.y - oa.y); // scale at which new box is to the bottom of old box
+ float s1 = (ob.tl.x - nb.br.x - padding) /
+ (na.x - oa.x); // scale at which new box is to the left of old box
+ float s2 = (ob.br.x - nb.tl.x + padding) /
+ (na.x - oa.x); // scale at which new box is to the right of old box
+ float s3 = (ob.tl.y - nb.br.y - padding) /
+ (na.y - oa.y); // scale at which new box is to the top of old box
+ float s4 = (ob.br.y - nb.tl.y + padding) /
+ (na.y - oa.y); // scale at which new box is to the bottom of old box
if (isnan(s1) || isnan(s2)) {
s1 = s2 = 1;
@@ -211,10 +169,8 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs,
// Only update label's min scale if the glyph was
// restricted by a collision
- if (collisionFreeScale > minPlacementScale &&
- collisionFreeScale > minScale &&
- collisionFreeScale < maxScale &&
- collisionFreeScale < placement.maxScale) {
+ if (collisionFreeScale > minPlacementScale && collisionFreeScale > minScale &&
+ collisionFreeScale < maxScale && collisionFreeScale < placement.maxScale) {
minPlacementScale = collisionFreeScale;
}
@@ -228,8 +184,7 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs,
return minPlacementScale;
}
-PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs,
- float placementScale,
+PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs, float placementScale,
bool horizontal) {
PlacementRange placementRange = {{2.0f * M_PI, 0}};
@@ -242,16 +197,13 @@ PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs,
float maxPlacedX = anchor.x + bbox.br.x / placementScale;
float maxPlacedY = anchor.y + bbox.br.y / placementScale;
- Box query_box{Point{minPlacedX, minPlacedY},
- Point{maxPlacedX, maxPlacedY}};
+ Box query_box{Point{minPlacedX, minPlacedY}, Point{maxPlacedX, maxPlacedY}};
std::vector<PlacementValue> blocking;
- ((Tree *)hTree)
- ->query(bgi::intersects(query_box), std::back_inserter(blocking));
+ ((Tree *)hTree)->query(bgi::intersects(query_box), std::back_inserter(blocking));
if (horizontal) {
- ((Tree *)cTree)
- ->query(bgi::intersects(query_box), std::back_inserter(blocking));
+ ((Tree *)cTree)->query(bgi::intersects(query_box), std::back_inserter(blocking));
}
for (const PlacementValue &value : blocking) {
@@ -275,10 +227,8 @@ PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs,
x2 = anchor.x + bbox.br.x / b.placementScale;
y2 = anchor.y + bbox.br.y / b.placementScale;
- intersectX = x1 < s.max_corner().get<0>() &&
- x2 > s.min_corner().get<0>();
- intersectY = y1 < s.max_corner().get<1>() &&
- y2 > s.min_corner().get<1>();
+ intersectX = x1 < s.max_corner().get<0>() && x2 > s.min_corner().get<0>();
+ intersectY = y1 < s.max_corner().get<1>() && y2 > s.min_corner().get<1>();
}
// If they can't intersect, skip more expensive rotation calculation
@@ -298,24 +248,22 @@ PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs,
};
void Collision::insert(const GlyphBoxes &glyphs, const CollisionAnchor &anchor,
- float placementScale,
- const PlacementRange &placementRange, bool horizontal,
- float padding) {
+ float placementScale, const PlacementRange &placementRange,
+ bool horizontal) {
assert(placementScale != std::numeric_limits<float>::infinity());
- std::vector<PlacementValue> result;
- result.reserve(glyphs.size());
+ std::vector<PlacementValue> allBounds;
+ allBounds.reserve(glyphs.size());
for (const GlyphBox &glyph : glyphs) {
const CollisionRect &box = glyph.box;
const CollisionRect &bbox = glyph.hBox ? glyph.hBox.get() : glyph.box;
- float minScale = std::fmax(placementScale, glyph.minScale);
+ const float minScale = util::max(placementScale, glyph.minScale);
+ const float maxScale = glyph.maxScale || std::numeric_limits<float>::infinity();
- Box bounds{Point{anchor.x + bbox.tl.x / minScale,
- anchor.y + bbox.tl.y / minScale},
- Point{anchor.x + bbox.br.x / minScale,
- anchor.y + bbox.br.y / minScale}};
+ Box bounds{Point{anchor.x + bbox.tl.x / minScale, anchor.y + bbox.tl.y / minScale},
+ Point{anchor.x + bbox.br.x / minScale, anchor.y + bbox.br.y / minScale}};
PlacementBox placement;
placement.anchor = anchor;
@@ -325,19 +273,19 @@ void Collision::insert(const GlyphBoxes &glyphs, const CollisionAnchor &anchor,
}
placement.placementRange = placementRange;
placement.placementScale = minScale;
- placement.maxScale = glyph.maxScale;
- placement.padding = padding;
+ placement.maxScale = maxScale;
+ placement.padding = glyph.padding;
assert(!isnan(bounds.min_corner().get<0>()) && !isnan(bounds.min_corner().get<1>()));
assert(!isnan(bounds.max_corner().get<0>()) && !isnan(bounds.max_corner().get<1>()));
- result.emplace_back(bounds, placement);
+ allBounds.emplace_back(bounds, placement);
}
// Bulk-insert all glyph boxes
if (horizontal) {
- ((Tree *)hTree)->insert(result.begin(), result.end());
+ ((Tree *)hTree)->insert(allBounds.begin(), allBounds.end());
} else {
- ((Tree *)cTree)->insert(result.begin(), result.end());
+ ((Tree *)cTree)->insert(allBounds.begin(), allBounds.end());
}
}
diff --git a/src/text/glyph_store.cpp b/src/text/glyph_store.cpp
index edcecde7ff..83c2beb5cb 100644
--- a/src/text/glyph_store.cpp
+++ b/src/text/glyph_store.cpp
@@ -40,7 +40,7 @@ const Shaping FontStack::getShaping(const std::u32string &string, const float &m
Shaping shaping;
// Loop through all characters of this label and shape.
for (uint32_t chr : string) {
- shaping.emplace_back(0, chr, x, 0);
+ shaping.emplace_back(chr, x, 0);
i++;
auto metric = metrics.find(chr);
if (metric != metrics.end()) {
@@ -55,7 +55,7 @@ const Shaping FontStack::getShaping(const std::u32string &string, const float &m
void alignVertically(Shaping &shaping, const uint32_t &lines, const float &lineHeight, const float &verticalAlign) {
const float dy = -(lineHeight * (lines - 1) + 24) * verticalAlign - 5;
- for (GlyphPlacement &shape : shaping) {
+ for (PositionedGlyph &shape : shaping) {
shape.y += dy;
}
}
@@ -86,7 +86,7 @@ void FontStack::lineWrap(Shaping &shaping, const float &lineHeight, const float
uint32_t line = 0;
for (uint32_t i = 0; i < shaping.size(); i++) {
- GlyphPlacement &shape = shaping[i];
+ PositionedGlyph &shape = shaping[i];
shape.x -= lengthBeforeCurrentLine;
shape.y += lineHeight * line;
diff --git a/src/text/placement.cpp b/src/text/placement.cpp
index 75988cc997..448b892934 100644
--- a/src/text/placement.cpp
+++ b/src/text/placement.cpp
@@ -1,74 +1,33 @@
#include <mbgl/text/placement.hpp>
-#include <mbgl/map/vector_tile.hpp>
-#include <mbgl/geometry/interpolate.hpp>
-#include <mbgl/renderer/symbol_bucket.hpp>
+#include <mbgl/geometry/anchor.hpp>
+#include <mbgl/text/glyph.hpp>
+#include <mbgl/text/placement.hpp>
+#include <mbgl/text/glyph_store.hpp>
+#include <mbgl/style/style_bucket.hpp>
+
#include <mbgl/util/math.hpp>
-#include <mbgl/util/constants.hpp>
-
-#include <algorithm>
-
-using namespace mbgl;
-
-const int Placement::tileExtent = 4096;
-const int Placement::glyphSize =
- 24; // size in pixels of this glyphs in the tile
-const float Placement::minScale = 0.5; // underscale by 1 zoom level
-
-Placement::Placement(int8_t zoom, float placementDepth)
- : zoom(zoom),
- zOffset(log(512.0 / util::tileSize) / log(2)),
-
- // Calculate the maximum scale we can go down in our fake-3d rtree so that
- // placement still makes sense. This is calculated so that the minimum
- // placement zoom can be at most 25.5 (we use an unsigned integer x10 to
- // store the minimum zoom).
- //
- // We don't want to place labels all the way to 25.5. This lets too many
- // glyphs be placed, slowing down collision checking. Only place labels if
- // they will show up within the intended zoom range of the tile.
- // TODO make this not hardcoded to 3
- maxPlacementScale(std::exp(
- log(2) *
- util::min(3.0f, util::min(placementDepth > 0 ? placementDepth : 1.0f, 25.5f - zoom)))) {}
-
-bool byScale(const Anchor &a, const Anchor &b) { return a.scale < b.scale; }
-
-static const Glyph null_glyph;
-
-inline const Glyph &getGlyph(const GlyphPlacement &placed,
- const GlyphPositions &face) {
- auto it = face.find(placed.glyph);
- if (it != face.end()) {
- return it->second;
- } else {
- fprintf(stderr, "glyph %d does not exist\n", placed.glyph);
- }
- return null_glyph;
-}
+namespace mbgl {
+
+const float Placement::globalMinScale = 0.5; // underscale by 1 zoom level
struct GlyphInstance {
explicit GlyphInstance(const vec2<float> &anchor) : anchor(anchor) {}
- explicit GlyphInstance(const vec2<float> &anchor, float offset,
- float minScale, float maxScale, float angle)
- : anchor(anchor),
- offset(offset),
- minScale(minScale),
- maxScale(maxScale),
- angle(angle) {}
+ explicit GlyphInstance(const vec2<float> &anchor, float offset, float minScale, float maxScale,
+ float angle)
+ : anchor(anchor), offset(offset), minScale(minScale), maxScale(maxScale), angle(angle) {}
const vec2<float> anchor;
const float offset = 0.0f;
- const float minScale = Placement::minScale;
+ const float minScale = Placement::globalMinScale;
const float maxScale = std::numeric_limits<float>::infinity();
const float angle = 0.0f;
};
typedef std::vector<GlyphInstance> GlyphInstances;
-void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs,
- Anchor &anchor, float offset,
- const std::vector<Coordinate> &line, int segment,
+void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, Anchor &anchor,
+ float offset, const std::vector<Coordinate> &line, int segment,
int8_t direction, float maxAngleDelta) {
const bool upsideDown = direction < 0;
@@ -94,8 +53,8 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs,
while (true) {
const float dist = util::dist<float>(newAnchor, end);
const float scale = offset / dist;
- float angle = -std::atan2(end.x - newAnchor.x, end.y - newAnchor.y) +
- direction * M_PI / 2.0f;
+ float angle =
+ -std::atan2(end.x - newAnchor.x, end.y - newAnchor.y) + direction * M_PI / 2.0f;
if (upsideDown)
angle += M_PI;
@@ -111,8 +70,7 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs,
/* offset */ static_cast<float>(upsideDown ? M_PI : 0.0),
/* minScale */ scale,
/* maxScale */ prevscale,
- /* angle */ static_cast<float>(
- std::fmod((angle + 2.0 * M_PI), (2.0 * M_PI)))};
+ /* angle */ static_cast<float>(std::fmod((angle + 2.0 * M_PI), (2.0 * M_PI)))};
if (scale <= placementScale)
break;
@@ -137,49 +95,155 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs,
}
}
-void getGlyphs(PlacedGlyphs &glyphs, GlyphBoxes &boxes,
- Anchor &anchor, vec2<float> origin, const Shaping &shaping,
- const GlyphPositions &face, float fontScale,
- bool horizontal, const std::vector<Coordinate> &line,
- float maxAngleDelta, float rotate) {
- // The total text advance is the width of this label.
-
- // TODO: allow setting an alignment
- // var alignment = 'center';
- // if (alignment == 'center') {
- // origin.x -= advance / 2;
- // } else if (alignment == 'right') {
- // origin.x -= advance;
- // }
+GlyphBox getMergedBoxes(const GlyphBoxes &glyphs, const Anchor &anchor) {
+ // Collision checks between rotating and fixed labels are relatively expensive,
+ // so we use one box per label, not per glyph for horizontal labels.
+
+ const float inf = std::numeric_limits<float>::infinity();
+
+ GlyphBox mergedglyphs{/* box */ CollisionRect{inf, inf, -inf, -inf},
+ /* anchor */ anchor,
+ /* minScale */ 0,
+ /* maxScale */ inf,
+ /* padding */ -inf};
+
+ CollisionRect &box = mergedglyphs.box;
+
+ for (const GlyphBox &glyph : glyphs) {
+ const CollisionRect &gbox = glyph.box;
+ box.tl.x = util::min(box.tl.x, gbox.tl.x);
+ box.tl.y = util::min(box.tl.y, gbox.tl.y);
+ box.br.x = util::max(box.br.x, gbox.br.x);
+ box.br.y = util::max(box.br.y, gbox.br.y);
+ mergedglyphs.minScale = util::max(mergedglyphs.minScale, glyph.minScale);
+ mergedglyphs.padding = util::max(mergedglyphs.padding, glyph.padding);
+ }
+ // for all horizontal labels, calculate bbox covering all rotated positions
+ float x12 = box.tl.x * box.tl.x, y12 = box.tl.y * box.tl.y, x22 = box.br.x * box.br.x,
+ y22 = box.br.y * box.br.y,
+ diag = std::sqrt(util::max(x12 + y12, x12 + y22, x22 + y12, x22 + y22));
+
+ mergedglyphs.hBox = CollisionRect{-diag, -diag, diag, diag};
+
+ return mergedglyphs;
+}
+
+Placement Placement::getIcon(Anchor &anchor, const Rect<uint16_t> &image, float boxScale,
+ const std::vector<Coordinate> &line, const StyleBucketSymbol &props) {
+ const float x = image.w / 2.0f; // No need to divide by image.pixelRatio here?
+ const float y = image.h / 2.0f; // image.pixelRatio;
+
+ const float dx = props.icon.offset.x;
+ const float dy = props.icon.offset.y;
+ float x1 = (dx - x);
+ float x2 = (dx + x);
+ float y1 = (dy - y);
+ float y2 = (dy + y);
+
+ vec2<float> tl{x1, y1};
+ vec2<float> tr{x2, y1};
+ vec2<float> br{x2, y2};
+ vec2<float> bl{x1, y2};
+
+ float angle = props.icon.rotate * M_PI / 180.0f;
+ if (anchor.segment >= 0 && props.icon.rotation_alignment != RotationAlignmentType::Viewport) {
+ const Coordinate &next = line[anchor.segment];
+ angle += -std::atan2(next.x - anchor.x, next.y - anchor.y) + M_PI / 2;
+ }
+
+ if (angle) {
+ // Compute the transformation matrix.
+ float angle_sin = std::sin(angle);
+ float angle_cos = std::cos(angle);
+ std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}};
+
+ tl = tl.matMul(matrix);
+ tr = tr.matMul(matrix);
+ bl = bl.matMul(matrix);
+ br = br.matMul(matrix);
+
+ x1 = util::min(tl.x, tr.x, bl.x, br.x);
+ x2 = util::max(tl.x, tr.x, bl.x, br.x);
+ y1 = util::min(tl.y, tr.y, bl.y, br.y);
+ y2 = util::max(tl.y, tr.y, bl.y, br.y);
+ }
+
+ const CollisionRect box{/* x1 */ x1 * boxScale,
+ /* y1 */ y1 * boxScale,
+ /* x2 */ y2 * boxScale,
+ /* y2 */ y2 * boxScale};
+
+ Placement placement;
+
+ placement.boxes.emplace_back(
+ /* box */ box,
+ /* anchor */ anchor,
+ /* minScale */ Placement::globalMinScale,
+ /* maxScale */ std::numeric_limits<float>::infinity(),
+ /* padding */ props.icon.padding);
+
+ placement.shapes.emplace_back(
+ /* tl */ tl,
+ /* tr */ tr,
+ /* bl */ bl,
+ /* br */ br,
+ /* image */ image,
+ /* angle */ 0,
+ /* anchors */ anchor,
+ /* minScale */ Placement::globalMinScale,
+ /* maxScale */ std::numeric_limits<float>::infinity());
+
+ placement.minScale = anchor.scale;
+
+ return placement;
+}
+
+Placement Placement::getGlyphs(Anchor &anchor, const vec2<float> &origin, const Shaping &shaping,
+ const GlyphPositions &face, float boxScale, bool horizontal,
+ const std::vector<Coordinate> &line,
+ const StyleBucketSymbol &props) {
+ const float maxAngleDelta = props.text.max_angle_delta * M_PI / 180;
+ const float rotate = props.text.rotate * M_PI / 180;
+ const float padding = props.text.padding;
+ const bool alongLine = props.text.rotation_alignment != RotationAlignmentType::Viewport;
+ const bool keepUpright = props.text.keep_upright;
+
+ Placement placement;
+
+ PlacedGlyphs &glyphs = placement.shapes;
+ GlyphBoxes &boxes = placement.boxes;
const uint32_t buffer = 3;
- for (const GlyphPlacement &placed : shaping) {
- const Glyph &glyph = getGlyph(placed, face);
- if (!glyph) {
- // This glyph is empty and doesn't have any pixels that we'd need to
- // show.
+ for (const PositionedGlyph &shape : shaping) {
+ auto face_it = face.find(shape.glyph);
+ if (face_it == face.end())
+ continue;
+ const Glyph &glyph = face_it->second;
+ const Rect<uint16_t> &rect = glyph.rect;
+
+ if (!glyph)
+ continue;
+
+ if (!(rect && rect.w > 0 && rect.h > 0))
continue;
- }
- float x =
- (origin.x + placed.x + glyph.metrics.left - buffer + glyph.rect.w / 2) *
- fontScale;
+ const float x = (origin.x + shape.x + glyph.metrics.left - buffer + rect.w / 2) * boxScale;
GlyphInstances glyphInstances;
- if (anchor.segment >= 0) {
- getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, x,
- line, anchor.segment, 1, maxAngleDelta);
- getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, x,
- line, anchor.segment, -1, maxAngleDelta);
+ if (anchor.segment >= 0 && alongLine) {
+ getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, x, line, anchor.segment, 1,
+ maxAngleDelta);
+ if (keepUpright)
+ getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, x, line,
+ anchor.segment, -1, maxAngleDelta);
} else {
-
glyphInstances.emplace_back(GlyphInstance{anchor});
}
- const float x1 = origin.x + placed.x + glyph.metrics.left - buffer;
- const float y1 = origin.y + placed.y - glyph.metrics.top - buffer;
+ const float x1 = origin.x + shape.x + glyph.metrics.left - buffer;
+ const float y1 = origin.y + shape.y - glyph.metrics.top - buffer;
const float x2 = x1 + glyph.rect.w;
const float y2 = y1 + glyph.rect.h;
@@ -188,8 +252,7 @@ void getGlyphs(PlacedGlyphs &glyphs, GlyphBoxes &boxes,
const vec2<float> obl{x1, y2};
const vec2<float> obr{x2, y2};
- const CollisionRect obox{fontScale *x1, fontScale *y1,
- fontScale *x2, fontScale *y2};
+ const CollisionRect obox{boxScale * x1, boxScale * y1, boxScale * x2, boxScale * y2};
for (const GlyphInstance &instance : glyphInstances) {
vec2<float> tl = otl;
@@ -206,79 +269,47 @@ void getGlyphs(PlacedGlyphs &glyphs, GlyphBoxes &boxes,
// Compute the transformation matrix.
float angle_sin = std::sin(angle);
float angle_cos = std::cos(angle);
- std::array<float, 4> matrix = {
- {angle_cos, -angle_sin, angle_sin, angle_cos}};
+ std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}};
tl = tl.matMul(matrix);
tr = tr.matMul(matrix);
bl = bl.matMul(matrix);
br = br.matMul(matrix);
-
- // Calculate the rotated glyph's bounding box offsets from the
- // anchor point.
- box = CollisionRect{
- fontScale * util::min(tl.x, tr.x, bl.x, br.x),
- fontScale * util::min(tl.y, tr.y, bl.y, br.y),
- fontScale * util::max(tl.x, tr.x, bl.x, br.x),
- fontScale * util::max(tl.y, tr.y, bl.y, br.y)};
}
- GlyphBox glyphBox = GlyphBox{
- box,
- // Prevent label from extending past the end of the line
- util::max(instance.minScale, anchor.scale),
- instance.maxScale,
- instance.anchor
- };
+ // Prevent label from extending past the end of the line
+ const float glyphMinScale = std::max(instance.minScale, anchor.scale);
// Remember the glyph for later insertion.
- glyphs.emplace_back(PlacedGlyph{
- tl, tr, bl, br, glyph.rect,
- static_cast<float>(
- std::fmod((anchor.angle + rotate + instance.offset + 2 * M_PI), (2 * M_PI))),
- glyphBox});
-
- if (instance.offset == 0.0f) {
- boxes.emplace_back(glyphBox);
+ glyphs.emplace_back(
+ tl, tr, bl, br, rect,
+ float(std::fmod((anchor.angle + rotate + instance.offset + 2 * M_PI), (2 * M_PI))),
+ instance.anchor, glyphMinScale, instance.maxScale);
+
+ if (!instance.offset) { // not a flipped glyph
+ if (angle) {
+ // Calculate the rotated glyph's bounding box offsets from the anchor point.
+ box = CollisionRect{boxScale * util::min(tl.x, tr.x, bl.x, br.x),
+ boxScale * util::min(tl.y, tr.y, bl.y, br.y),
+ boxScale * util::max(tl.x, tr.x, bl.x, br.x),
+ boxScale * util::max(tl.y, tr.y, bl.y, br.y)};
+ }
+ boxes.emplace_back(box, instance.anchor, glyphMinScale, instance.maxScale, padding);
}
}
}
-}
-
-void Placement::addFeature(SymbolBucket &bucket, const std::vector<Coordinate> &line,
- const StyleBucketSymbol &info, const GlyphPositions &face,
- const Shaping &shaping) {
- const bool horizontal = info.text.rotation_alignment == RotationAlignmentType::Viewport;
- const float fontScale = (tileExtent / util::tileSize) / (glyphSize / info.text.max_size);
-
- Anchors anchors;
- if (line.size() == 1) {
- // Point labels
- anchors = Anchors{{Anchor{
- static_cast<float>(line[0].x), static_cast<float>(line[0].y),
- 0, minScale}}};
+ // TODO avoid creating the boxes in the first place?
+ if (horizontal)
+ boxes = {getMergedBoxes(boxes, anchor)};
- } else {
- // Line labels
- anchors = interpolate(line, info.min_distance, minScale);
-
- // Sort anchors by segment so that we can start placement with
- // the anchors that can be shown at the lowest zoom levels.
- std::sort(anchors.begin(), anchors.end(), byScale);
+ const float minPlacementScale = anchor.scale;
+ placement.minScale = std::numeric_limits<float>::infinity();
+ for (const GlyphBox &box : boxes) {
+ placement.minScale = util::min(placement.minScale, box.minScale);
}
+ placement.minScale = util::max(minPlacementScale, Placement::globalMinScale);
- for (Anchor anchor : anchors) {
- PlacedGlyphs glyphs;
- GlyphBoxes boxes;
-
- getGlyphs(glyphs, boxes, anchor, info.text.offset, shaping, face, fontScale, horizontal,
- line, info.text.max_angle_delta * (M_PI / 180), info.text.rotate);
- PlacementProperty place =
- collision.place(boxes, anchor, anchor.scale, maxPlacementScale, info.text.padding,
- horizontal, info.text.allow_overlap, info.text.ignore_placement);
- if (place) {
- bucket.addGlyphs(glyphs, place.zoom, place.rotationRange, zoom - zOffset);
- }
- }
+ return placement;
+}
}