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 maxAngle = props.text.max_angle * 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; const uint32_t buffer = 3; 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) continue; const float x = (origin.x + shape.x + glyph.metrics.left - buffer + rect.w / 2) * boxScale; GlyphInstances glyphInstances; if (anchor.segment >= 0 && alongLine) { getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, x, line, anchor.segment, 1, maxAngle); if (keepUpright) getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, x, line, anchor.segment, -1, maxAngle); } else { glyphInstances.emplace_back(GlyphInstance{anchor}); } 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; const vec2<float> otl{x1, y1}; const vec2<float> otr{x2, y1}; const vec2<float> obl{x1, y2}; const vec2<float> obr{x2, y2}; const CollisionRect obox{boxScale * x1, boxScale * y1, boxScale * x2, boxScale * y2}; for (const GlyphInstance &instance : glyphInstances) { vec2<float> tl = otl; vec2<float> tr = otr; vec2<float> bl = obl; vec2<float> br = obr; CollisionRect box = obox; // Clamp to -90/+90 degrees const float angle = instance.angle + rotate; 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); // Remember the glyph for later insertion. placement.shapes.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)}; } placement.boxes.emplace_back(box, instance.anchor, glyphMinScale, instance.maxScale, padding); } } } // TODO avoid creating the boxes in the first place? if (horizontal) placement.boxes = {getMergedBoxes(placement.boxes, anchor)}; const float minPlacementScale = anchor.scale; placement.minScale = std::numeric_limits<float>::infinity(); for (const GlyphBox &box : placement.boxes) { placement.minScale = util::min(placement.minScale, box.minScale); } placement.minScale = util::max(minPlacementScale, Placement::globalMinScale); return placement; }
SymbolQuads getGlyphQuads(Anchor& anchor, const Shaping& shapedText, const float boxScale, const GeometryCoordinates& line, const SymbolLayoutProperties& layout, const bool alongLine, const GlyphPositions& face) { const float textRotate = layout.text.rotate * util::DEG2RAD; 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<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; }
SymbolQuads getGlyphQuads(Anchor& anchor, const Shaping& shapedText, const float boxScale, const GeometryCoordinates& line, const SymbolLayoutProperties::Evaluated& layout, const style::SymbolPlacementType placement, const GlyphPositionMap& positions) { const float textRotate = layout.get<TextRotate>() * util::DEG2RAD; const bool keepUpright = layout.get<TextKeepUpright>(); SymbolQuads quads; for (const PositionedGlyph &positionedGlyph: shapedText.positionedGlyphs) { auto positionsIt = positions.find(positionedGlyph.glyph); if (positionsIt == positions.end()) continue; const GlyphPosition& glyph = positionsIt->second; const Rect<uint16_t>& rect = glyph.rect; const float centerX = (positionedGlyph.x + glyph.metrics.advance / 2.0f) * boxScale; GlyphInstances glyphInstances; if (placement == style::SymbolPlacementType::Line) { getLineGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, false); if (keepUpright) getLineGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, true); } 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<float> center{positionedGlyph.x, static_cast<float>(static_cast<float>(glyph.metrics.advance) / 2.0)}; Point<float> otl{x1, y1}; Point<float> otr{x2, y1}; Point<float> obl{x1, y2}; Point<float> obr{x2, y2}; if (positionedGlyph.angle != 0) { otl = util::rotate(otl - center, positionedGlyph.angle) + center; otr = util::rotate(otr - center, positionedGlyph.angle) + center; obl = util::rotate(obl - center, positionedGlyph.angle) + center; obr = util::rotate(obr - center, positionedGlyph.angle) + center; } for (const GlyphInstance &instance : glyphInstances) { Point<float> tl = otl; Point<float> tr = otr; Point<float> bl = obl; Point<float> br = obr; if (textRotate) { // Compute the transformation matrix. float angle_sin = std::sin(textRotate); float angle_cos = std::cos(textRotate); std::array<float, 4> 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); // All the glyphs for a label are tagged with either the "right side up" or "upside down" anchor angle, // which is used at placement time to determine which set to show const float anchorAngle = std::fmod((anchor.angle + (instance.upsideDown ? M_PI : 0.0) + 2 * M_PI), (2 * M_PI)); const float glyphAngle = std::fmod((instance.angle + (instance.upsideDown ? M_PI : 0.0) + 2 * M_PI), (2 * M_PI)); quads.emplace_back(tl, tr, bl, br, rect, anchorAngle, glyphAngle, instance.anchorPoint, glyphMinScale, instance.maxScale, shapedText.writingMode); } } return quads; }