206 lines
6.8 KiB
C++
206 lines
6.8 KiB
C++
#include <mbgl/text/quads.hpp>
|
|
#include <mbgl/text/shaping.hpp>
|
|
#include <mbgl/geometry/anchor.hpp>
|
|
#include <mbgl/style/style_layout.hpp>
|
|
#include <mbgl/util/math.hpp>
|
|
#include <cassert>
|
|
|
|
namespace mbgl {
|
|
|
|
const float globalMinScale = 0.5f; // underscale by 1 zoom level
|
|
|
|
SymbolQuads getIconQuads(Anchor &anchor, const PositionedIcon &shapedIcon,
|
|
const std::vector<Coordinate> &line, const StyleLayoutSymbol &layout,
|
|
const bool alongLine) {
|
|
|
|
const float border = 1.0;
|
|
auto left = shapedIcon.left - border;
|
|
auto right = left + shapedIcon.image.w;
|
|
auto top = shapedIcon.top - border;
|
|
auto bottom = top + shapedIcon.image.h;
|
|
vec2<float> tl{left, top};
|
|
vec2<float> tr{right, top};
|
|
vec2<float> br{right, bottom};
|
|
vec2<float> bl{left, bottom};
|
|
|
|
|
|
float angle = layout.icon.rotate * M_PI / 180.0f;
|
|
if (alongLine) {
|
|
assert(static_cast<unsigned int>(anchor.segment) < line.size());
|
|
const Coordinate &prev= line[anchor.segment];
|
|
angle += std::atan2(anchor.y - prev.y, anchor.x - prev.x);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
SymbolQuads quads;
|
|
quads.emplace_back(tl, tr, bl, br, shapedIcon.image, 0, anchor, globalMinScale, std::numeric_limits<float>::infinity());
|
|
return quads;
|
|
}
|
|
|
|
struct GlyphInstance {
|
|
explicit GlyphInstance(const vec2<float> &anchorPoint_) : anchorPoint(anchorPoint_) {}
|
|
explicit GlyphInstance(const vec2<float> &anchorPoint_, float offset_, float minScale_, float maxScale_,
|
|
float angle_)
|
|
: anchorPoint(anchorPoint_), offset(offset_), minScale(minScale_), maxScale(maxScale_), angle(angle_) {}
|
|
|
|
const vec2<float> anchorPoint;
|
|
const float offset = 0.0f;
|
|
const float minScale = 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, bool forward) {
|
|
|
|
const bool upsideDown = !forward;
|
|
|
|
if (offset < 0)
|
|
forward = !forward;
|
|
|
|
if (forward)
|
|
segment++;
|
|
|
|
assert((int)line.size() > segment);
|
|
vec2<float> end = line[segment];
|
|
vec2<float> newAnchorPoint = anchor;
|
|
float prevscale = std::numeric_limits<float>::infinity();
|
|
|
|
offset = std::fabs(offset);
|
|
|
|
const float placementScale = anchor.scale;
|
|
|
|
while (true) {
|
|
const float dist = util::dist<float>(newAnchorPoint, end);
|
|
const float scale = offset / dist;
|
|
float angle = std::atan2(end.y - newAnchorPoint.y, end.x - newAnchorPoint.x);
|
|
if (!forward)
|
|
angle += M_PI;
|
|
if (upsideDown)
|
|
angle += M_PI;
|
|
|
|
glyphs = GlyphInstance{
|
|
/* anchor */ newAnchorPoint,
|
|
/* 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)))};
|
|
|
|
if (scale <= placementScale)
|
|
break;
|
|
|
|
newAnchorPoint = end;
|
|
|
|
// skip duplicate nodes
|
|
while (newAnchorPoint == end) {
|
|
segment += forward ? 1 : -1;
|
|
if ((int)line.size() <= segment || segment < 0) {
|
|
anchor.scale = scale;
|
|
return;
|
|
}
|
|
end = line[segment];
|
|
}
|
|
|
|
vec2<float> normal = util::normal<float>(newAnchorPoint, end) * dist;
|
|
newAnchorPoint = newAnchorPoint - normal;
|
|
|
|
prevscale = scale;
|
|
}
|
|
}
|
|
|
|
SymbolQuads getGlyphQuads(Anchor &anchor, const Shaping &shapedText,
|
|
const float boxScale, const std::vector<Coordinate> &line, const StyleLayoutSymbol &layout,
|
|
const bool alongLine, const GlyphPositions &face) {
|
|
|
|
const float textRotate = layout.text.rotate * M_PI / 180;
|
|
const bool keepUpright = layout.text.keep_upright;
|
|
|
|
SymbolQuads quads;
|
|
|
|
for (const PositionedGlyph &positionedGlyph: shapedText.positionedGlyphs) {
|
|
auto face_it = face.find(positionedGlyph.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.hasArea())
|
|
continue;
|
|
|
|
const float centerX = (positionedGlyph.x + glyph.metrics.advance / 2.0f) * boxScale;
|
|
|
|
GlyphInstances glyphInstances;
|
|
if (alongLine) {
|
|
getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, true);
|
|
if (keepUpright)
|
|
getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, false);
|
|
|
|
} else {
|
|
glyphInstances.emplace_back(GlyphInstance{anchor});
|
|
}
|
|
|
|
// The rects have an addditional buffer that is not included in their size;
|
|
const float glyphPadding = 1.0f;
|
|
const float rectBuffer = 3.0f + glyphPadding;
|
|
|
|
const float x1 = positionedGlyph.x + glyph.metrics.left - rectBuffer;
|
|
const float y1 = positionedGlyph.y - glyph.metrics.top - rectBuffer;
|
|
const float x2 = x1 + rect.w;
|
|
const float y2 = y1 + rect.h;
|
|
|
|
const vec2<float> otl{x1, y1};
|
|
const vec2<float> otr{x2, y1};
|
|
const vec2<float> obl{x1, y2};
|
|
const vec2<float> obr{x2, y2};
|
|
|
|
for (const GlyphInstance &instance : glyphInstances) {
|
|
|
|
vec2<float> tl = otl;
|
|
vec2<float> tr = otr;
|
|
vec2<float> bl = obl;
|
|
vec2<float> br = obr;
|
|
const float angle = instance.angle + textRotate;
|
|
|
|
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);
|
|
}
|
|
|
|
// Prevent label from extending past the end of the line
|
|
const float glyphMinScale = std::max(instance.minScale, anchor.scale);
|
|
|
|
const float glyphAngle = std::fmod((anchor.angle + textRotate + instance.offset + 2 * M_PI), (2 * M_PI));
|
|
quads.emplace_back(tl, tr, bl, br, rect, glyphAngle, instance.anchorPoint, glyphMinScale, instance.maxScale);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return quads;
|
|
}
|
|
}
|