From e24bc4b1733584f09c88f5789541562c8f61b469 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Fri, 13 Sep 2019 18:16:56 -0700 Subject: [PATCH 01/11] initial icon offset syncing implementation --- debug/8583.html | 133 +++++++++++++++++++++++++++ src/data/array_types.js | 107 +++++++++++---------- src/data/bucket/symbol_attributes.js | 4 +- src/data/bucket/symbol_bucket.js | 7 +- src/render/draw_symbol.js | 48 ++++++++-- src/symbol/symbol_layout.js | 86 +++++++++-------- 6 files changed, 289 insertions(+), 96 deletions(-) create mode 100644 debug/8583.html diff --git a/debug/8583.html b/debug/8583.html new file mode 100644 index 00000000000..d4dede550d6 --- /dev/null +++ b/debug/8583.html @@ -0,0 +1,133 @@ + + + + + Mapbox GL JS debug page + + + + + + + +
+ + + + + + \ No newline at end of file diff --git a/src/data/array_types.js b/src/data/array_types.js index 31138acba54..b5114e89d7a 100644 --- a/src/data/array_types.js +++ b/src/data/array_types.js @@ -422,10 +422,11 @@ register('StructArrayLayout2ub2f12', StructArrayLayout2ub2f12); * [28]: Float32[2] * [36]: Uint8[3] * [40]: Uint32[1] + * [44]: Int16[1] * * @private */ -class StructArrayLayout2i2ui3ul3ui2f3ub1ul44 extends StructArray { +class StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48 extends StructArray { uint8: Uint8Array; int16: Int16Array; uint16: Uint16Array; @@ -440,16 +441,16 @@ class StructArrayLayout2i2ui3ul3ui2f3ub1ul44 extends StructArray { this.float32 = new Float32Array(this.arrayBuffer); } - emplaceBack(v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number) { + emplaceBack(v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, v16: number) { const i = this.length; this.resize(i + 1); - return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15); + return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16); } - emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number) { - const o2 = i * 22; - const o4 = i * 11; - const o1 = i * 44; + emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, v16: number) { + const o2 = i * 24; + const o4 = i * 12; + const o1 = i * 48; this.int16[o2 + 0] = v0; this.int16[o2 + 1] = v1; this.uint16[o2 + 2] = v2; @@ -466,23 +467,24 @@ class StructArrayLayout2i2ui3ul3ui2f3ub1ul44 extends StructArray { this.uint8[o1 + 37] = v13; this.uint8[o1 + 38] = v14; this.uint32[o4 + 10] = v15; + this.int16[o2 + 22] = v16; return i; } } -StructArrayLayout2i2ui3ul3ui2f3ub1ul44.prototype.bytesPerElement = 44; -register('StructArrayLayout2i2ui3ul3ui2f3ub1ul44', StructArrayLayout2i2ui3ul3ui2f3ub1ul44); +StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48.prototype.bytesPerElement = 48; +register('StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48', StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48); /** * Implementation of the StructArray layout: - * [0]: Int16[6] - * [12]: Uint16[11] + * [0]: Int16[7] + * [14]: Uint16[11] * [36]: Uint32[1] * [40]: Float32[3] * * @private */ -class StructArrayLayout6i11ui1ul3f52 extends StructArray { +class StructArrayLayout7i11ui1ul3f52 extends StructArray { uint8: Uint8Array; int16: Int16Array; uint16: Uint16Array; @@ -497,13 +499,13 @@ class StructArrayLayout6i11ui1ul3f52 extends StructArray { this.float32 = new Float32Array(this.arrayBuffer); } - emplaceBack(v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, v16: number, v17: number, v18: number, v19: number, v20: number) { + emplaceBack(v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, v16: number, v17: number, v18: number, v19: number, v20: number, v21: number) { const i = this.length; this.resize(i + 1); - return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20); + return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21); } - emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, v16: number, v17: number, v18: number, v19: number, v20: number) { + emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, v16: number, v17: number, v18: number, v19: number, v20: number, v21: number) { const o2 = i * 26; const o4 = i * 13; this.int16[o2 + 0] = v0; @@ -512,7 +514,7 @@ class StructArrayLayout6i11ui1ul3f52 extends StructArray { this.int16[o2 + 3] = v3; this.int16[o2 + 4] = v4; this.int16[o2 + 5] = v5; - this.uint16[o2 + 6] = v6; + this.int16[o2 + 6] = v6; this.uint16[o2 + 7] = v7; this.uint16[o2 + 8] = v8; this.uint16[o2 + 9] = v9; @@ -523,16 +525,17 @@ class StructArrayLayout6i11ui1ul3f52 extends StructArray { this.uint16[o2 + 14] = v14; this.uint16[o2 + 15] = v15; this.uint16[o2 + 16] = v16; - this.uint32[o4 + 9] = v17; - this.float32[o4 + 10] = v18; - this.float32[o4 + 11] = v19; - this.float32[o4 + 12] = v20; + this.uint16[o2 + 17] = v17; + this.uint32[o4 + 9] = v18; + this.float32[o4 + 10] = v19; + this.float32[o4 + 11] = v20; + this.float32[o4 + 12] = v21; return i; } } -StructArrayLayout6i11ui1ul3f52.prototype.bytesPerElement = 52; -register('StructArrayLayout6i11ui1ul3f52', StructArrayLayout6i11ui1ul3f52); +StructArrayLayout7i11ui1ul3f52.prototype.bytesPerElement = 52; +register('StructArrayLayout7i11ui1ul3f52', StructArrayLayout7i11ui1ul3f52); /** * Implementation of the StructArray layout: @@ -874,6 +877,7 @@ class PlacedSymbolStruct extends Struct { placedOrientation: number; hidden: number; crossTileID: number; + associatedIconIndex: number; get anchorX() { return this._structArray.int16[this._pos2 + 0]; } set anchorX(x: number) { this._structArray.int16[this._pos2 + 0] = x; } get anchorY() { return this._structArray.int16[this._pos2 + 1]; } @@ -906,16 +910,18 @@ class PlacedSymbolStruct extends Struct { set hidden(x: number) { this._structArray.uint8[this._pos1 + 38] = x; } get crossTileID() { return this._structArray.uint32[this._pos4 + 10]; } set crossTileID(x: number) { this._structArray.uint32[this._pos4 + 10] = x; } + get associatedIconIndex() { return this._structArray.int16[this._pos2 + 22]; } + set associatedIconIndex(x: number) { this._structArray.int16[this._pos2 + 22] = x; } } -PlacedSymbolStruct.prototype.size = 44; +PlacedSymbolStruct.prototype.size = 48; export type PlacedSymbol = PlacedSymbolStruct; /** * @private */ -export class PlacedSymbolArray extends StructArrayLayout2i2ui3ul3ui2f3ub1ul44 { +export class PlacedSymbolArray extends StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48 { /** * Return the PlacedSymbolStruct at the given location in the array. * @param {number} index The index of the element. @@ -936,6 +942,7 @@ class SymbolInstanceStruct extends Struct { centerJustifiedTextSymbolIndex: number; leftJustifiedTextSymbolIndex: number; verticalPlacedTextSymbolIndex: number; + placedIconSymbolIndex: number; key: number; textBoxStartIndex: number; textBoxEndIndex: number; @@ -963,28 +970,30 @@ class SymbolInstanceStruct extends Struct { set leftJustifiedTextSymbolIndex(x: number) { this._structArray.int16[this._pos2 + 4] = x; } get verticalPlacedTextSymbolIndex() { return this._structArray.int16[this._pos2 + 5]; } set verticalPlacedTextSymbolIndex(x: number) { this._structArray.int16[this._pos2 + 5] = x; } - get key() { return this._structArray.uint16[this._pos2 + 6]; } - set key(x: number) { this._structArray.uint16[this._pos2 + 6] = x; } - get textBoxStartIndex() { return this._structArray.uint16[this._pos2 + 7]; } - set textBoxStartIndex(x: number) { this._structArray.uint16[this._pos2 + 7] = x; } - get textBoxEndIndex() { return this._structArray.uint16[this._pos2 + 8]; } - set textBoxEndIndex(x: number) { this._structArray.uint16[this._pos2 + 8] = x; } - get verticalTextBoxStartIndex() { return this._structArray.uint16[this._pos2 + 9]; } - set verticalTextBoxStartIndex(x: number) { this._structArray.uint16[this._pos2 + 9] = x; } - get verticalTextBoxEndIndex() { return this._structArray.uint16[this._pos2 + 10]; } - set verticalTextBoxEndIndex(x: number) { this._structArray.uint16[this._pos2 + 10] = x; } - get iconBoxStartIndex() { return this._structArray.uint16[this._pos2 + 11]; } - set iconBoxStartIndex(x: number) { this._structArray.uint16[this._pos2 + 11] = x; } - get iconBoxEndIndex() { return this._structArray.uint16[this._pos2 + 12]; } - set iconBoxEndIndex(x: number) { this._structArray.uint16[this._pos2 + 12] = x; } - get featureIndex() { return this._structArray.uint16[this._pos2 + 13]; } - set featureIndex(x: number) { this._structArray.uint16[this._pos2 + 13] = x; } - get numHorizontalGlyphVertices() { return this._structArray.uint16[this._pos2 + 14]; } - set numHorizontalGlyphVertices(x: number) { this._structArray.uint16[this._pos2 + 14] = x; } - get numVerticalGlyphVertices() { return this._structArray.uint16[this._pos2 + 15]; } - set numVerticalGlyphVertices(x: number) { this._structArray.uint16[this._pos2 + 15] = x; } - get numIconVertices() { return this._structArray.uint16[this._pos2 + 16]; } - set numIconVertices(x: number) { this._structArray.uint16[this._pos2 + 16] = x; } + get placedIconSymbolIndex() { return this._structArray.int16[this._pos2 + 6]; } + set placedIconSymbolIndex(x: number) { this._structArray.int16[this._pos2 + 6] = x; } + get key() { return this._structArray.uint16[this._pos2 + 7]; } + set key(x: number) { this._structArray.uint16[this._pos2 + 7] = x; } + get textBoxStartIndex() { return this._structArray.uint16[this._pos2 + 8]; } + set textBoxStartIndex(x: number) { this._structArray.uint16[this._pos2 + 8] = x; } + get textBoxEndIndex() { return this._structArray.uint16[this._pos2 + 9]; } + set textBoxEndIndex(x: number) { this._structArray.uint16[this._pos2 + 9] = x; } + get verticalTextBoxStartIndex() { return this._structArray.uint16[this._pos2 + 10]; } + set verticalTextBoxStartIndex(x: number) { this._structArray.uint16[this._pos2 + 10] = x; } + get verticalTextBoxEndIndex() { return this._structArray.uint16[this._pos2 + 11]; } + set verticalTextBoxEndIndex(x: number) { this._structArray.uint16[this._pos2 + 11] = x; } + get iconBoxStartIndex() { return this._structArray.uint16[this._pos2 + 12]; } + set iconBoxStartIndex(x: number) { this._structArray.uint16[this._pos2 + 12] = x; } + get iconBoxEndIndex() { return this._structArray.uint16[this._pos2 + 13]; } + set iconBoxEndIndex(x: number) { this._structArray.uint16[this._pos2 + 13] = x; } + get featureIndex() { return this._structArray.uint16[this._pos2 + 14]; } + set featureIndex(x: number) { this._structArray.uint16[this._pos2 + 14] = x; } + get numHorizontalGlyphVertices() { return this._structArray.uint16[this._pos2 + 15]; } + set numHorizontalGlyphVertices(x: number) { this._structArray.uint16[this._pos2 + 15] = x; } + get numVerticalGlyphVertices() { return this._structArray.uint16[this._pos2 + 16]; } + set numVerticalGlyphVertices(x: number) { this._structArray.uint16[this._pos2 + 16] = x; } + get numIconVertices() { return this._structArray.uint16[this._pos2 + 17]; } + set numIconVertices(x: number) { this._structArray.uint16[this._pos2 + 17] = x; } get crossTileID() { return this._structArray.uint32[this._pos4 + 9]; } set crossTileID(x: number) { this._structArray.uint32[this._pos4 + 9] = x; } get textBoxScale() { return this._structArray.float32[this._pos4 + 10]; } @@ -1002,7 +1011,7 @@ export type SymbolInstance = SymbolInstanceStruct; /** * @private */ -export class SymbolInstanceArray extends StructArrayLayout6i11ui1ul3f52 { +export class SymbolInstanceArray extends StructArrayLayout7i11ui1ul3f52 { /** * Return the SymbolInstanceStruct at the given location in the array. * @param {number} index The index of the element. @@ -1124,8 +1133,8 @@ export { StructArrayLayout6i1ul2ui2i24, StructArrayLayout2i2i2i12, StructArrayLayout2ub2f12, - StructArrayLayout2i2ui3ul3ui2f3ub1ul44, - StructArrayLayout6i11ui1ul3f52, + StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48, + StructArrayLayout7i11ui1ul3f52, StructArrayLayout1f4, StructArrayLayout3i6, StructArrayLayout1ul2ui8, diff --git a/src/data/bucket/symbol_attributes.js b/src/data/bucket/symbol_attributes.js index bf78d74a098..d3384f3cb24 100644 --- a/src/data/bucket/symbol_attributes.js +++ b/src/data/bucket/symbol_attributes.js @@ -73,7 +73,8 @@ export const placement = createLayout([ {type: 'Uint8', name: 'writingMode'}, {type: 'Uint8', name: 'placedOrientation'}, {type: 'Uint8', name: 'hidden'}, - {type: 'Uint32', name: 'crossTileID'} + {type: 'Uint32', name: 'crossTileID'}, + {type: 'Int16', name: 'associatedIconIndex'} ]); export const symbolInstance = createLayout([ @@ -83,6 +84,7 @@ export const symbolInstance = createLayout([ {type: 'Int16', name: 'centerJustifiedTextSymbolIndex'}, {type: 'Int16', name: 'leftJustifiedTextSymbolIndex'}, {type: 'Int16', name: 'verticalPlacedTextSymbolIndex'}, + {type: 'Int16', name: 'placedIconSymbolIndex'}, {type: 'Uint16', name: 'key'}, {type: 'Uint16', name: 'textBoxStartIndex'}, {type: 'Uint16', name: 'textBoxEndIndex'}, diff --git a/src/data/bucket/symbol_bucket.js b/src/data/bucket/symbol_bucket.js index 91a7b0aff06..3c5655fb3f9 100644 --- a/src/data/bucket/symbol_bucket.js +++ b/src/data/bucket/symbol_bucket.js @@ -539,7 +539,8 @@ class SymbolBucket implements Bucket { writingMode: any, labelAnchor: Anchor, lineStartIndex: number, - lineLength: number) { + lineLength: number, + associatedIconIndex: number) { const indexArray = arrays.indexArray; const layoutVertexArray = arrays.layoutVertexArray; const dynamicLayoutVertexArray = arrays.dynamicLayoutVertexArray; @@ -619,7 +620,9 @@ class SymbolBucket implements Bucket { 0, (false: any), // The crossTileID is only filled/used on the foreground for dynamic text anchors - 0); + 0, + associatedIconIndex + ); } _addCollisionDebugVertex(layoutVertexArray: StructArray, collisionVertexArray: StructArray, point: Point, anchorX: number, anchorY: number, extrude: Point) { diff --git a/src/render/draw_symbol.js b/src/render/draw_symbol.js index e27937449a7..d662a46e0eb 100644 --- a/src/render/draw_symbol.js +++ b/src/render/draw_symbol.js @@ -98,18 +98,22 @@ function calculateVariableRenderShift(anchor, width, height, textOffset, textBox } function updateVariableAnchors(bucket, rotateWithMap, pitchWithMap, variableOffsets, symbolSize, - transform, labelPlaneMatrix, posMatrix, tileScale, size) { + transform, labelPlaneMatrix, posMatrix, tileScale, size, updateTextFitIcon) { const placedSymbols = bucket.text.placedSymbolArray; - const dynamicLayoutVertexArray = bucket.text.dynamicLayoutVertexArray; - dynamicLayoutVertexArray.clear(); + const dynamicTextLayoutVertexArray = bucket.text.dynamicLayoutVertexArray; + const dynamicIconLayoutVertexArray = bucket.icon.dynamicLayoutVertexArray; + const placedTextShifts = {}; + + dynamicTextLayoutVertexArray.clear(); for (let s = 0; s < placedSymbols.length; s++) { const symbol: any = placedSymbols.get(s); const skipOrientation = bucket.allowVerticalPlacement && !symbol.placedOrientation; const variableOffset = (!symbol.hidden && symbol.crossTileID && !skipOrientation) ? variableOffsets[symbol.crossTileID] : null; + if (!variableOffset) { // These symbols are from a justification that is not being used, or a label that wasn't placed // so we don't need to do the extra math to figure out what incremental shift to apply. - symbolProjection.hideGlyphs(symbol.numGlyphs, dynamicLayoutVertexArray); + symbolProjection.hideGlyphs(symbol.numGlyphs, dynamicTextLayoutVertexArray); } else { const tileAnchor = new Point(symbol.anchorX, symbol.anchorY); const projectedAnchor = symbolProjection.project(tileAnchor, pitchWithMap ? posMatrix : labelPlaneMatrix); @@ -136,11 +140,36 @@ function updateVariableAnchors(bucket, rotateWithMap, pitchWithMap, variableOffs const angle = (bucket.allowVerticalPlacement && symbol.placedOrientation === WritingMode.vertical) ? Math.PI / 2 : 0; for (let g = 0; g < symbol.numGlyphs; g++) { - addDynamicAttributes(dynamicLayoutVertexArray, shiftedAnchor, angle); + addDynamicAttributes(dynamicTextLayoutVertexArray, shiftedAnchor, angle); + } + if (updateTextFitIcon && symbol.associatedIconIndex >= 0) { + placedTextShifts[symbol.associatedIconIndex] = {index: s, shiftedAnchor}; } } } - bucket.text.dynamicLayoutVertexBuffer.updateData(dynamicLayoutVertexArray); + + if (updateTextFitIcon) { + dynamicIconLayoutVertexArray.clear(); + const placedIcons = bucket.icon.placedSymbolArray; + for (let i = 0; i < placedIcons.length; i++) { + const placedIcon = placedIcons.get(i); + if (placedIcon.hidden || (!placedIcon.placedOrientation && bucket.allowVerticalPlacement)) { + symbolProjection.hideGlyphs(placedIcon.numGlyphs, bucket.icon.dynamicLayoutVertexArray); + } else { + const placedShift = placedTextShifts[i]; + if (!placedShift) { + symbolProjection.hideGlyphs(placedIcon.numGlyphs, bucket.icon.dynamicLayoutVertexArray); + } else { + const {index, shiftedAnchor} = placedShift; + for (let g = 0; g < placedIcon.numGlyphs; g++) { + addDynamicAttributes(bucket.icon.dynamicLayoutVertexArray, shiftedAnchor, 0); + } + } + } + } + bucket.icon.dynamicLayoutVertexBuffer.updateData(dynamicIconLayoutVertexArray); + } + bucket.text.dynamicLayoutVertexBuffer.updateData(dynamicTextLayoutVertexArray); } function updateVerticalLabels(bucket) { @@ -233,12 +262,17 @@ function drawLayerSymbols(painter, sourceCache, layer, coords, isText, translate const labelPlaneMatrix = symbolProjection.getLabelPlaneMatrix(coord.posMatrix, pitchWithMap, rotateWithMap, painter.transform, s); const glCoordMatrix = symbolProjection.getGlCoordMatrix(coord.posMatrix, pitchWithMap, rotateWithMap, painter.transform, s); + const hasVariableAnchors = variablePlacement && bucket.hasTextData(); + const updateTextFitIcon = layer.layout.get('icon-text-fit') !== 'none' && + hasVariableAnchors && + bucket.hasIconData(); + if (alongLine) { symbolProjection.updateLineLabels(bucket, coord.posMatrix, painter, isText, labelPlaneMatrix, glCoordMatrix, pitchWithMap, keepUpright); } else if (isText && size && variablePlacement) { const tileScale = Math.pow(2, tr.zoom - tile.tileID.overscaledZ); updateVariableAnchors(bucket, rotateWithMap, pitchWithMap, variableOffsets, symbolSize, - tr, labelPlaneMatrix, coord.posMatrix, tileScale, size); + tr, labelPlaneMatrix, coord.posMatrix, tileScale, size, updateTextFitIcon); } else if (isText && size && bucket.allowVerticalPlacement) { updateVerticalLabels(bucket); } diff --git a/src/symbol/symbol_layout.js b/src/symbol/symbol_layout.js index cc1b44bdd99..a0169ce66ed 100644 --- a/src/symbol/symbol_layout.js +++ b/src/symbol/symbol_layout.js @@ -462,6 +462,7 @@ function addTextVertices(bucket: SymbolBucket, placementTypes: Array<'vertical' | 'center' | 'left' | 'right'>, placedTextSymbolIndices: {[string]: number}, glyphPositionMap: {[string]: {[number]: GlyphPosition}}, + placedIconIndex: number, sizes: Sizes) { const glyphQuads = getGlyphQuads(anchor, shapedText, textOffset, layer, textAlongLine, feature, glyphPositionMap, bucket.allowVerticalPlacement); @@ -496,7 +497,8 @@ function addTextVertices(bucket: SymbolBucket, writingMode, anchor, lineArray.lineStartIndex, - lineArray.lineLength); + lineArray.lineLength, + placedIconIndex); // The placedSymbolArray is used at render time in drawTileSymbols // These indices allow access to the array at collision detection time @@ -549,6 +551,7 @@ function addSymbol(bucket: SymbolBucket, let numIconVertices = 0; let numHorizontalGlyphVertices = 0; let numVerticalGlyphVertices = 0; + let placedIconSymbolIndex = -1; const placedTextSymbolIndices = {}; let key = murmur3(''); @@ -568,41 +571,10 @@ function addSymbol(bucket: SymbolBucket, verticalTextCollisionFeature = new CollisionFeature(collisionBoxArray, line, anchor, featureIndex, sourceLayerIndex, bucketIndex, verticalShaping, textBoxScale, textPadding, textAlongLine, bucket.overscaling, verticalTextRotation); } - for (const justification: any in shapedTextOrientations.horizontal) { - const shaping = shapedTextOrientations.horizontal[justification]; - - if (!textCollisionFeature) { - key = murmur3(shaping.text); - const textRotate = layer.layout.get('text-rotate').evaluate(feature, {}); - // As a collision approximation, we can use either the vertical or any of the horizontal versions of the feature - // We're counting on all versions having similar dimensions - textCollisionFeature = new CollisionFeature(collisionBoxArray, line, anchor, featureIndex, sourceLayerIndex, bucketIndex, shaping, textBoxScale, textPadding, textAlongLine, bucket.overscaling, textRotate); - } - - const singleLine = shaping.lineCount === 1; - numHorizontalGlyphVertices += addTextVertices( - bucket, anchor, shaping, layer, textAlongLine, feature, textOffset, lineArray, - shapedTextOrientations.vertical ? WritingMode.horizontal : WritingMode.horizontalOnly, - singleLine ? (Object.keys(shapedTextOrientations.horizontal): any) : [justification], - placedTextSymbolIndices, glyphPositionMap, sizes); - - if (singleLine) { - break; - } - } - - if (shapedTextOrientations.vertical) { - numVerticalGlyphVertices += addTextVertices( - bucket, anchor, shapedTextOrientations.vertical, layer, textAlongLine, feature, - textOffset, lineArray, WritingMode.vertical, ['vertical'], placedTextSymbolIndices, glyphPositionMap, sizes); - } - - const textBoxStartIndex = textCollisionFeature ? textCollisionFeature.boxStartIndex : bucket.collisionBoxArray.length; - const textBoxEndIndex = textCollisionFeature ? textCollisionFeature.boxEndIndex : bucket.collisionBoxArray.length; - - const verticalTextBoxStartIndex = verticalTextCollisionFeature ? verticalTextCollisionFeature.boxStartIndex : bucket.collisionBoxArray.length; - const verticalTextBoxEndIndex = verticalTextCollisionFeature ? verticalTextCollisionFeature.boxEndIndex : bucket.collisionBoxArray.length; - + //Place icon first, so text can have a reference it its index in the placed symbol array. + //Text symbols can lazily shift at render-time because of variable anchor placement. + //If the style specifies an `icon-text-fit` then the icon would have to shift along with it. + // For more info check `updateVariableAnchors` in `draw_symbol.js` . if (shapedIcon) { const iconQuads = getIconQuads(anchor, shapedIcon, layer, iconAlongLine, getDefaultHorizontalShaping(shapedTextOrientations.horizontal), @@ -642,9 +614,48 @@ function addSymbol(bucket: SymbolBucket, false, anchor, lineArray.lineStartIndex, - lineArray.lineLength); + lineArray.lineLength, + // The icon itself does not have an associated symbol since the text isnt placed yet + -1); + + placedIconSymbolIndex = bucket.icon.placedSymbolArray.length - 1; } + for (const justification: any in shapedTextOrientations.horizontal) { + const shaping = shapedTextOrientations.horizontal[justification]; + + if (!textCollisionFeature) { + key = murmur3(shaping.text); + const textRotate = layer.layout.get('text-rotate').evaluate(feature, {}); + // As a collision approximation, we can use either the vertical or any of the horizontal versions of the feature + // We're counting on all versions having similar dimensions + textCollisionFeature = new CollisionFeature(collisionBoxArray, line, anchor, featureIndex, sourceLayerIndex, bucketIndex, shaping, textBoxScale, textPadding, textAlongLine, bucket.overscaling, textRotate); + } + + const singleLine = shaping.lineCount === 1; + numHorizontalGlyphVertices += addTextVertices( + bucket, anchor, shaping, layer, textAlongLine, feature, textOffset, lineArray, + shapedTextOrientations.vertical ? WritingMode.horizontal : WritingMode.horizontalOnly, + singleLine ? (Object.keys(shapedTextOrientations.horizontal): any) : [justification], + placedTextSymbolIndices, glyphPositionMap, placedIconSymbolIndex, sizes); + + if (singleLine) { + break; + } + } + + if (shapedTextOrientations.vertical) { + numVerticalGlyphVertices += addTextVertices( + bucket, anchor, shapedTextOrientations.vertical, layer, textAlongLine, feature, + textOffset, lineArray, WritingMode.vertical, ['vertical'], placedTextSymbolIndices, glyphPositionMap, placedIconSymbolIndex, sizes); + } + + const textBoxStartIndex = textCollisionFeature ? textCollisionFeature.boxStartIndex : bucket.collisionBoxArray.length; + const textBoxEndIndex = textCollisionFeature ? textCollisionFeature.boxEndIndex : bucket.collisionBoxArray.length; + + const verticalTextBoxStartIndex = verticalTextCollisionFeature ? verticalTextCollisionFeature.boxStartIndex : bucket.collisionBoxArray.length; + const verticalTextBoxEndIndex = verticalTextCollisionFeature ? verticalTextCollisionFeature.boxEndIndex : bucket.collisionBoxArray.length; + const iconBoxStartIndex = iconCollisionFeature ? iconCollisionFeature.boxStartIndex : bucket.collisionBoxArray.length; const iconBoxEndIndex = iconCollisionFeature ? iconCollisionFeature.boxEndIndex : bucket.collisionBoxArray.length; @@ -659,6 +670,7 @@ function addSymbol(bucket: SymbolBucket, placedTextSymbolIndices.center >= 0 ? placedTextSymbolIndices.center : -1, placedTextSymbolIndices.left >= 0 ? placedTextSymbolIndices.left : -1, placedTextSymbolIndices.vertical || -1, + placedIconSymbolIndex, key, textBoxStartIndex, textBoxEndIndex, From e479fc5bc3bb7b7a1620588a6a6ccc04471dbed2 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Mon, 16 Sep 2019 14:54:21 -0700 Subject: [PATCH 02/11] ensure u_label_plane_matrix is passed to icon shader --- debug/8583.html | 2 +- src/render/draw_symbol.js | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/debug/8583.html b/debug/8583.html index d4dede550d6..7b1cc3c1cfd 100644 --- a/debug/8583.html +++ b/debug/8583.html @@ -125,7 +125,7 @@ var map = window.map = new mapboxgl.Map({ container: 'map', - style: style + style }); diff --git a/src/render/draw_symbol.js b/src/render/draw_symbol.js index d662a46e0eb..4e9cfea4e11 100644 --- a/src/render/draw_symbol.js +++ b/src/render/draw_symbol.js @@ -143,7 +143,7 @@ function updateVariableAnchors(bucket, rotateWithMap, pitchWithMap, variableOffs addDynamicAttributes(dynamicTextLayoutVertexArray, shiftedAnchor, angle); } if (updateTextFitIcon && symbol.associatedIconIndex >= 0) { - placedTextShifts[symbol.associatedIconIndex] = {index: s, shiftedAnchor}; + placedTextShifts[symbol.associatedIconIndex] = shiftedAnchor; } } } @@ -154,15 +154,14 @@ function updateVariableAnchors(bucket, rotateWithMap, pitchWithMap, variableOffs for (let i = 0; i < placedIcons.length; i++) { const placedIcon = placedIcons.get(i); if (placedIcon.hidden || (!placedIcon.placedOrientation && bucket.allowVerticalPlacement)) { - symbolProjection.hideGlyphs(placedIcon.numGlyphs, bucket.icon.dynamicLayoutVertexArray); + symbolProjection.hideGlyphs(placedIcon.numGlyphs, dynamicIconLayoutVertexArray); } else { - const placedShift = placedTextShifts[i]; - if (!placedShift) { - symbolProjection.hideGlyphs(placedIcon.numGlyphs, bucket.icon.dynamicLayoutVertexArray); + const shiftedAnchor = placedTextShifts[i]; + if (!shiftedAnchor) { + symbolProjection.hideGlyphs(placedIcon.numGlyphs, dynamicIconLayoutVertexArray); } else { - const {index, shiftedAnchor} = placedShift; for (let g = 0; g < placedIcon.numGlyphs; g++) { - addDynamicAttributes(bucket.icon.dynamicLayoutVertexArray, shiftedAnchor, 0); + addDynamicAttributes(dynamicIconLayoutVertexArray, shiftedAnchor, 0); } } } @@ -278,7 +277,7 @@ function drawLayerSymbols(painter, sourceCache, layer, coords, isText, translate } const matrix = painter.translatePosMatrix(coord.posMatrix, tile, translate, translateAnchor), - uLabelPlaneMatrix = (alongLine || (isText && variablePlacement)) ? identityMat4 : labelPlaneMatrix, + uLabelPlaneMatrix = (alongLine || variablePlacement) ? identityMat4 : labelPlaneMatrix, uglCoordMatrix = painter.translatePosMatrix(glCoordMatrix, tile, translate, translateAnchor, true); const hasHalo = isSDF && layer.paint.get(isText ? 'text-halo-width' : 'icon-halo-width').constantOr(1) !== 0; From cb04ddf8b2a03c846c9f8b2584c804cbffb91da6 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Mon, 16 Sep 2019 15:43:17 -0700 Subject: [PATCH 03/11] fix broken render tests --- src/render/draw_symbol.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render/draw_symbol.js b/src/render/draw_symbol.js index 4e9cfea4e11..878b12cade3 100644 --- a/src/render/draw_symbol.js +++ b/src/render/draw_symbol.js @@ -277,7 +277,7 @@ function drawLayerSymbols(painter, sourceCache, layer, coords, isText, translate } const matrix = painter.translatePosMatrix(coord.posMatrix, tile, translate, translateAnchor), - uLabelPlaneMatrix = (alongLine || variablePlacement) ? identityMat4 : labelPlaneMatrix, + uLabelPlaneMatrix = (alongLine || (isText && variablePlacement) || updateTextFitIcon) ? identityMat4 : labelPlaneMatrix, uglCoordMatrix = painter.translatePosMatrix(glCoordMatrix, tile, translate, translateAnchor, true); const hasHalo = isSDF && layer.paint.get(isText ? 'text-halo-width' : 'icon-halo-width').constantOr(1) !== 0; From 868ff57e08a2eddb79ea9c7289c523dd87ba0078 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Tue, 17 Sep 2019 13:20:23 -0700 Subject: [PATCH 04/11] compute variable placement text-offsets before painting --- src/render/draw_symbol.js | 54 ++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/src/render/draw_symbol.js b/src/render/draw_symbol.js index 878b12cade3..f2d52e229b4 100644 --- a/src/render/draw_symbol.js +++ b/src/render/draw_symbol.js @@ -55,6 +55,17 @@ function drawSymbols(painter: Painter, sourceCache: SourceCache, layer: SymbolSt // Disable the stencil test so that labels aren't clipped to tile boundaries. const stencilMode = StencilMode.disabled; const colorMode = painter.colorModeForRenderPass(); + const variablePlacement = layer.layout.get('text-variable-anchor'); + + //Compute variable-offsets before painting since icons and text data positioning + //depend on each other in this case. + if (variablePlacement) { + updateVariableAnchors(coords, painter, layer, sourceCache, + layer.layout.get('text-rotation-alignment'), + layer.layout.get('text-pitch-alignment'), + variableOffsets + ); + } if (layer.paint.get('icon-opacity').constantOr(1) !== 0) { drawLayerSymbols(painter, sourceCache, layer, coords, false, @@ -63,7 +74,7 @@ function drawSymbols(painter: Painter, sourceCache: SourceCache, layer: SymbolSt layer.layout.get('icon-rotation-alignment'), layer.layout.get('icon-pitch-alignment'), layer.layout.get('icon-keep-upright'), - stencilMode, colorMode, variableOffsets + stencilMode, colorMode ); } @@ -74,7 +85,7 @@ function drawSymbols(painter: Painter, sourceCache: SourceCache, layer: SymbolSt layer.layout.get('text-rotation-alignment'), layer.layout.get('text-pitch-alignment'), layer.layout.get('text-keep-upright'), - stencilMode, colorMode, variableOffsets + stencilMode, colorMode ); } @@ -97,7 +108,36 @@ function calculateVariableRenderShift(anchor, width, height, textOffset, textBox ); } -function updateVariableAnchors(bucket, rotateWithMap, pitchWithMap, variableOffsets, symbolSize, +function updateVariableAnchors(coords, painter, layer, sourceCache, rotationAlignment, pitchAlignment, variableOffsets) { + const tr = painter.transform; + const rotateWithMap = rotationAlignment === 'map'; + const pitchWithMap = pitchAlignment === 'map'; + + for (const coord of coords) { + const tile = sourceCache.getTile(coord); + const bucket: SymbolBucket = (tile.getBucket(layer): any); + if (!bucket) continue; + const buffers = bucket.text; + if (!buffers || !buffers.segments.get().length) continue; + + const sizeData = bucket.textSizeData; + const size = symbolSize.evaluateSizeForZoom(sizeData, tr.zoom); + + const s = pixelsToTileUnits(tile, 1, painter.transform.zoom); + const labelPlaneMatrix = symbolProjection.getLabelPlaneMatrix(coord.posMatrix, pitchWithMap, rotateWithMap, painter.transform, s); + const updateTextFitIcon = layer.layout.get('icon-text-fit') !== 'none' && bucket.hasIconData(); + + if (size) { + const tileScale = Math.pow(2, tr.zoom - tile.tileID.overscaledZ); + updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, variableOffsets, symbolSize, + tr, labelPlaneMatrix, coord.posMatrix, tileScale, size, updateTextFitIcon); + + } + } + +} + +function updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, variableOffsets, symbolSize, transform, labelPlaneMatrix, posMatrix, tileScale, size, updateTextFitIcon) { const placedSymbols = bucket.text.placedSymbolArray; const dynamicTextLayoutVertexArray = bucket.text.dynamicLayoutVertexArray; @@ -195,7 +235,7 @@ function updateVerticalLabels(bucket) { } function drawLayerSymbols(painter, sourceCache, layer, coords, isText, translate, translateAnchor, - rotationAlignment, pitchAlignment, keepUpright, stencilMode, colorMode, variableOffsets) { + rotationAlignment, pitchAlignment, keepUpright, stencilMode, colorMode) { const context = painter.context; const gl = context.gl; @@ -268,11 +308,7 @@ function drawLayerSymbols(painter, sourceCache, layer, coords, isText, translate if (alongLine) { symbolProjection.updateLineLabels(bucket, coord.posMatrix, painter, isText, labelPlaneMatrix, glCoordMatrix, pitchWithMap, keepUpright); - } else if (isText && size && variablePlacement) { - const tileScale = Math.pow(2, tr.zoom - tile.tileID.overscaledZ); - updateVariableAnchors(bucket, rotateWithMap, pitchWithMap, variableOffsets, symbolSize, - tr, labelPlaneMatrix, coord.posMatrix, tileScale, size, updateTextFitIcon); - } else if (isText && size && bucket.allowVerticalPlacement) { + } else if (isText && size && bucket.allowVerticalPlacement && !variablePlacement) { updateVerticalLabels(bucket); } From 3a13ebccf0b8c4f5cd405ebf643c9a5729fe92b1 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Tue, 17 Sep 2019 13:40:30 -0700 Subject: [PATCH 05/11] Add render test --- .../text-variable-anchor/expected.png | Bin 0 -> 22229 bytes .../text-variable-anchor/style.json | 59 ++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 test/integration/render-tests/icon-text-fit/text-variable-anchor/expected.png create mode 100644 test/integration/render-tests/icon-text-fit/text-variable-anchor/style.json diff --git a/test/integration/render-tests/icon-text-fit/text-variable-anchor/expected.png b/test/integration/render-tests/icon-text-fit/text-variable-anchor/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..93b68ba6490222f7bd8222f39b283be51d6c825b GIT binary patch literal 22229 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 literal 0 HcmV?d00001 diff --git a/test/integration/render-tests/icon-text-fit/text-variable-anchor/style.json b/test/integration/render-tests/icon-text-fit/text-variable-anchor/style.json new file mode 100644 index 00000000000..e27f39374e8 --- /dev/null +++ b/test/integration/render-tests/icon-text-fit/text-variable-anchor/style.json @@ -0,0 +1,59 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 256, + "allowed": 0.0005 + } + }, + "center": [ + 13.417, + 52.502 + ], + "zoom": 14, + "sources": { + "mapbox": { + "type": "vector", + "maxzoom": 14, + "tiles": [ + "local://tiles/{z}-{x}-{y}.mvt" + ] + } + }, + "sprite": "local://sprites/icon-text-fit", + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "white" + } + }, + { + "id": "road", + "type": "symbol", + "source": "mapbox", + "source-layer": "road_label", + "layout": { + "text-variable-anchor": ["left", "right", "bottom", "top"], + "text-field": "{name}", + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ], + "icon-image": "label", + "icon-text-fit": "both", + "icon-text-fit-padding": [ + 5, + 10, + 5, + 10 + ] + }, + "paint": { + "icon-opacity": 1 + } + } + ] +} From 93ad8e70769fda32be7e4a42393a02a5497a989b Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Tue, 17 Sep 2019 15:02:29 -0700 Subject: [PATCH 06/11] Rename debug page based on CR --- debug/{8583.html => variable-anchor-with-icon-text-fit.html} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename debug/{8583.html => variable-anchor-with-icon-text-fit.html} (100%) diff --git a/debug/8583.html b/debug/variable-anchor-with-icon-text-fit.html similarity index 100% rename from debug/8583.html rename to debug/variable-anchor-with-icon-text-fit.html From b943aa53e26700e4886f5121fde85f54972f9b65 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Wed, 18 Sep 2019 12:50:35 -0700 Subject: [PATCH 07/11] Remove private accessToken and spritesheet --- debug/variable-anchor-with-icon-text-fit.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/debug/variable-anchor-with-icon-text-fit.html b/debug/variable-anchor-with-icon-text-fit.html index 7b1cc3c1cfd..9ade7ac7172 100644 --- a/debug/variable-anchor-with-icon-text-fit.html +++ b/debug/variable-anchor-with-icon-text-fit.html @@ -17,7 +17,6 @@