From 8ef9fcdf3ee9d1266771878205568944aedc7aa6 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Mon, 10 Jul 2017 15:41:17 -0700 Subject: [core] Base label "keep-upright" orientation on start and end of label Fixes issue #9457. --- src/mbgl/layout/symbol_projection.cpp | 94 ++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp index 99555f7997..8ccd6a0410 100644 --- a/src/mbgl/layout/symbol_projection.cpp +++ b/src/mbgl/layout/symbol_projection.cpp @@ -144,6 +144,12 @@ namespace mbgl { Point point; float angle; }; + + enum PlacementResult { + OK, + NotEnoughRoom, + NeedsFlipping + }; optional placeGlyphAlongLine(const float offsetX, const float lineOffsetX, const float lineOffsetY, const bool flip, Point anchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const mat4& labelPlaneMatrix) { @@ -198,8 +204,14 @@ namespace mbgl { return {{ p, segmentAngle }}; } - void placeGlyphsAlongLine(const PlacedSymbol& symbol, const float fontSize, const bool flip, const mat4& labelPlaneMatrix, - gl::VertexVector& dynamicVertexArray) { + PlacementResult placeGlyphsAlongLine(const PlacedSymbol& symbol, + const float fontSize, + const bool flip, + const bool keepUpright, + const mat4& posMatrix, + const mat4& labelPlaneMatrix, + const mat4& glCoordMatrix, + gl::VertexVector& dynamicVertexArray) { const float fontScale = fontSize / 24.0; const float lineOffsetX = symbol.lineOffset[0] * fontSize; const float lineOffsetY = symbol.lineOffset[1] * fontSize; @@ -207,19 +219,60 @@ namespace mbgl { const Point anchorPoint = project(symbol.anchorPoint, labelPlaneMatrix); std::vector placedGlyphs; - for (auto glyphOffsetX : symbol.glyphOffsets) { - auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); - if (placedGlyph) { + if (symbol.glyphOffsets.size() > 1) { + + const float firstGlyphOffset = symbol.glyphOffsets.front(); + const float lastGlyphOffset = symbol.glyphOffsets.back(); + + optional firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); + if (!firstPlacedGlyph) + return PlacementResult::NotEnoughRoom; + + optional lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); + if (!lastPlacedGlyph) + return PlacementResult::NotEnoughRoom; + + const Point firstPoint = project(firstPlacedGlyph->point, glCoordMatrix); + const Point lastPoint = project(lastPlacedGlyph->point, glCoordMatrix); + + if (keepUpright && !flip && + (symbol.useVerticalMode ? firstPoint.y < lastPoint.y : firstPoint.x > lastPoint.x)) { + return PlacementResult::NeedsFlipping; + } + + placedGlyphs.push_back(*firstPlacedGlyph); + for (size_t glyphIndex = 1; glyphIndex < symbol.glyphOffsets.size() - 1; glyphIndex++) { + const float glyphOffsetX = symbol.glyphOffsets[glyphIndex]; + // Since first and last glyph fit on the line, we're sure that the rest of the glyphs can be placed + auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); placedGlyphs.push_back(*placedGlyph); - } else { - hideGlyphs(symbol.glyphOffsets.size(), dynamicVertexArray); - return; } + placedGlyphs.push_back(*lastPlacedGlyph); + } else { + // Only a single glyph to place + // So, determine whether to flip based on projected angle of the line segment it's on + if (keepUpright && !flip) { + const Point a = project(convertPoint(symbol.line.at(symbol.segment)), posMatrix); + const Point b = project(convertPoint(symbol.line.at(symbol.segment + 1)), posMatrix); + if (symbol.useVerticalMode ? b.y > a.y : b.x < a.x) { + return PlacementResult::NeedsFlipping; + } + } + assert(symbol.glyphOffsets.size() == 1); // We are relying on SymbolInstance.hasText filtering out symbols without any glyphs at all + const float glyphOffsetX = symbol.glyphOffsets.front(); + optional singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, + symbol.line, labelPlaneMatrix); + if (!singleGlyph) + return PlacementResult::NotEnoughRoom; + + placedGlyphs.push_back(*singleGlyph); } for (auto& placedGlyph : placedGlyphs) { addDynamicAttributes(placedGlyph.point, placedGlyph.angle, symbol.placementZoom, dynamicVertexArray); } + + return PlacementResult::OK; } void reprojectLineLabels(gl::VertexVector& dynamicVertexArray, const std::vector& placedSymbols, @@ -229,9 +282,15 @@ namespace mbgl { const ZoomEvaluatedSize partiallyEvaluatedSize = sizeBinder.evaluateForZoom(state.getZoom()); const std::array clippingBuffer = {{ 256.0 / state.getSize().width * 2.0 + 1.0, 256.0 / state.getSize().height * 2.0 + 1.0 }}; + + const bool pitchWithMap = values.pitchAlignment == style::AlignmentType::Map; + const bool rotateWithMap = values.rotationAlignment == style::AlignmentType::Map; + const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1, state.getZoom()); - const mat4 labelPlaneMatrix = getLabelPlaneMatrix(posMatrix, values.pitchAlignment == style::AlignmentType::Map, - values.rotationAlignment == style::AlignmentType::Map, state, tile.id.pixelsToTileUnits(1, state.getZoom())); + const mat4 labelPlaneMatrix = getLabelPlaneMatrix(posMatrix, pitchWithMap, + rotateWithMap, state, pixelsToTileUnits); + + const mat4 glCoordMatrix = getGlCoordMatrix(posMatrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits); dynamicVertexArray.clear(); @@ -245,13 +304,6 @@ namespace mbgl { continue; } - bool flip = false; - if (values.keepUpright) { - const Point a = project(convertPoint(placedSymbol.line.at(placedSymbol.segment)), posMatrix); - const Point b = project(convertPoint(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); @@ -260,7 +312,13 @@ namespace mbgl { fontSize * perspectiveRatio : fontSize / perspectiveRatio; - placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, flip, labelPlaneMatrix, dynamicVertexArray); + PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray); + + if (placeUnflipped == PlacementResult::NotEnoughRoom || + (placeUnflipped == PlacementResult::NeedsFlipping && + placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray) == PlacementResult::NotEnoughRoom)) { + hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray); + } } } } // end namespace mbgl -- cgit v1.2.1