summaryrefslogtreecommitdiff
path: root/src/mbgl/layout
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl/layout')
-rw-r--r--src/mbgl/layout/symbol_instance.cpp23
-rw-r--r--src/mbgl/layout/symbol_instance.hpp9
-rw-r--r--src/mbgl/layout/symbol_layout.cpp98
-rw-r--r--src/mbgl/layout/symbol_layout.hpp9
-rw-r--r--src/mbgl/layout/symbol_projection.cpp266
-rw-r--r--src/mbgl/layout/symbol_projection.hpp25
6 files changed, 361 insertions, 69 deletions
diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp
index ffb70c7ca2..02fb800df6 100644
--- a/src/mbgl/layout/symbol_instance.cpp
+++ b/src/mbgl/layout/symbol_instance.cpp
@@ -5,8 +5,8 @@ namespace mbgl {
using namespace style;
-SymbolInstance::SymbolInstance(Anchor& anchor,
- const GeometryCoordinates& line,
+SymbolInstance::SymbolInstance(Anchor& anchor_,
+ GeometryCoordinates line_,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
const SymbolLayoutProperties::Evaluated& layout,
@@ -16,33 +16,38 @@ SymbolInstance::SymbolInstance(Anchor& anchor,
const float textBoxScale,
const float textPadding,
const SymbolPlacementType textPlacement,
+ const std::array<float, 2> textOffset_,
const float iconBoxScale,
const float iconPadding,
const SymbolPlacementType iconPlacement,
+ const std::array<float, 2> iconOffset_,
const GlyphPositionMap& positions,
const IndexedSubfeature& indexedFeature,
const std::size_t featureIndex_) :
- point(anchor.point),
+ anchor(anchor_),
+ line(line_),
index(index_),
hasText(shapedTextOrientations.first || shapedTextOrientations.second),
hasIcon(shapedIcon),
// Create the collision features that will be used to check whether this symbol instance can be placed
- textCollisionFeature(line, anchor, shapedTextOrientations.second ?: shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature),
- iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature),
- featureIndex(featureIndex_) {
+ textCollisionFeature(line_, anchor, shapedTextOrientations.second ?: shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature),
+ iconCollisionFeature(line_, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature),
+ featureIndex(featureIndex_),
+ textOffset(textOffset_),
+ iconOffset(iconOffset_) {
// Create the quads used for rendering the icon and glyphs.
if (addToBuffers) {
if (shapedIcon) {
- iconQuad = getIconQuad(anchor, *shapedIcon, line, layout, layoutTextSize, iconPlacement, shapedTextOrientations.first);
+ iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.first);
}
if (shapedTextOrientations.first) {
- auto quads = getGlyphQuads(anchor, shapedTextOrientations.first, textBoxScale, line, layout, textPlacement, positions);
+ auto quads = getGlyphQuads(shapedTextOrientations.first, layout, textPlacement, positions);
glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end());
}
if (shapedTextOrientations.second) {
- auto quads = getGlyphQuads(anchor, shapedTextOrientations.second, textBoxScale, line, layout, textPlacement, positions);
+ auto quads = getGlyphQuads(shapedTextOrientations.second, layout, textPlacement, positions);
glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end());
}
}
diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp
index f199d929df..f1df416cd1 100644
--- a/src/mbgl/layout/symbol_instance.hpp
+++ b/src/mbgl/layout/symbol_instance.hpp
@@ -13,7 +13,7 @@ class IndexedSubfeature;
class SymbolInstance {
public:
SymbolInstance(Anchor& anchor,
- const GeometryCoordinates& line,
+ GeometryCoordinates line,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
const style::SymbolLayoutProperties::Evaluated&,
@@ -23,14 +23,17 @@ public:
const float textBoxScale,
const float textPadding,
style::SymbolPlacementType textPlacement,
+ const std::array<float, 2> textOffset,
const float iconBoxScale,
const float iconPadding,
style::SymbolPlacementType iconPlacement,
+ const std::array<float, 2> iconOffset,
const GlyphPositionMap&,
const IndexedSubfeature&,
const std::size_t featureIndex);
- Point<float> point;
+ Anchor anchor;
+ GeometryCoordinates line;
uint32_t index;
bool hasText;
bool hasIcon;
@@ -40,6 +43,8 @@ public:
CollisionFeature iconCollisionFeature;
WritingModeType writingModes;
std::size_t featureIndex;
+ std::array<float, 2> textOffset;
+ std::array<float, 2> iconOffset;
};
} // namespace mbgl
diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp
index a664957489..e308da618f 100644
--- a/src/mbgl/layout/symbol_layout.cpp
+++ b/src/mbgl/layout/symbol_layout.cpp
@@ -305,6 +305,8 @@ void SymbolLayout::addFeature(const std::size_t index,
const float layoutTextSize = layout.evaluate<TextSize>(zoom + 1, feature);
const float layoutIconSize = layout.evaluate<IconSize>(zoom + 1, feature);
+ const std::array<float, 2> textOffset = layout.evaluate<TextOffset>(zoom, feature);
+ const std::array<float, 2> iconOffset = layout.evaluate<IconOffset>(zoom, feature);
// To reduce the number of labels that jump around when zooming we need
// to use a text-size value that is the same for all zoom levels.
@@ -355,8 +357,8 @@ void SymbolLayout::addFeature(const std::size_t index,
symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon,
layout.evaluate(zoom, feature), layoutTextSize,
addToBuffers, symbolInstances.size(),
- textBoxScale, textPadding, textPlacement,
- iconBoxScale, iconPadding, iconPlacement,
+ textBoxScale, textPadding, textPlacement, textOffset,
+ iconBoxScale, iconPadding, iconPlacement, iconOffset,
glyphPositionMap, indexedFeature, index);
};
@@ -455,8 +457,8 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
const float cos = std::cos(collisionTile.config.angle);
std::sort(symbolInstances.begin(), symbolInstances.end(), [sin, cos](SymbolInstance &a, SymbolInstance &b) {
- const int32_t aRotated = sin * a.point.x + cos * a.point.y;
- const int32_t bRotated = sin * b.point.x + cos * b.point.y;
+ const int32_t aRotated = sin * a.anchor.point.x + cos * a.anchor.point.y;
+ const int32_t bRotated = sin * b.anchor.point.x + cos * b.anchor.point.y;
return aRotated != bRotated ?
aRotated < bRotated :
a.index > b.index;
@@ -501,10 +503,21 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
const float placementZoom = util::max(util::log2(glyphScale) + zoom, 0.0f);
collisionTile.insertFeature(symbolInstance.textCollisionFeature, glyphScale, layout.get<TextIgnorePlacement>());
if (glyphScale < collisionTile.maxScale) {
+
+ const float labelAngle = std::fmod((symbolInstance.anchor.angle + collisionTile.config.angle) + 2 * M_PI, 2 * M_PI);
+ const bool inVerticalRange = (
+ (labelAngle > M_PI * 1.0 / 4.0 && labelAngle <= M_PI * 3.0 / 4) ||
+ (labelAngle > M_PI * 5.0 / 4.0 && labelAngle <= M_PI * 7.0 / 4));
+ const bool useVerticalMode = symbolInstance.writingModes & WritingModeType::Vertical && inVerticalRange;
+
+ const Range<float> sizeData = bucket->textSizeBinder->getVertexSizeData(feature);
+ bucket->text.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
+ symbolInstance.textOffset, placementZoom, useVerticalMode, symbolInstance.line);
+
for (const auto& symbol : symbolInstance.glyphQuads) {
addSymbol(
- bucket->text, *bucket->textSizeBinder, symbol, feature, placementZoom,
- keepUpright, textPlacement, collisionTile.config.angle, symbolInstance.writingModes, symbolInstance.point);
+ bucket->text, sizeData, symbol, placementZoom,
+ keepUpright, textPlacement, symbolInstance.anchor, bucket->text.placedSymbols.back());
}
}
}
@@ -513,9 +526,12 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
const float placementZoom = util::max(util::log2(iconScale) + zoom, 0.0f);
collisionTile.insertFeature(symbolInstance.iconCollisionFeature, iconScale, layout.get<IconIgnorePlacement>());
if (iconScale < collisionTile.maxScale && symbolInstance.iconQuad) {
+ const Range<float> sizeData = bucket->iconSizeBinder->getVertexSizeData(feature);
+ bucket->icon.placedSymbols.emplace_back(symbolInstance.anchor.point, symbolInstance.anchor.segment, sizeData.min, sizeData.max,
+ symbolInstance.iconOffset, placementZoom, false, symbolInstance.line);
addSymbol(
- bucket->icon, *bucket->iconSizeBinder, *symbolInstance.iconQuad, feature, placementZoom,
- keepUpright, iconPlacement, collisionTile.config.angle, symbolInstance.writingModes, symbolInstance.point);
+ bucket->icon, sizeData, *symbolInstance.iconQuad, placementZoom,
+ keepUpright, iconPlacement, symbolInstance.anchor, bucket->icon.placedSymbols.back());
}
}
@@ -534,15 +550,13 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
template <typename Buffer>
void SymbolLayout::addSymbol(Buffer& buffer,
- SymbolSizeBinder& sizeBinder,
+ const Range<float> sizeData,
const SymbolQuad& symbol,
- const SymbolFeature& feature,
const float placementZoom,
const bool keepUpright,
const style::SymbolPlacementType placement,
- const float placementAngle,
- const WritingModeType writingModes,
- const Point<float> labelAnchor) {
+ const Anchor& labelAnchor,
+ PlacedSymbol& placedSymbol) {
constexpr const uint16_t vertexLength = 4;
const auto &tl = symbol.tl;
@@ -551,30 +565,9 @@ void SymbolLayout::addSymbol(Buffer& buffer,
const auto &br = symbol.br;
const auto &tex = symbol.tex;
- float minZoom = util::max(zoom + util::log2(symbol.minScale), placementZoom);
- float maxZoom = util::min(zoom + util::log2(symbol.maxScale), util::MAX_ZOOM_F);
- const auto &anchorPoint = symbol.anchorPoint;
-
- // drop incorrectly oriented glyphs
- const float a = std::fmod(symbol.anchorAngle + placementAngle + M_PI, M_PI * 2);
- if (writingModes & WritingModeType::Vertical) {
- if (placement == style::SymbolPlacementType::Line && symbol.writingMode == WritingModeType::Vertical) {
- if (keepUpright && placement == style::SymbolPlacementType::Line && (a <= (M_PI * 5 / 4) || a > (M_PI * 7 / 4)))
- return;
- } else if (keepUpright && placement == style::SymbolPlacementType::Line && (a <= (M_PI * 3 / 4) || a > (M_PI * 5 / 4)))
- return;
- } else if (keepUpright && placement == style::SymbolPlacementType::Line &&
- (a <= M_PI / 2 || a > M_PI * 3 / 2)) {
- return;
- }
-
- if (maxZoom <= minZoom)
- return;
-
- // 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;
+ if (placement == style::SymbolPlacementType::Line && keepUpright) {
+ // drop incorrectly oriented glyphs
+ if ((symbol.writingMode == WritingModeType::Vertical) != placedSymbol.useVerticalMode) return;
}
if (buffer.segments.empty() || buffer.segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) {
@@ -587,20 +580,17 @@ void SymbolLayout::addSymbol(Buffer& buffer,
assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max());
uint16_t index = segment.vertexLength;
- // Encode angle of glyph
- uint8_t glyphAngle = std::round((symbol.glyphAngle / (M_PI * 2)) * 256);
-
// coordinates (2 triangles)
- buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tl, labelAnchor, tex.x, tex.y,
- minZoom, maxZoom, placementZoom, glyphAngle));
- buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tr, labelAnchor, tex.x + tex.w, tex.y,
- minZoom, maxZoom, placementZoom, glyphAngle));
- buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, bl, labelAnchor, tex.x, tex.y + tex.h,
- minZoom, maxZoom, placementZoom, glyphAngle));
- buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, br, labelAnchor, tex.x + tex.w, tex.y + tex.h,
- minZoom, maxZoom, placementZoom, glyphAngle));
+ buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, tl, symbol.glyphOffset.y, tex.x, tex.y, sizeData));
+ buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, tr, symbol.glyphOffset.y, tex.x + tex.w, tex.y, sizeData));
+ buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, bl, symbol.glyphOffset.y, tex.x, tex.y + tex.h, sizeData));
+ buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, br, symbol.glyphOffset.y, tex.x + tex.w, tex.y + tex.h, sizeData));
- sizeBinder.populateVertexVector(feature);
+ auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(labelAnchor.point, 0, placementZoom);
+ buffer.dynamicVertices.emplace_back(dynamicVertex);
+ buffer.dynamicVertices.emplace_back(dynamicVertex);
+ buffer.dynamicVertices.emplace_back(dynamicVertex);
+ buffer.dynamicVertices.emplace_back(dynamicVertex);
// add the two triangles, referencing the four coordinates we just inserted.
buffer.triangles.emplace_back(index + 0, index + 1, index + 2);
@@ -608,6 +598,8 @@ void SymbolLayout::addSymbol(Buffer& buffer,
segment.vertexLength += vertexLength;
segment.indexLength += 6;
+
+ placedSymbol.glyphOffsets.push_back(symbol.glyphOffset.x);
}
void SymbolLayout::addToDebugBuffers(CollisionTile& collisionTile, SymbolBucket& bucket) {
@@ -647,10 +639,10 @@ void SymbolLayout::addToDebugBuffers(CollisionTile& collisionTile, SymbolBucket&
auto& segment = collisionBox.segments.back();
uint16_t index = segment.vertexLength;
- collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.point, tl, maxZoom, placementZoom));
- collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.point, tr, maxZoom, placementZoom));
- collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.point, br, maxZoom, placementZoom));
- collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.point, bl, maxZoom, placementZoom));
+ collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tl, maxZoom, placementZoom));
+ collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, tr, maxZoom, placementZoom));
+ collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, br, maxZoom, placementZoom));
+ collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, symbolInstance.anchor.point, bl, maxZoom, placementZoom));
collisionBox.lines.emplace_back(index + 0, index + 1);
collisionBox.lines.emplace_back(index + 1, index + 2);
diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp
index 5dc0f3eb76..90f5b3c91d 100644
--- a/src/mbgl/layout/symbol_layout.hpp
+++ b/src/mbgl/layout/symbol_layout.hpp
@@ -20,6 +20,7 @@ class CollisionTile;
class SymbolBucket;
class Anchor;
class RenderLayer;
+class PlacedSymbol;
namespace style {
class Filter;
@@ -58,15 +59,13 @@ private:
// Adds placed items to the buffer.
template <typename Buffer>
void addSymbol(Buffer&,
- SymbolSizeBinder& sizeBinder,
+ const Range<float> sizeData,
const SymbolQuad&,
- const SymbolFeature& feature,
float scale,
const bool keepUpright,
const style::SymbolPlacementType,
- const float placementAngle,
- WritingModeType writingModes,
- const Point<float> labelAnchor);
+ const Anchor& labelAnchor,
+ PlacedSymbol& placedSymbol);
// Stores the layer so that we can hold on to GeometryTileFeature instances in SymbolFeature,
// which may reference data from this object.
diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp
new file mode 100644
index 0000000000..99555f7997
--- /dev/null
+++ b/src/mbgl/layout/symbol_projection.cpp
@@ -0,0 +1,266 @@
+#include <mbgl/layout/symbol_projection.hpp>
+#include <mbgl/map/transform_state.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
+#include <mbgl/renderer/layers/render_symbol_layer.hpp>
+#include <mbgl/renderer/frame_history.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/math.hpp>
+
+namespace mbgl {
+
+ /*
+ * # Overview of coordinate spaces
+ *
+ * ## Tile coordinate spaces
+ * Each label has an anchor. Some labels have corresponding line geometries.
+ * The points for both anchors and lines are stored in tile units. Each tile has it's own
+ * coordinate space going from (0, 0) at the top left to (EXTENT, EXTENT) at the bottom right.
+ *
+ * ## GL coordinate space
+ * At the end of everything, the vertex shader needs to produce a position in GL coordinate space,
+ * which is (-1, 1) at the top left and (1, -1) in the bottom right.
+ *
+ * ## Map pixel coordinate spaces
+ * Each tile has a pixel coordinate space. It's just the tile units scaled so that one unit is
+ * whatever counts as 1 pixel at the current zoom.
+ * This space is used for pitch-alignment=map, rotation-alignment=map
+ *
+ * ## Rotated map pixel coordinate spaces
+ * Like the above, but rotated so axis of the space are aligned with the viewport instead of the tile.
+ * This space is used for pitch-alignment=map, rotation-alignment=viewport
+ *
+ * ## Viewport pixel coordinate space
+ * (0, 0) is at the top left of the canvas and (pixelWidth, pixelHeight) is at the bottom right corner
+ * of the canvas. This space is used for pitch-alignment=viewport
+ *
+ *
+ * # Vertex projection
+ * It goes roughly like this:
+ * 1. project the anchor and line from tile units into the correct label coordinate space
+ * - map pixel space pitch-alignment=map rotation-alignment=map
+ * - rotated map pixel space pitch-alignment=map rotation-alignment=viewport
+ * - viewport pixel space pitch-alignment=viewport rotation-alignment=*
+ * 2. if the label follows a line, find the point along the line that is the correct distance from the anchor.
+ * 3. add the glyph's corner offset to the point from step 3
+ * 4. convert from the label coordinate space to gl coordinates
+ *
+ * For horizontal labels we want to do step 1 in the shader for performance reasons (no cpu work).
+ * This is what `u_label_plane_matrix` is used for.
+ * For labels aligned with lines we have to steps 1 and 2 on the cpu since we need access to the line geometry.
+ * This is what `updateLineLabels(...)` does.
+ * Since the conversion is handled on the cpu we just set `u_label_plane_matrix` to an identity matrix.
+ *
+ * Steps 3 and 4 are done in the shaders for all labels.
+ */
+
+ /*
+ * Returns a matrix for converting from tile units to the correct label coordinate space.
+ */
+ mat4 getLabelPlaneMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits) {
+ mat4 m;
+ matrix::identity(m);
+ if (pitchWithMap) {
+ matrix::scale(m, m, 1 / pixelsToTileUnits, 1 / pixelsToTileUnits, 1);
+ if (!rotateWithMap) {
+ matrix::rotate_z(m, m, state.getAngle());
+ }
+ } else {
+ matrix::scale(m, m, state.getSize().width / 2.0, -(state.getSize().height / 2.0), 1.0);
+ matrix::translate(m, m, 1, -1, 0);
+ matrix::multiply(m, m, posMatrix);
+ }
+ return m;
+ }
+
+ /*
+ * Returns a matrix for converting from the correct label coordinate space to gl coords.
+ */
+ mat4 getGlCoordMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits) {
+ mat4 m;
+ matrix::identity(m);
+ if (pitchWithMap) {
+ matrix::multiply(m, m, posMatrix);
+ matrix::scale(m, m, pixelsToTileUnits, pixelsToTileUnits, 1);
+ if (!rotateWithMap) {
+ matrix::rotate_z(m, m, -state.getAngle());
+ }
+ } else {
+ matrix::scale(m, m, 1, -1, 1);
+ matrix::translate(m, m, -1, -1, 0);
+ matrix::scale(m, m, 2.0 / state.getSize().width, 2.0 / state.getSize().height, 1.0);
+ }
+ return m;
+ }
+
+
+ Point<float> project(const Point<float>& point, const mat4& matrix) {
+ vec4 pos = {{ point.x, point.y, 0, 1 }};
+ matrix::transformMat4(pos, pos, matrix);
+ return { static_cast<float>(pos[0] / pos[3]), static_cast<float>(pos[1] / pos[3]) };
+ }
+
+ float evaluateSizeForFeature(const ZoomEvaluatedSize& zoomEvaluatedSize, const PlacedSymbol& placedSymbol) {
+ if (zoomEvaluatedSize.isFeatureConstant) {
+ return zoomEvaluatedSize.size;
+ } else {
+ if (zoomEvaluatedSize.isZoomConstant) {
+ return placedSymbol.lowerSize;
+ } else {
+ return placedSymbol.lowerSize + zoomEvaluatedSize.sizeT * (placedSymbol.upperSize - placedSymbol.lowerSize);
+ }
+ }
+ }
+
+ bool isVisible(const vec4& anchorPos, const float placementZoom, const std::array<double, 2>& clippingBuffer, const FrameHistory& frameHistory) {
+ const float x = anchorPos[0] / anchorPos[3];
+ const float y = anchorPos[1] / anchorPos[3];
+ const bool inPaddedViewport = (
+ x >= -clippingBuffer[0] &&
+ x <= clippingBuffer[0] &&
+ y >= -clippingBuffer[1] &&
+ y <= clippingBuffer[1]);
+ return inPaddedViewport && frameHistory.isVisible(placementZoom);
+ }
+
+ void addDynamicAttributes(const Point<float>& anchorPoint, const float angle, const float placementZoom,
+ gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) {
+ auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(anchorPoint, angle, placementZoom);
+ dynamicVertexArray.emplace_back(dynamicVertex);
+ dynamicVertexArray.emplace_back(dynamicVertex);
+ dynamicVertexArray.emplace_back(dynamicVertex);
+ dynamicVertexArray.emplace_back(dynamicVertex);
+ }
+
+ void hideGlyphs(size_t numGlyphs, gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) {
+ const Point<float> offscreenPoint = { -INFINITY, -INFINITY };
+ for (size_t i = 0; i < numGlyphs; i++) {
+ addDynamicAttributes(offscreenPoint, 0, 25, dynamicVertexArray);
+ }
+ }
+
+ struct PlacedGlyph {
+ PlacedGlyph(Point<float> point_, float angle_) : point(point_), angle(angle_) {}
+ Point<float> point;
+ float angle;
+ };
+
+ optional<PlacedGlyph> placeGlyphAlongLine(const float offsetX, const float lineOffsetX, const float lineOffsetY, const bool flip,
+ Point<float> anchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const mat4& labelPlaneMatrix) {
+
+ const float combinedOffsetX = flip ?
+ offsetX - lineOffsetX :
+ offsetX + lineOffsetX;
+
+ int16_t dir = combinedOffsetX > 0 ? 1 : -1;
+
+ float angle = 0.0;
+ if (flip) {
+ // The label needs to be flipped to keep text upright.
+ // Iterate in the reverse direction.
+ dir *= -1;
+ angle = M_PI;
+ }
+
+ if (dir < 0) angle += M_PI;
+
+ int32_t currentIndex = dir > 0 ? anchorSegment : anchorSegment + 1;
+
+ Point<float> current = anchorPoint;
+ Point<float> prev = anchorPoint;
+ float distanceToPrev = 0.0;
+ float currentSegmentDistance = 0.0;
+ const float absOffsetX = std::abs(combinedOffsetX);
+
+ while (distanceToPrev + currentSegmentDistance <= absOffsetX) {
+ currentIndex += dir;
+
+ // offset does not fit on the projected line
+ if (currentIndex < 0 || currentIndex >= static_cast<int32_t>(line.size())) return {};
+
+ prev = current;
+ current = project(convertPoint<float>(line.at(currentIndex)), labelPlaneMatrix);
+
+ distanceToPrev += currentSegmentDistance;
+ currentSegmentDistance = util::dist<float>(prev, current);
+ }
+
+ // The point is on the current segment. Interpolate to find it.
+ const float segmentInterpolationT = (absOffsetX - distanceToPrev) / currentSegmentDistance;
+ const Point<float> prevToCurrent = current - prev;
+ Point<float> p = (prevToCurrent * segmentInterpolationT) + prev;
+
+ // offset the point from the line to text-offset and icon-offset
+ p += util::perp(prevToCurrent) * static_cast<float>(lineOffsetY * dir / util::mag(prevToCurrent));
+
+ const float segmentAngle = angle + std::atan2(current.y - prev.y, current.x - prev.x);
+
+ return {{ p, segmentAngle }};
+ }
+
+ void placeGlyphsAlongLine(const PlacedSymbol& symbol, const float fontSize, const bool flip, const mat4& labelPlaneMatrix,
+ gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) {
+ const float fontScale = fontSize / 24.0;
+ const float lineOffsetX = symbol.lineOffset[0] * fontSize;
+ const float lineOffsetY = symbol.lineOffset[1] * fontSize;
+
+ const Point<float> anchorPoint = project(symbol.anchorPoint, labelPlaneMatrix);
+
+ std::vector<PlacedGlyph> placedGlyphs;
+ for (auto glyphOffsetX : symbol.glyphOffsets) {
+ auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix);
+ if (placedGlyph) {
+ placedGlyphs.push_back(*placedGlyph);
+ } else {
+ hideGlyphs(symbol.glyphOffsets.size(), dynamicVertexArray);
+ return;
+ }
+ }
+
+ for (auto& placedGlyph : placedGlyphs) {
+ addDynamicAttributes(placedGlyph.point, placedGlyph.angle, symbol.placementZoom, dynamicVertexArray);
+ }
+ }
+
+ void reprojectLineLabels(gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray, const std::vector<PlacedSymbol>& placedSymbols,
+ const mat4& posMatrix, const style::SymbolPropertyValues& values,
+ const RenderTile& tile, const SymbolSizeBinder& sizeBinder, const TransformState& state, const FrameHistory& frameHistory) {
+
+ const ZoomEvaluatedSize partiallyEvaluatedSize = sizeBinder.evaluateForZoom(state.getZoom());
+
+ const std::array<double, 2> clippingBuffer = {{ 256.0 / state.getSize().width * 2.0 + 1.0, 256.0 / state.getSize().height * 2.0 + 1.0 }};
+
+ const mat4 labelPlaneMatrix = getLabelPlaneMatrix(posMatrix, values.pitchAlignment == style::AlignmentType::Map,
+ values.rotationAlignment == style::AlignmentType::Map, state, tile.id.pixelsToTileUnits(1, state.getZoom()));
+
+ dynamicVertexArray.clear();
+
+ for (auto& placedSymbol : placedSymbols) {
+ vec4 anchorPos = {{ placedSymbol.anchorPoint.x, placedSymbol.anchorPoint.y, 0, 1 }};
+ matrix::transformMat4(anchorPos, anchorPos, posMatrix);
+
+ // Don't bother calculating the correct point for invisible labels.
+ if (!isVisible(anchorPos, placedSymbol.placementZoom, clippingBuffer, frameHistory)) {
+ hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray);
+ continue;
+ }
+
+ bool flip = false;
+ if (values.keepUpright) {
+ const Point<float> a = project(convertPoint<float>(placedSymbol.line.at(placedSymbol.segment)), posMatrix);
+ const Point<float> b = project(convertPoint<float>(placedSymbol.line.at(placedSymbol.segment + 1)), posMatrix);
+ flip = placedSymbol.useVerticalMode ? b.y > a.y : b.x < a.x;
+ }
+
+ const float cameraToAnchorDistance = anchorPos[3];
+ const float perspectiveRatio = 1 + 0.5 * ((cameraToAnchorDistance / state.getCameraToCenterDistance()) - 1.0);
+
+ const float fontSize = evaluateSizeForFeature(partiallyEvaluatedSize, placedSymbol);
+ const float pitchScaledFontSize = values.pitchAlignment == style::AlignmentType::Map ?
+ fontSize * perspectiveRatio :
+ fontSize / perspectiveRatio;
+
+ placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, flip, labelPlaneMatrix, dynamicVertexArray);
+ }
+ }
+} // end namespace mbgl
diff --git a/src/mbgl/layout/symbol_projection.hpp b/src/mbgl/layout/symbol_projection.hpp
new file mode 100644
index 0000000000..2652fe7ace
--- /dev/null
+++ b/src/mbgl/layout/symbol_projection.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <mbgl/util/mat4.hpp>
+#include <mbgl/gl/vertex_buffer.hpp>
+#include <mbgl/programs/symbol_program.hpp>
+
+namespace mbgl {
+
+ class TransformState;
+ class RenderTile;
+ class FrameHistory;
+ class SymbolSizeBinder;
+ class PlacedSymbol;
+ namespace style {
+ class SymbolPropertyValues;
+ } // end namespace style
+
+ mat4 getLabelPlaneMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits);
+ mat4 getGlCoordMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits);
+
+ void reprojectLineLabels(gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>&, const std::vector<PlacedSymbol>&,
+ const mat4& posMatrix, const style::SymbolPropertyValues&,
+ const RenderTile&, const SymbolSizeBinder& sizeBinder, const TransformState&, const FrameHistory& frameHistory);
+
+} // end namespace mbgl