#include #include #include #include #include #include namespace mbgl { const float globalMinScale = 0.5f; // underscale by 1 zoom level SymbolQuads getIconQuads(Anchor& anchor, const PositionedIcon& shapedIcon, const std::vector& line, const SymbolLayoutProperties& layout, const bool alongLine) { auto image = *(shapedIcon.image); const float border = 1.0; auto left = shapedIcon.left - border; auto right = left + image.pos.w / image.relativePixelRatio; auto top = shapedIcon.top - border; auto bottom = top + image.pos.h / image.relativePixelRatio; vec2 tl{left, top}; vec2 tr{right, top}; vec2 br{right, bottom}; vec2 bl{left, bottom}; float angle = layout.icon.rotate * M_PI / 180.0f; if (alongLine) { assert(static_cast(anchor.segment) < line.size()); const Coordinate &prev= line[anchor.segment]; if (anchor.y == prev.y && anchor.x == prev.x && static_cast(anchor.segment + 1) < line.size()) { const Coordinate &next= line[anchor.segment + 1]; angle += std::atan2(anchor.y - next.y, anchor.x - next.x) + M_PI; } else { 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 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, image.pos, 0, anchor, globalMinScale, std::numeric_limits::infinity()); return quads; } struct GlyphInstance { explicit GlyphInstance(const vec2 &anchorPoint_) : anchorPoint(anchorPoint_) {} explicit GlyphInstance(const vec2 &anchorPoint_, float offset_, float minScale_, float maxScale_, float angle_) : anchorPoint(anchorPoint_), offset(offset_), minScale(minScale_), maxScale(maxScale_), angle(angle_) {} const vec2 anchorPoint; const float offset = 0.0f; const float minScale = globalMinScale; const float maxScale = std::numeric_limits::infinity(); const float angle = 0.0f; }; typedef std::vector GlyphInstances; void getSegmentGlyphs(std::back_insert_iterator glyphs, Anchor &anchor, float offset, const std::vector &line, int segment, bool forward) { const bool upsideDown = !forward; if (offset < 0) forward = !forward; if (forward) segment++; assert((int)line.size() > segment); vec2 end = line[segment]; vec2 newAnchorPoint = anchor; float prevscale = std::numeric_limits::infinity(); offset = std::fabs(offset); const float placementScale = anchor.scale; while (true) { const float dist = util::dist(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(upsideDown ? M_PI : 0.0), /* minScale */ scale, /* maxScale */ prevscale, /* angle */ static_cast(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 normal = util::normal(newAnchorPoint, end) * dist; newAnchorPoint = newAnchorPoint - normal; prevscale = scale; } } SymbolQuads getGlyphQuads(Anchor& anchor, const Shaping& shapedText, const float boxScale, const std::vector& line, const SymbolLayoutProperties& layout, const bool alongLine, const GlyphPositions& face) { const float textRotate = layout.text.rotate * M_PI / 180; const bool keepUpright = layout.text.keepUpright; 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 &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 otl{x1, y1}; const vec2 otr{x2, y1}; const vec2 obl{x1, y2}; const vec2 obr{x2, y2}; for (const GlyphInstance &instance : glyphInstances) { vec2 tl = otl; vec2 tr = otr; vec2 bl = obl; vec2 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 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; } } // namespace mbgl