#include #include #include #include #include #include #include #include namespace mbgl { using namespace style; const float globalMinScale = 0.5f; // underscale by 1 zoom level SymbolQuads getIconQuads(Anchor& anchor, const PositionedIcon& shapedIcon, const GeometryCoordinates& line, const SymbolLayoutProperties::Evaluated& layout, const style::SymbolPlacementType placement, const Shaping& shapedText) { 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; Point tl; Point tr; Point br; Point bl; if (layout.get() != IconTextFitType::None && shapedText) { auto iconWidth = right - left; auto iconHeight = bottom - top; auto size = layout.get() / 24.0f; auto textLeft = shapedText.left * size; auto textRight = shapedText.right * size; auto textTop = shapedText.top * size; auto textBottom = shapedText.bottom * size; auto textWidth = textRight - textLeft; auto textHeight = textBottom - textTop;; auto padT = layout.get()[0]; auto padR = layout.get()[1]; auto padB = layout.get()[2]; auto padL = layout.get()[3]; auto offsetY = layout.get() == IconTextFitType::Width ? (textHeight - iconHeight) * 0.5 : 0; auto offsetX = layout.get() == IconTextFitType::Height ? (textWidth - iconWidth) * 0.5 : 0; auto width = layout.get() == IconTextFitType::Width || layout.get() == IconTextFitType::Both ? textWidth : iconWidth; auto height = layout.get() == IconTextFitType::Height || layout.get() == IconTextFitType::Both ? textHeight : iconHeight; left = textLeft + offsetX - padL; top = textTop + offsetY - padT; right = textLeft + offsetX + padR + width; bottom = textTop + offsetY + padB + height; tl = {left, top}; tr = {right, top}; br = {right, bottom}; bl = {left, bottom}; } else { tl = {left, top}; tr = {right, top}; br = {right, bottom}; bl = {left, bottom}; } float angle = layout.get() * util::DEG2RAD; if (placement == style::SymbolPlacementType::Line) { assert(static_cast(anchor.segment) < line.size()); const GeometryCoordinate &prev= line[anchor.segment]; if (anchor.point.y == prev.y && anchor.point.x == prev.x && static_cast(anchor.segment + 1) < line.size()) { const GeometryCoordinate &next= line[anchor.segment + 1]; angle += std::atan2(anchor.point.y - next.y, anchor.point.x - next.x) + M_PI; } else { angle += std::atan2(anchor.point.y - prev.y, anchor.point.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 = util::matrixMultiply(matrix, tl); tr = util::matrixMultiply(matrix, tr); bl = util::matrixMultiply(matrix, bl); br = util::matrixMultiply(matrix, br); } SymbolQuads quads; quads.emplace_back(tl, tr, bl, br, image.pos, 0, 0, anchor.point, globalMinScale, std::numeric_limits::infinity()); return quads; } struct GlyphInstance { explicit GlyphInstance(Point anchorPoint_) : anchorPoint(std::move(anchorPoint_)) {} explicit GlyphInstance(Point anchorPoint_, float offset_, float minScale_, float maxScale_, float angle_) : anchorPoint(std::move(anchorPoint_)), offset(offset_), minScale(minScale_), maxScale(maxScale_), angle(angle_) {} const Point 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 GeometryCoordinates &line, int segment, bool forward) { const bool upsideDown = !forward; if (offset < 0) forward = !forward; if (forward) segment++; assert((int)line.size() > segment); Point end = convertPoint(line[segment]); Point newAnchorPoint = anchor.point; 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; 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 = convertPoint(line[segment]); } Point normal = util::normal(newAnchorPoint, end) * dist; newAnchorPoint = newAnchorPoint - normal; prevscale = scale; } } SymbolQuads getGlyphQuads(Anchor& anchor, const Shaping& shapedText, const float boxScale, const GeometryCoordinates& line, const SymbolLayoutProperties::Evaluated& layout, const style::SymbolPlacementType placement, const GlyphPositions& face) { const float textRotate = layout.get() * util::DEG2RAD; const bool keepUpright = layout.get(); 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 (placement == style::SymbolPlacementType::Line) { 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.point}); } // 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 Point otl{x1, y1}; const Point otr{x2, y1}; const Point obl{x1, y2}; const Point obr{x2, y2}; for (const GlyphInstance &instance : glyphInstances) { Point tl = otl; Point tr = otr; Point bl = obl; Point br = obr; if (textRotate) { // Compute the transformation matrix. float angle_sin = std::sin(textRotate); float angle_cos = std::cos(textRotate); std::array matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}}; tl = util::matrixMultiply(matrix, tl); tr = util::matrixMultiply(matrix, tr); bl = util::matrixMultiply(matrix, bl); br = util::matrixMultiply(matrix, br); } // Prevent label from extending past the end of the line const float glyphMinScale = std::max(instance.minScale, anchor.scale); const float anchorAngle = std::fmod((anchor.angle + instance.offset + 2 * M_PI), (2 * M_PI)); const float glyphAngle = std::fmod((instance.angle + instance.offset + 2 * M_PI), (2 * M_PI)); quads.emplace_back(tl, tr, bl, br, rect, anchorAngle, glyphAngle, instance.anchorPoint, glyphMinScale, instance.maxScale); } } return quads; } } // namespace mbgl