From 99a12946aee19d381ee4e97830c0ee5735445696 Mon Sep 17 00:00:00 2001 From: Young Hahn Date: Thu, 2 Jun 2016 16:52:45 -0400 Subject: [PATCH 1/4] First pass at unskewed line labels on pitched maps --- js/data/bucket/symbol_bucket.js | 17 +++++--- js/render/draw_symbol.js | 28 ++++++------ js/symbol/quads.js | 76 +++++++++++++++++++++++++-------- package.json | 2 +- 4 files changed, 85 insertions(+), 38 deletions(-) diff --git a/js/data/bucket/symbol_bucket.js b/js/data/bucket/symbol_bucket.js index 11fdd34a377..358638df30e 100644 --- a/js/data/bucket/symbol_bucket.js +++ b/js/data/bucket/symbol_bucket.js @@ -69,7 +69,7 @@ var programAttributes = [{ type: 'Uint8' }]; -function addVertex(array, x, y, ox, oy, tx, ty, minzoom, maxzoom, labelminzoom) { +function addVertex(array, x, y, ox, oy, tx, ty, minzoom, maxzoom, labelminzoom, labelangle) { return array.emplaceBack( // pos x, @@ -81,7 +81,7 @@ function addVertex(array, x, y, ox, oy, tx, ty, minzoom, maxzoom, labelminzoom) tx / 4, // tex ty / 4, // tex (labelminzoom || 0) * 10, // labelminzoom - 0, + labelangle, // labelangle // data2 (minzoom || 0) * 10, // minzoom Math.min(maxzoom || 25, 25) * 10); // minzoom @@ -467,7 +467,7 @@ SymbolBucket.prototype.addSymbols = function(programName, quadsStart, quadsEnd, // drop upside down versions of glyphs var a = (angle + placementAngle + Math.PI) % (Math.PI * 2); - if (keepUpright && alongLine && (a <= Math.PI / 2 || a > Math.PI * 3 / 2)) continue; + if (keepUpright && alongLine && symbol.curved && (a <= Math.PI / 2 || a > Math.PI * 3 / 2)) continue; var tl = symbol.tl, tr = symbol.tr, @@ -484,10 +484,13 @@ SymbolBucket.prototype.addSymbols = function(programName, quadsStart, quadsEnd, // Lower min zoom so that while fading out the label it can be shown outside of collision-free zoom levels if (minZoom === placementZoom) minZoom = 0; - var index = addVertex(vertexArray, anchorPoint.x, anchorPoint.y, tl.x, tl.y, tex.x, tex.y, minZoom, maxZoom, placementZoom); - addVertex(vertexArray, anchorPoint.x, anchorPoint.y, tr.x, tr.y, tex.x + tex.w, tex.y, minZoom, maxZoom, placementZoom); - addVertex(vertexArray, anchorPoint.x, anchorPoint.y, bl.x, bl.y, tex.x, tex.y + tex.h, minZoom, maxZoom, placementZoom); - addVertex(vertexArray, anchorPoint.x, anchorPoint.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h, minZoom, maxZoom, placementZoom); + // Encode angle of line together with symbol.alongLine + var placementAngleLine = symbol.curved ? 0 : 1 + Math.round(((angle%Math.PI)/Math.PI)*255); + + var index = addVertex(vertexArray, anchorPoint.x, anchorPoint.y, tl.x, tl.y, tex.x, tex.y, minZoom, maxZoom, placementZoom, placementAngleLine); + addVertex(vertexArray, anchorPoint.x, anchorPoint.y, tr.x, tr.y, tex.x + tex.w, tex.y, minZoom, maxZoom, placementZoom, placementAngleLine); + addVertex(vertexArray, anchorPoint.x, anchorPoint.y, bl.x, bl.y, tex.x, tex.y + tex.h, minZoom, maxZoom, placementZoom, placementAngleLine); + addVertex(vertexArray, anchorPoint.x, anchorPoint.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h, minZoom, maxZoom, placementZoom, placementAngleLine); elementArray.emplaceBack(index, index + 1, index + 2); elementArray.emplaceBack(index + 1, index + 2, index + 3); diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index 9bf42783855..51fe6b853d4 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -109,24 +109,26 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, bufferGroups, isTex var defaultSize = isText ? 24 : 1; var fontScale = size / defaultSize; - var extrudeScale, s, gammaScale; - if (alignedWithMap) { - s = pixelsToTileUnits(tile, 1, painter.transform.zoom) * fontScale; - gammaScale = 1 / Math.cos(tr._pitch); - extrudeScale = [s, s]; - } else { - s = painter.transform.altitude * fontScale; - gammaScale = 1; - extrudeScale = [ tr.pixelsToGLUnits[0] * s, tr.pixelsToGLUnits[1] * s]; - } + var s1 = painter.transform.altitude * fontScale; + var s2 = pixelsToTileUnits(tile, 1, painter.transform.zoom) * fontScale; + var gammaScale = 1; // 1 / Math.cos(tr._pitch); + var extrudeScale = [ tr.pixelsToGLUnits[0] * s1, tr.pixelsToGLUnits[1] * s1]; + var extrudeScaleSkewed = [s2, s2]; if (!isText && !painter.style.sprite.loaded()) return; - var program = painter.useProgram(sdf ? 'sdf' : 'icon'); + var program; + if (sdf && alignedWithMap) { + program = painter.useProgram('sdf'); + } else if (sdf) { + program = painter.useProgram('sdfviewport'); + } else { + program = painter.useProgram('icon'); + } gl.uniformMatrix4fv(program.u_matrix, false, painter.translatePosMatrix(posMatrix, tile, translate, translateAnchor)); - gl.uniform1i(program.u_skewed, alignedWithMap); gl.uniform2fv(program.u_extrude_scale, extrudeScale); + gl.uniform2fv(program.u_extrude_scale_skewed, extrudeScaleSkewed); gl.activeTexture(gl.TEXTURE0); gl.uniform1i(program.u_texture, 0); @@ -181,6 +183,8 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, bufferGroups, isTex gl.uniform4fv(program.u_color, color); gl.uniform1f(program.u_opacity, opacity); gl.uniform1f(program.u_buffer, (256 - 64) / 256); + gl.uniform1f(program.u_pitch, tr.pitch / 360 * 2 * Math.PI); + gl.uniform1f(program.u_aspect_ratio, tr.width / tr.height); for (var i = 0; i < bufferGroups.length; i++) { group = bufferGroups[i]; diff --git a/js/symbol/quads.js b/js/symbol/quads.js index 24ff5d755f4..235509bff05 100644 --- a/js/symbol/quads.js +++ b/js/symbol/quads.js @@ -10,6 +10,10 @@ module.exports = { var minScale = 0.5; // underscale by 1 zoom level +// Characters along a line must be placed within 10 degree threshold to be +// straight enough for linear placement. +var LINEAR_THRESHOLD = 10 / 360 * (Math.PI*2); + /** * A textured quad for rendering a single icon or glyph. * @@ -24,11 +28,12 @@ var minScale = 0.5; // underscale by 1 zoom level * @param {number} angle The angle of the label at it's center, not the angle of this quad. * @param {number} minScale The minimum scale, relative to the tile's intended scale, that the glyph can be shown at. * @param {number} maxScale The maximum scale, relative to the tile's intended scale, that the glyph can be shown at. + * @param {boolean} curved Whether this quad was generated for display along a curved line. * * @class SymbolQuad * @private */ -function SymbolQuad(anchorPoint, tl, tr, bl, br, tex, angle, minScale, maxScale) { +function SymbolQuad(anchorPoint, tl, tr, bl, br, tex, angle, minScale, maxScale, curved) { this.anchorPoint = anchorPoint; this.tl = tl; this.tr = tr; @@ -38,6 +43,7 @@ function SymbolQuad(anchorPoint, tl, tr, bl, br, tex, angle, minScale, maxScale) this.angle = angle; this.minScale = minScale; this.maxScale = maxScale; + this.curved = curved; } /** @@ -109,36 +115,71 @@ function getGlyphQuads(anchor, shaping, boxScale, line, layout, alongLine) { var keepUpright = layout['text-keep-upright']; var positionedGlyphs = shaping.positionedGlyphs; + var segmentedGlyphs = []; var quads = []; + var angleMin = Infinity; + var angleMax = -Infinity; + + // First iteration, generate all curved and linear glyphs for (var k = 0; k < positionedGlyphs.length; k++) { + if (!positionedGlyphs[k].glyph.rect) continue; + var positionedGlyph = positionedGlyphs[k]; var glyph = positionedGlyph.glyph; var rect = glyph.rect; - if (!rect) continue; - var centerX = (positionedGlyph.x + glyph.advance / 2) * boxScale; - var glyphInstances; + var curved = []; + var linear = []; var labelMinScale = minScale; if (alongLine) { - glyphInstances = []; - labelMinScale = getSegmentGlyphs(glyphInstances, anchor, centerX, line, anchor.segment, true); + labelMinScale = getSegmentGlyphs(curved, anchor, centerX, line, anchor.segment, true); + if (positionedGlyph.codePoint > 32) { + for (var i = 0; i < curved.length; i++) { + if (curved[i].minScale < 1.0) continue; + angleMin = Math.min(curved[i].angle, angleMin); + angleMax = Math.max(curved[i].angle, angleMax); + } + } if (keepUpright) { - labelMinScale = Math.min(labelMinScale, getSegmentGlyphs(glyphInstances, anchor, centerX, line, anchor.segment, false)); + labelMinScale = Math.min(labelMinScale, getSegmentGlyphs(curved, anchor, centerX, line, anchor.segment, false)); } - - } else { - glyphInstances = [{ - anchorPoint: new Point(anchor.x, anchor.y), - offset: 0, - angle: 0, - maxScale: Infinity, - minScale: minScale - }]; } + linear.push({ + anchorPoint: new Point(anchor.x, anchor.y), + offset: 0, + angle: 0, + maxScale: Infinity, + minScale: minScale, + alongLine: false + }); + + segmentedGlyphs[k] = { + curved: curved, + linear: linear, + labelMinScale: labelMinScale + }; + } + + // Second iteration, determine which glyph placements to use + for (var k = 0; k < positionedGlyphs.length; k++) { + if (!positionedGlyphs[k].glyph.rect) continue; + + var positionedGlyph = positionedGlyphs[k]; + var glyph = positionedGlyph.glyph; + var rect = glyph.rect; + + var labelMinScale = segmentedGlyphs[k].labelMinScale; + + // Find the angle between min and max angles of positioned glyph + // characters as a cheap way to determine whether a label's characters + // are largely placed in a single linear line. + var curved = alongLine && ((angleMax-angleMin) >= LINEAR_THRESHOLD); + var glyphInstances = curved ? segmentedGlyphs[k].curved : segmentedGlyphs[k].linear; + var x1 = positionedGlyph.x + glyph.left, y1 = positionedGlyph.y - glyph.top, x2 = x1 + rect.w, @@ -173,8 +214,7 @@ function getGlyphQuads(anchor, shaping, boxScale, line, layout, alongLine) { var glyphMinScale = Math.max(instance.minScale, labelMinScale); var glyphAngle = (anchor.angle + textRotate + instance.offset + 2 * Math.PI) % (2 * Math.PI); - quads.push(new SymbolQuad(instance.anchorPoint, tl, tr, bl, br, rect, glyphAngle, glyphMinScale, instance.maxScale)); - + quads.push(new SymbolQuad(instance.anchorPoint, tl, tr, bl, br, rect, glyphAngle, glyphMinScale, instance.maxScale, curved)); } } diff --git a/package.json b/package.json index 52bcc9834cf..4268a113fdb 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "grid-index": "^0.1.0", "mapbox-gl-function": "^1.2.1", "mapbox-gl-js-supported": "^1.1.0", - "mapbox-gl-shaders": "mapbox/mapbox-gl-shaders#f0b94dcc5f782958e9aade61d592a207d8a46e0f", + "mapbox-gl-shaders": "mapbox/mapbox-gl-shaders#7467fd7916186e1c7201188d85ebdd5e81c3678e", "mapbox-gl-style-spec": "^8.7.0", "minifyify": "^7.0.1", "pbf": "^1.3.2", From f7aed4eeb9b1efc2a9ce9c438d2db2e0c35378cd Mon Sep 17 00:00:00 2001 From: Young Hahn Date: Sat, 4 Jun 2016 13:05:21 -0400 Subject: [PATCH 2/4] Unskew curved labels as well --- js/data/bucket/symbol_bucket.js | 22 ++++---- js/render/draw_symbol.js | 41 ++++++++------ js/symbol/quads.js | 96 ++++++++++----------------------- js/symbol/symbol_quads.js | 9 ++-- package.json | 6 +-- test/js/symbol/quads.test.js | 6 ++- 6 files changed, 77 insertions(+), 103 deletions(-) diff --git a/js/data/bucket/symbol_bucket.js b/js/data/bucket/symbol_bucket.js index 358638df30e..fc62cbfc5a5 100644 --- a/js/data/bucket/symbol_bucket.js +++ b/js/data/bucket/symbol_bucket.js @@ -462,12 +462,11 @@ SymbolBucket.prototype.addSymbols = function(programName, quadsStart, quadsEnd, for (var k = quadsStart; k < quadsEnd; k++) { - var symbol = this.symbolQuadsArray.get(k).SymbolQuad, - angle = symbol.angle; + var symbol = this.symbolQuadsArray.get(k).SymbolQuad; // drop upside down versions of glyphs - var a = (angle + placementAngle + Math.PI) % (Math.PI * 2); - if (keepUpright && alongLine && symbol.curved && (a <= Math.PI / 2 || a > Math.PI * 3 / 2)) continue; + var a = (symbol.anchorAngle + placementAngle + Math.PI) % (Math.PI * 2); + if (keepUpright && alongLine && (a <= Math.PI / 2 || a > Math.PI * 3 / 2)) continue; var tl = symbol.tl, tr = symbol.tr, @@ -484,13 +483,13 @@ SymbolBucket.prototype.addSymbols = function(programName, quadsStart, quadsEnd, // Lower min zoom so that while fading out the label it can be shown outside of collision-free zoom levels if (minZoom === placementZoom) minZoom = 0; - // Encode angle of line together with symbol.alongLine - var placementAngleLine = symbol.curved ? 0 : 1 + Math.round(((angle%Math.PI)/Math.PI)*255); + // Encode angle of glyph + var glyphAngle = Math.round((symbol.glyphAngle / (Math.PI * 2)) * 256); - var index = addVertex(vertexArray, anchorPoint.x, anchorPoint.y, tl.x, tl.y, tex.x, tex.y, minZoom, maxZoom, placementZoom, placementAngleLine); - addVertex(vertexArray, anchorPoint.x, anchorPoint.y, tr.x, tr.y, tex.x + tex.w, tex.y, minZoom, maxZoom, placementZoom, placementAngleLine); - addVertex(vertexArray, anchorPoint.x, anchorPoint.y, bl.x, bl.y, tex.x, tex.y + tex.h, minZoom, maxZoom, placementZoom, placementAngleLine); - addVertex(vertexArray, anchorPoint.x, anchorPoint.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h, minZoom, maxZoom, placementZoom, placementAngleLine); + var index = addVertex(vertexArray, anchorPoint.x, anchorPoint.y, tl.x, tl.y, tex.x, tex.y, minZoom, maxZoom, placementZoom, glyphAngle); + addVertex(vertexArray, anchorPoint.x, anchorPoint.y, tr.x, tr.y, tex.x + tex.w, tex.y, minZoom, maxZoom, placementZoom, glyphAngle); + addVertex(vertexArray, anchorPoint.x, anchorPoint.y, bl.x, bl.y, tex.x, tex.y + tex.h, minZoom, maxZoom, placementZoom, glyphAngle); + addVertex(vertexArray, anchorPoint.x, anchorPoint.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h, minZoom, maxZoom, placementZoom, glyphAngle); elementArray.emplaceBack(index, index + 1, index + 2); elementArray.emplaceBack(index + 1, index + 2, index + 3); @@ -627,7 +626,8 @@ SymbolBucket.prototype.addSymbolQuad = function(symbolQuad) { symbolQuad.tex.x, symbolQuad.tex.y, //angle - symbolQuad.angle, + symbolQuad.anchorAngle, + symbolQuad.glyphAngle, // scales symbolQuad.maxScale, symbolQuad.minScale); diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index 51fe6b853d4..271e2097fbb 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -34,6 +34,9 @@ function drawSymbols(painter, source, layer, coords) { layer.paint['icon-translate'], layer.paint['icon-translate-anchor'], layer.layout['icon-rotation-alignment'], + // icon-pitch-alignment is not yet implemented + // and we simply inherit the rotation alignment + layer.layout['icon-rotation-alignment'], layer.layout['icon-size'], layer.paint['icon-halo-width'], layer.paint['icon-halo-color'], @@ -45,6 +48,7 @@ function drawSymbols(painter, source, layer, coords) { layer.paint['text-translate'], layer.paint['text-translate-anchor'], layer.layout['text-rotation-alignment'], + layer.layout['text-pitch-alignment'], layer.layout['text-size'], layer.paint['text-halo-width'], layer.paint['text-halo-color'], @@ -61,6 +65,7 @@ function drawLayerSymbols(painter, source, layer, coords, isText, translate, translateAnchor, rotationAlignment, + pitchAlignment, size, haloWidth, haloColor, @@ -83,6 +88,7 @@ function drawLayerSymbols(painter, source, layer, coords, isText, translate, translateAnchor, rotationAlignment, + pitchAlignment, size, haloWidth, haloColor, @@ -96,39 +102,41 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, bufferGroups, isTex translate, translateAnchor, rotationAlignment, + pitchAlignment, size, haloWidth, haloColor, haloBlur, opacity, color) { + var gl = painter.gl; var tr = painter.transform; - var alignedWithMap = rotationAlignment === 'map'; + var rotateWithMap = rotationAlignment === 'map'; + var pitchWithMap = pitchAlignment === 'map'; var defaultSize = isText ? 24 : 1; var fontScale = size / defaultSize; - var s1 = painter.transform.altitude * fontScale; - var s2 = pixelsToTileUnits(tile, 1, painter.transform.zoom) * fontScale; - var gammaScale = 1; // 1 / Math.cos(tr._pitch); - var extrudeScale = [ tr.pixelsToGLUnits[0] * s1, tr.pixelsToGLUnits[1] * s1]; - var extrudeScaleSkewed = [s2, s2]; + var extrudeScale, s, gammaScale; + if (pitchWithMap) { + s = pixelsToTileUnits(tile, 1, painter.transform.zoom) * fontScale; + gammaScale = 1 / Math.cos(tr._pitch); + extrudeScale = [s, s]; + } else { + s = painter.transform.altitude * fontScale; + gammaScale = 1; + extrudeScale = [ tr.pixelsToGLUnits[0] * s, tr.pixelsToGLUnits[1] * s]; + } if (!isText && !painter.style.sprite.loaded()) return; - var program; - if (sdf && alignedWithMap) { - program = painter.useProgram('sdf'); - } else if (sdf) { - program = painter.useProgram('sdfviewport'); - } else { - program = painter.useProgram('icon'); - } + var program = painter.useProgram(sdf ? 'sdf' : 'icon'); gl.uniformMatrix4fv(program.u_matrix, false, painter.translatePosMatrix(posMatrix, tile, translate, translateAnchor)); + gl.uniform1i(program.u_rotate_with_map, rotateWithMap); + gl.uniform1i(program.u_pitch_with_map, pitchWithMap); gl.uniform2fv(program.u_extrude_scale, extrudeScale); - gl.uniform2fv(program.u_extrude_scale_skewed, extrudeScaleSkewed); gl.activeTexture(gl.TEXTURE0); gl.uniform1i(program.u_texture, 0); @@ -144,7 +152,7 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, bufferGroups, isTex } else { var mapMoving = painter.options.rotating || painter.options.zooming; var iconScaled = fontScale !== 1 || browser.devicePixelRatio !== painter.spriteAtlas.pixelRatio || iconsNeedLinear; - var iconTransformed = alignedWithMap || painter.transform.pitch; + var iconTransformed = pitchWithMap || painter.transform.pitch; painter.spriteAtlas.bind(gl, sdf || mapMoving || iconScaled || iconTransformed); gl.uniform2f(program.u_texsize, painter.spriteAtlas.width / 4, painter.spriteAtlas.height / 4); } @@ -184,6 +192,7 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, bufferGroups, isTex gl.uniform1f(program.u_opacity, opacity); gl.uniform1f(program.u_buffer, (256 - 64) / 256); gl.uniform1f(program.u_pitch, tr.pitch / 360 * 2 * Math.PI); + gl.uniform1f(program.u_bearing, tr.bearing / 360 * 2 * Math.PI); gl.uniform1f(program.u_aspect_ratio, tr.width / tr.height); for (var i = 0; i < bufferGroups.length; i++) { diff --git a/js/symbol/quads.js b/js/symbol/quads.js index 235509bff05..a664991db97 100644 --- a/js/symbol/quads.js +++ b/js/symbol/quads.js @@ -10,10 +10,6 @@ module.exports = { var minScale = 0.5; // underscale by 1 zoom level -// Characters along a line must be placed within 10 degree threshold to be -// straight enough for linear placement. -var LINEAR_THRESHOLD = 10 / 360 * (Math.PI*2); - /** * A textured quad for rendering a single icon or glyph. * @@ -25,25 +21,25 @@ var LINEAR_THRESHOLD = 10 / 360 * (Math.PI*2); * @param {Point} bl The offset of the bottom left corner from the anchor. * @param {Point} br The offset of the bottom right corner from the anchor. * @param {Object} tex The texture coordinates. - * @param {number} angle The angle of the label at it's center, not the angle of this quad. + * @param {number} anchorAngle The angle of the label at it's center, not the angle of this quad. + * @param {number} glyphAngle The angle of the glyph to be positioned in the quad. * @param {number} minScale The minimum scale, relative to the tile's intended scale, that the glyph can be shown at. * @param {number} maxScale The maximum scale, relative to the tile's intended scale, that the glyph can be shown at. - * @param {boolean} curved Whether this quad was generated for display along a curved line. * * @class SymbolQuad * @private */ -function SymbolQuad(anchorPoint, tl, tr, bl, br, tex, angle, minScale, maxScale, curved) { +function SymbolQuad(anchorPoint, tl, tr, bl, br, tex, anchorAngle, glyphAngle, minScale, maxScale) { this.anchorPoint = anchorPoint; this.tl = tl; this.tr = tr; this.bl = bl; this.br = br; this.tex = tex; - this.angle = angle; + this.anchorAngle = anchorAngle; + this.glyphAngle = glyphAngle; this.minScale = minScale; this.maxScale = maxScale; - this.curved = curved; } /** @@ -94,7 +90,7 @@ function getIconQuads(anchor, shapedIcon, boxScale, line, layout, alongLine) { br = br.matMult(matrix); } - return [new SymbolQuad(new Point(anchor.x, anchor.y), tl, tr, bl, br, shapedIcon.image.rect, 0, minScale, Infinity)]; + return [new SymbolQuad(new Point(anchor.x, anchor.y), tl, tr, bl, br, shapedIcon.image.rect, 0, 0, minScale, Infinity)]; } /** @@ -115,70 +111,35 @@ function getGlyphQuads(anchor, shaping, boxScale, line, layout, alongLine) { var keepUpright = layout['text-keep-upright']; var positionedGlyphs = shaping.positionedGlyphs; - var segmentedGlyphs = []; var quads = []; - var angleMin = Infinity; - var angleMax = -Infinity; - - // First iteration, generate all curved and linear glyphs for (var k = 0; k < positionedGlyphs.length; k++) { - if (!positionedGlyphs[k].glyph.rect) continue; - var positionedGlyph = positionedGlyphs[k]; var glyph = positionedGlyph.glyph; var rect = glyph.rect; + if (!rect) continue; + var centerX = (positionedGlyph.x + glyph.advance / 2) * boxScale; - var curved = []; - var linear = []; + var glyphInstances; var labelMinScale = minScale; if (alongLine) { - labelMinScale = getSegmentGlyphs(curved, anchor, centerX, line, anchor.segment, true); - if (positionedGlyph.codePoint > 32) { - for (var i = 0; i < curved.length; i++) { - if (curved[i].minScale < 1.0) continue; - angleMin = Math.min(curved[i].angle, angleMin); - angleMax = Math.max(curved[i].angle, angleMax); - } - } + glyphInstances = []; + labelMinScale = getSegmentGlyphs(glyphInstances, anchor, centerX, line, anchor.segment, true); if (keepUpright) { - labelMinScale = Math.min(labelMinScale, getSegmentGlyphs(curved, anchor, centerX, line, anchor.segment, false)); + labelMinScale = Math.min(labelMinScale, getSegmentGlyphs(glyphInstances, anchor, centerX, line, anchor.segment, false)); } - } - - linear.push({ - anchorPoint: new Point(anchor.x, anchor.y), - offset: 0, - angle: 0, - maxScale: Infinity, - minScale: minScale, - alongLine: false - }); - - segmentedGlyphs[k] = { - curved: curved, - linear: linear, - labelMinScale: labelMinScale - }; - } - // Second iteration, determine which glyph placements to use - for (var k = 0; k < positionedGlyphs.length; k++) { - if (!positionedGlyphs[k].glyph.rect) continue; - - var positionedGlyph = positionedGlyphs[k]; - var glyph = positionedGlyph.glyph; - var rect = glyph.rect; - - var labelMinScale = segmentedGlyphs[k].labelMinScale; - - // Find the angle between min and max angles of positioned glyph - // characters as a cheap way to determine whether a label's characters - // are largely placed in a single linear line. - var curved = alongLine && ((angleMax-angleMin) >= LINEAR_THRESHOLD); - var glyphInstances = curved ? segmentedGlyphs[k].curved : segmentedGlyphs[k].linear; + } else { + glyphInstances = [{ + anchorPoint: new Point(anchor.x, anchor.y), + offset: 0, + angle: 0, + maxScale: Infinity, + minScale: minScale + }]; + } var x1 = positionedGlyph.x + glyph.left, y1 = positionedGlyph.y - glyph.top, @@ -196,12 +157,11 @@ function getGlyphQuads(anchor, shaping, boxScale, line, layout, alongLine) { tl = otl, tr = otr, bl = obl, - br = obr, - angle = instance.angle + textRotate; + br = obr; - if (angle) { - var sin = Math.sin(angle), - cos = Math.cos(angle), + if (textRotate) { + var sin = Math.sin(textRotate), + cos = Math.cos(textRotate), matrix = [cos, -sin, sin, cos]; tl = tl.matMult(matrix); @@ -213,8 +173,9 @@ function getGlyphQuads(anchor, shaping, boxScale, line, layout, alongLine) { // Prevent label from extending past the end of the line var glyphMinScale = Math.max(instance.minScale, labelMinScale); - var glyphAngle = (anchor.angle + textRotate + instance.offset + 2 * Math.PI) % (2 * Math.PI); - quads.push(new SymbolQuad(instance.anchorPoint, tl, tr, bl, br, rect, glyphAngle, glyphMinScale, instance.maxScale, curved)); + var anchorAngle = (anchor.angle + textRotate + instance.offset + 2 * Math.PI) % (2 * Math.PI); + var glyphAngle = (instance.angle + textRotate + instance.offset + 2 * Math.PI) % (2 * Math.PI); + quads.push(new SymbolQuad(instance.anchorPoint, tl, tr, bl, br, rect, anchorAngle, glyphAngle, glyphMinScale, instance.maxScale)); } } @@ -259,7 +220,6 @@ function getSegmentGlyphs(glyphs, anchor, offset, line, segment, forward) { // Get the angle of the line segment var angle = Math.atan2(end.y - newAnchorPoint.y, end.x - newAnchorPoint.x); if (!forward) angle += Math.PI; - if (upsideDown) angle += Math.PI; glyphs.push({ anchorPoint: newAnchorPoint, diff --git a/js/symbol/symbol_quads.js b/js/symbol/symbol_quads.js index 4b06f1bfae5..001260d0c81 100644 --- a/js/symbol/symbol_quads.js +++ b/js/symbol/symbol_quads.js @@ -41,8 +41,10 @@ var SymbolQuadsArray = module.exports = new StructArrayType({ { type: 'Int16', name: 'texX' }, { type: 'Int16', name: 'texY' }, - //the angle of the label at it's center, not the angle of this quad. - { type: 'Float32', name: 'angle' }, + // the angle of the label at it's center, not the angle of this quad. + { type: 'Float32', name: 'anchorAngle' }, + // the angle of this quad. + { type: 'Float32', name: 'glyphAngle' }, // quad is only valid for scales < maxScale && scale > minScale. { type: 'Float32', name: 'maxScale' }, @@ -61,7 +63,8 @@ util.extendAll(SymbolQuadsArray.prototype.StructType.prototype, { new Point(this.blX, this.blY), new Point(this.brX, this.brY), { x: this.texX, y: this.texY, h: this.texH, w: this.texW, height: this.texH, width: this.texW }, - this.angle, + this.anchorAngle, + this.glyphAngle, this.minScale, this.maxScale); } diff --git a/package.json b/package.json index 4268a113fdb..992874691c7 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,8 @@ "grid-index": "^0.1.0", "mapbox-gl-function": "^1.2.1", "mapbox-gl-js-supported": "^1.1.0", - "mapbox-gl-shaders": "mapbox/mapbox-gl-shaders#7467fd7916186e1c7201188d85ebdd5e81c3678e", - "mapbox-gl-style-spec": "^8.7.0", + "mapbox-gl-shaders": "mapbox/mapbox-gl-shaders#adcc18ad79b363ff17443d746d26c2ecee6bcb5e", + "mapbox-gl-style-spec": "mapbox/mapbox-gl-style-spec#586dbf48f7ebd79ed623c5ad809a564013173acf", "minifyify": "^7.0.1", "pbf": "^1.3.2", "pngjs": "^2.2.0", @@ -57,7 +57,7 @@ "highlight.js": "9.3.0", "istanbul": "^0.4.2", "lodash": "^4.13.1", - "mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#6d3bcff5d51f6acea41230ffabad6bebdea49fa3", + "mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#fd5e956e948fb430d1b56659c6bd93d3d27b8962", "nyc": "^6.1.1", "remark": "4.2.2", "remark-html": "3.0.0", diff --git a/test/js/symbol/quads.test.js b/test/js/symbol/quads.test.js index 1831f617cfa..32df909791d 100644 --- a/test/js/symbol/quads.test.js +++ b/test/js/symbol/quads.test.js @@ -29,7 +29,8 @@ test('getIconQuads', function(t) { bl: { x: -8, y: 5 }, br: { x: 7, y: 5 }, tex: { w: 15, h: 11 }, - angle: 0, + anchorAngle: 0, + glyphAngle: 0, minScale: 0.5, maxScale: Infinity } ]); t.end(); @@ -45,7 +46,8 @@ test('getIconQuads', function(t) { bl: { x: -8, y: 5 }, br: { x: 7, y: 5 }, tex: { w: 15, h: 11 }, - angle: 0, + anchorAngle: 0, + glyphAngle: 0, minScale: 0.5, maxScale: Infinity }]); t.end(); From c028af4a29ddd65e79148709be708aa1f98fd5d6 Mon Sep 17 00:00:00 2001 From: Young Hahn Date: Fri, 10 Jun 2016 09:54:18 -0400 Subject: [PATCH 3/4] Inherit default value of text-pitch-alignment from text-rotation-alignment. --- js/style/style_layer/symbol_style_layer.js | 3 ++ package.json | 2 +- test/js/style/style_layer.test.js | 60 ++++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/js/style/style_layer/symbol_style_layer.js b/js/style/style_layer/symbol_style_layer.js index 3ebf4412d48..4d1ba0c9c46 100644 --- a/js/style/style_layer/symbol_style_layer.js +++ b/js/style/style_layer/symbol_style_layer.js @@ -30,6 +30,9 @@ SymbolStyleLayer.prototype = util.inherit(StyleLayer, { this.getLayoutValue('symbol-placement', globalProperties, featureProperties) === 'line' && !this.getLayoutProperty('icon-rotation-alignment')) { return 'map'; + // If unspecified `text-pitch-alignment` inherits `text-rotation-alignment` + } else if (name === 'text-pitch-alignment' && !this.getLayoutProperty('text-pitch-alignment')) { + return this.getLayoutValue('text-rotation-alignment'); } else { return StyleLayer.prototype.getLayoutValue.apply(this, arguments); } diff --git a/package.json b/package.json index 992874691c7..d7f986757f3 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "mapbox-gl-function": "^1.2.1", "mapbox-gl-js-supported": "^1.1.0", "mapbox-gl-shaders": "mapbox/mapbox-gl-shaders#adcc18ad79b363ff17443d746d26c2ecee6bcb5e", - "mapbox-gl-style-spec": "mapbox/mapbox-gl-style-spec#586dbf48f7ebd79ed623c5ad809a564013173acf", + "mapbox-gl-style-spec": "mapbox/mapbox-gl-style-spec#2461efc3d883f2f2e56a6c6b2bfd7d54bbfe9f86", "minifyify": "^7.0.1", "pbf": "^1.3.2", "pngjs": "^2.2.0", diff --git a/test/js/style/style_layer.test.js b/test/js/style/style_layer.test.js index f745550042f..d22bd8ca593 100644 --- a/test/js/style/style_layer.test.js +++ b/test/js/style/style_layer.test.js @@ -539,6 +539,66 @@ test('StyleLayer#serialize', function(t) { t.end(); }); +test('StyleLayer#getLayoutValue (default exceptions)', function(assert) { + assert.test('symbol-placement:point => *-rotation-alignment:viewport', function(assert) { + var layer = StyleLayer.create({ + "type": "symbol", + "layout": { + "symbol-placement": "point" + } + }); + assert.equal(layer.getLayoutValue('text-rotation-alignment'), 'viewport'); + assert.equal(layer.getLayoutValue('icon-rotation-alignment'), 'viewport'); + assert.end(); + }); + assert.test('symbol-placement:line => *-rotation-alignment:map', function(assert) { + var layer = StyleLayer.create({ + "type": "symbol", + "layout": { + "symbol-placement": "line" + } + }); + assert.equal(layer.getLayoutValue('text-rotation-alignment'), 'map'); + assert.equal(layer.getLayoutValue('icon-rotation-alignment'), 'map'); + assert.end(); + }); + assert.test('text-rotation-alignment:map => text-pitch-alignment:map', function(assert) { + var layer = StyleLayer.create({ + "type": "symbol", + "layout": { + "text-rotation-alignment": "map" + } + }); + assert.equal(layer.getLayoutValue('text-rotation-alignment'), 'map'); + assert.equal(layer.getLayoutValue('text-pitch-alignment'), 'map'); + assert.end(); + }); + assert.test('text-rotation-alignment:viewport => text-pitch-alignment:viewport', function(assert) { + var layer = StyleLayer.create({ + "type": "symbol", + "layout": { + "text-rotation-alignment": "viewport" + } + }); + assert.equal(layer.getLayoutValue('text-rotation-alignment'), 'viewport'); + assert.equal(layer.getLayoutValue('text-pitch-alignment'), 'viewport'); + assert.end(); + }); + assert.test('text-pitch-alignment respected when set', function(assert) { + var layer = StyleLayer.create({ + "type": "symbol", + "layout": { + "text-rotation-alignment": "viewport", + "text-pitch-alignment": "map" + } + }); + assert.equal(layer.getLayoutValue('text-rotation-alignment'), 'viewport'); + assert.equal(layer.getLayoutValue('text-pitch-alignment'), 'map'); + assert.end(); + }); + assert.end(); +}); + function createAnimationLoop() { return { set: function() {}, From ad3fe1cfb3b2acb352635e6085c506d39df0c4f4 Mon Sep 17 00:00:00 2001 From: Young Hahn Date: Fri, 10 Jun 2016 12:55:40 -0400 Subject: [PATCH 4/4] Update dependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d7f986757f3..7a26fdd242e 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "grid-index": "^0.1.0", "mapbox-gl-function": "^1.2.1", "mapbox-gl-js-supported": "^1.1.0", - "mapbox-gl-shaders": "mapbox/mapbox-gl-shaders#adcc18ad79b363ff17443d746d26c2ecee6bcb5e", + "mapbox-gl-shaders": "mapbox/mapbox-gl-shaders#30caf388dbddd02cfc4f967ffc94c1338c30fbf8", "mapbox-gl-style-spec": "mapbox/mapbox-gl-style-spec#2461efc3d883f2f2e56a6c6b2bfd7d54bbfe9f86", "minifyify": "^7.0.1", "pbf": "^1.3.2", @@ -57,7 +57,7 @@ "highlight.js": "9.3.0", "istanbul": "^0.4.2", "lodash": "^4.13.1", - "mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#fd5e956e948fb430d1b56659c6bd93d3d27b8962", + "mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#5587d796c99145991ea2a7b749a8782b7a0cb483", "nyc": "^6.1.1", "remark": "4.2.2", "remark-html": "3.0.0",