diff --git a/src/data/array_types.js b/src/data/array_types.js index b5114e89d7a..ffc8bf7f62c 100644 --- a/src/data/array_types.js +++ b/src/data/array_types.js @@ -477,14 +477,14 @@ register('StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48', StructArrayLayout2i2ui3ul3u /** * Implementation of the StructArray layout: - * [0]: Int16[7] - * [14]: Uint16[11] - * [36]: Uint32[1] - * [40]: Float32[3] + * [0]: Int16[8] + * [16]: Uint16[14] + * [44]: Uint32[1] + * [48]: Float32[3] * * @private */ -class StructArrayLayout7i11ui1ul3f52 extends StructArray { +class StructArrayLayout8i14ui1ul3f60 extends StructArray { uint8: Uint8Array; int16: Int16Array; uint16: Uint16Array; @@ -499,15 +499,15 @@ class StructArrayLayout7i11ui1ul3f52 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, v21: 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, v22: number, v23: number, v24: number, v25: 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, v21); + 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, v22, v23, v24, v25); } - 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; + 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, v22: number, v23: number, v24: number, v25: number) { + const o2 = i * 30; + const o4 = i * 15; this.int16[o2 + 0] = v0; this.int16[o2 + 1] = v1; this.int16[o2 + 2] = v2; @@ -515,7 +515,7 @@ class StructArrayLayout7i11ui1ul3f52 extends StructArray { this.int16[o2 + 4] = v4; this.int16[o2 + 5] = v5; this.int16[o2 + 6] = v6; - this.uint16[o2 + 7] = v7; + this.int16[o2 + 7] = v7; this.uint16[o2 + 8] = v8; this.uint16[o2 + 9] = v9; this.uint16[o2 + 10] = v10; @@ -526,16 +526,20 @@ class StructArrayLayout7i11ui1ul3f52 extends StructArray { this.uint16[o2 + 15] = v15; this.uint16[o2 + 16] = v16; 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; + this.uint16[o2 + 18] = v18; + this.uint16[o2 + 19] = v19; + this.uint16[o2 + 20] = v20; + this.uint16[o2 + 21] = v21; + this.uint32[o4 + 11] = v22; + this.float32[o4 + 12] = v23; + this.float32[o4 + 13] = v24; + this.float32[o4 + 14] = v25; return i; } } -StructArrayLayout7i11ui1ul3f52.prototype.bytesPerElement = 52; -register('StructArrayLayout7i11ui1ul3f52', StructArrayLayout7i11ui1ul3f52); +StructArrayLayout8i14ui1ul3f60.prototype.bytesPerElement = 60; +register('StructArrayLayout8i14ui1ul3f60', StructArrayLayout8i14ui1ul3f60); /** * Implementation of the StructArray layout: @@ -943,6 +947,7 @@ class SymbolInstanceStruct extends Struct { leftJustifiedTextSymbolIndex: number; verticalPlacedTextSymbolIndex: number; placedIconSymbolIndex: number; + verticalPlacedIconSymbolIndex: number; key: number; textBoxStartIndex: number; textBoxEndIndex: number; @@ -950,10 +955,13 @@ class SymbolInstanceStruct extends Struct { verticalTextBoxEndIndex: number; iconBoxStartIndex: number; iconBoxEndIndex: number; + verticalIconBoxStartIndex: number; + verticalIconBoxEndIndex: number; featureIndex: number; numHorizontalGlyphVertices: number; numVerticalGlyphVertices: number; numIconVertices: number; + numVerticalIconVertices: number; crossTileID: number; textBoxScale: number; textOffset0: number; @@ -972,46 +980,54 @@ class SymbolInstanceStruct extends Struct { set verticalPlacedTextSymbolIndex(x: number) { this._structArray.int16[this._pos2 + 5] = 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]; } - set textBoxScale(x: number) { this._structArray.float32[this._pos4 + 10] = x; } - get textOffset0() { return this._structArray.float32[this._pos4 + 11]; } - set textOffset0(x: number) { this._structArray.float32[this._pos4 + 11] = x; } - get textOffset1() { return this._structArray.float32[this._pos4 + 12]; } - set textOffset1(x: number) { this._structArray.float32[this._pos4 + 12] = x; } + get verticalPlacedIconSymbolIndex() { return this._structArray.int16[this._pos2 + 7]; } + set verticalPlacedIconSymbolIndex(x: number) { this._structArray.int16[this._pos2 + 7] = x; } + get key() { return this._structArray.uint16[this._pos2 + 8]; } + set key(x: number) { this._structArray.uint16[this._pos2 + 8] = x; } + get textBoxStartIndex() { return this._structArray.uint16[this._pos2 + 9]; } + set textBoxStartIndex(x: number) { this._structArray.uint16[this._pos2 + 9] = x; } + get textBoxEndIndex() { return this._structArray.uint16[this._pos2 + 10]; } + set textBoxEndIndex(x: number) { this._structArray.uint16[this._pos2 + 10] = x; } + get verticalTextBoxStartIndex() { return this._structArray.uint16[this._pos2 + 11]; } + set verticalTextBoxStartIndex(x: number) { this._structArray.uint16[this._pos2 + 11] = x; } + get verticalTextBoxEndIndex() { return this._structArray.uint16[this._pos2 + 12]; } + set verticalTextBoxEndIndex(x: number) { this._structArray.uint16[this._pos2 + 12] = x; } + get iconBoxStartIndex() { return this._structArray.uint16[this._pos2 + 13]; } + set iconBoxStartIndex(x: number) { this._structArray.uint16[this._pos2 + 13] = x; } + get iconBoxEndIndex() { return this._structArray.uint16[this._pos2 + 14]; } + set iconBoxEndIndex(x: number) { this._structArray.uint16[this._pos2 + 14] = x; } + get verticalIconBoxStartIndex() { return this._structArray.uint16[this._pos2 + 15]; } + set verticalIconBoxStartIndex(x: number) { this._structArray.uint16[this._pos2 + 15] = x; } + get verticalIconBoxEndIndex() { return this._structArray.uint16[this._pos2 + 16]; } + set verticalIconBoxEndIndex(x: number) { this._structArray.uint16[this._pos2 + 16] = x; } + get featureIndex() { return this._structArray.uint16[this._pos2 + 17]; } + set featureIndex(x: number) { this._structArray.uint16[this._pos2 + 17] = x; } + get numHorizontalGlyphVertices() { return this._structArray.uint16[this._pos2 + 18]; } + set numHorizontalGlyphVertices(x: number) { this._structArray.uint16[this._pos2 + 18] = x; } + get numVerticalGlyphVertices() { return this._structArray.uint16[this._pos2 + 19]; } + set numVerticalGlyphVertices(x: number) { this._structArray.uint16[this._pos2 + 19] = x; } + get numIconVertices() { return this._structArray.uint16[this._pos2 + 20]; } + set numIconVertices(x: number) { this._structArray.uint16[this._pos2 + 20] = x; } + get numVerticalIconVertices() { return this._structArray.uint16[this._pos2 + 21]; } + set numVerticalIconVertices(x: number) { this._structArray.uint16[this._pos2 + 21] = x; } + get crossTileID() { return this._structArray.uint32[this._pos4 + 11]; } + set crossTileID(x: number) { this._structArray.uint32[this._pos4 + 11] = x; } + get textBoxScale() { return this._structArray.float32[this._pos4 + 12]; } + set textBoxScale(x: number) { this._structArray.float32[this._pos4 + 12] = x; } + get textOffset0() { return this._structArray.float32[this._pos4 + 13]; } + set textOffset0(x: number) { this._structArray.float32[this._pos4 + 13] = x; } + get textOffset1() { return this._structArray.float32[this._pos4 + 14]; } + set textOffset1(x: number) { this._structArray.float32[this._pos4 + 14] = x; } } -SymbolInstanceStruct.prototype.size = 52; +SymbolInstanceStruct.prototype.size = 60; export type SymbolInstance = SymbolInstanceStruct; /** * @private */ -export class SymbolInstanceArray extends StructArrayLayout7i11ui1ul3f52 { +export class SymbolInstanceArray extends StructArrayLayout8i14ui1ul3f60 { /** * Return the SymbolInstanceStruct at the given location in the array. * @param {number} index The index of the element. @@ -1134,7 +1150,7 @@ export { StructArrayLayout2i2i2i12, StructArrayLayout2ub2f12, StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48, - StructArrayLayout7i11ui1ul3f52, + StructArrayLayout8i14ui1ul3f60, StructArrayLayout1f4, StructArrayLayout3i6, StructArrayLayout1ul2ui8, diff --git a/src/data/bucket/symbol_attributes.js b/src/data/bucket/symbol_attributes.js index d3384f3cb24..bc8bb06e0bf 100644 --- a/src/data/bucket/symbol_attributes.js +++ b/src/data/bucket/symbol_attributes.js @@ -85,6 +85,7 @@ export const symbolInstance = createLayout([ {type: 'Int16', name: 'leftJustifiedTextSymbolIndex'}, {type: 'Int16', name: 'verticalPlacedTextSymbolIndex'}, {type: 'Int16', name: 'placedIconSymbolIndex'}, + {type: 'Int16', name: 'verticalPlacedIconSymbolIndex'}, {type: 'Uint16', name: 'key'}, {type: 'Uint16', name: 'textBoxStartIndex'}, {type: 'Uint16', name: 'textBoxEndIndex'}, @@ -92,10 +93,13 @@ export const symbolInstance = createLayout([ {type: 'Uint16', name: 'verticalTextBoxEndIndex'}, {type: 'Uint16', name: 'iconBoxStartIndex'}, {type: 'Uint16', name: 'iconBoxEndIndex'}, + {type: 'Uint16', name: 'verticalIconBoxStartIndex'}, + {type: 'Uint16', name: 'verticalIconBoxEndIndex'}, {type: 'Uint16', name: 'featureIndex'}, {type: 'Uint16', name: 'numHorizontalGlyphVertices'}, {type: 'Uint16', name: 'numVerticalGlyphVertices'}, {type: 'Uint16', name: 'numIconVertices'}, + {type: 'Uint16', name: 'numVerticalIconVertices'}, {type: 'Uint32', name: 'crossTileID'}, {type: 'Float32', name: 'textBoxScale'}, {type: 'Float32', components: 2, name: 'textOffset'} diff --git a/src/data/bucket/symbol_bucket.js b/src/data/bucket/symbol_bucket.js index b506c48f61e..c34f8d880b4 100644 --- a/src/data/bucket/symbol_bucket.js +++ b/src/data/bucket/symbol_bucket.js @@ -68,10 +68,12 @@ export type CollisionArrays = { textBox?: SingleCollisionBox; verticalTextBox?: SingleCollisionBox; iconBox?: SingleCollisionBox; + verticalIconBox?: SingleCollisionBox; textCircles?: Array; textFeatureIndex?: number; verticalTextFeatureIndex?: number; iconFeatureIndex?: number; + verticalIconFeatureIndex?: number; }; export type SymbolFeature = {| @@ -711,12 +713,18 @@ class SymbolBucket implements Bucket { this.addDebugCollisionBoxes(symbolInstance.textBoxStartIndex, symbolInstance.textBoxEndIndex, symbolInstance, true); this.addDebugCollisionBoxes(symbolInstance.verticalTextBoxStartIndex, symbolInstance.verticalTextBoxEndIndex, symbolInstance, true); this.addDebugCollisionBoxes(symbolInstance.iconBoxStartIndex, symbolInstance.iconBoxEndIndex, symbolInstance, false); + this.addDebugCollisionBoxes(symbolInstance.verticalIconBoxStartIndex, symbolInstance.verticalIconBoxEndIndex, symbolInstance, false); } } // These flat arrays are meant to be quicker to iterate over than the source // CollisionBoxArray - _deserializeCollisionBoxesForSymbol(collisionBoxArray: CollisionBoxArray, textStartIndex: number, textEndIndex: number, verticalTextStartIndex: number, verticalTextEndIndex: number, iconStartIndex: number, iconEndIndex: number): CollisionArrays { + _deserializeCollisionBoxesForSymbol(collisionBoxArray: CollisionBoxArray, + textStartIndex: number, textEndIndex: number, + verticalTextStartIndex: number, verticalTextEndIndex: number, + iconStartIndex: number, iconEndIndex: number, + verticalIconStartIndex: number, verticalIconEndIndex: number): CollisionArrays { + const collisionArrays = {}; for (let k = textStartIndex; k < textEndIndex; k++) { const box: CollisionBox = (collisionBoxArray.get(k): any); @@ -750,6 +758,15 @@ class SymbolBucket implements Bucket { break; // Only one box allowed per instance } } + for (let k = verticalIconStartIndex; k < verticalIconEndIndex; k++) { + // An icon can only have one box now, so this indexing is a bit vestigial... + const box: CollisionBox = (collisionBoxArray.get(k): any); + if (box.radius === 0) { + collisionArrays.verticalIconBox = {x1: box.x1, y1: box.y1, x2: box.x2, y2: box.y2, anchorPointX: box.anchorPointX, anchorPointY: box.anchorPointY}; + collisionArrays.verticalIconFeatureIndex = box.featureIndex; + break; // Only one box allowed per instance + } + } return collisionArrays; } @@ -764,7 +781,9 @@ class SymbolBucket implements Bucket { symbolInstance.verticalTextBoxStartIndex, symbolInstance.verticalTextBoxEndIndex, symbolInstance.iconBoxStartIndex, - symbolInstance.iconBoxEndIndex + symbolInstance.iconBoxEndIndex, + symbolInstance.verticalIconBoxStartIndex, + symbolInstance.verticalIconBoxEndIndex )); } } @@ -803,6 +822,15 @@ class SymbolBucket implements Bucket { } } + addIndicesForPlacedIconSymbol(placedIconSymbolIndex: number) { + const placedIcon = this.icon.placedSymbolArray.get(placedIconSymbolIndex); + if (placedIcon.numGlyphs) { + const vertexIndex = placedIcon.vertexStartIndex; + this.icon.indexArray.emplaceBack(vertexIndex, vertexIndex + 1, vertexIndex + 2); + this.icon.indexArray.emplaceBack(vertexIndex + 1, vertexIndex + 2, vertexIndex + 3); + } + } + getSortedSymbolIndexes(angle: number) { if (this.sortedAngle === angle && this.symbolInstanceIndexes !== undefined) { return this.symbolInstanceIndexes; @@ -870,11 +898,12 @@ class SymbolBucket implements Bucket { this.addIndicesForPlacedTextSymbol(symbolInstance.verticalPlacedTextSymbolIndex); } - const placedIcon = this.icon.placedSymbolArray.get(i); - if (placedIcon.numGlyphs) { - const vertexIndex = placedIcon.vertexStartIndex; - this.icon.indexArray.emplaceBack(vertexIndex, vertexIndex + 1, vertexIndex + 2); - this.icon.indexArray.emplaceBack(vertexIndex + 1, vertexIndex + 2, vertexIndex + 3); + if (symbolInstance.placedIconSymbolIndex >= 0) { + this.addIndicesForPlacedIconSymbol(symbolInstance.placedIconSymbolIndex); + } + + if (symbolInstance.verticalPlacedIconSymbolIndex >= 0) { + this.addIndicesForPlacedIconSymbol(symbolInstance.verticalPlacedIconSymbolIndex); } } diff --git a/src/render/draw_symbol.js b/src/render/draw_symbol.js index e7279f92633..ff8c06cdaee 100644 --- a/src/render/draw_symbol.js +++ b/src/render/draw_symbol.js @@ -208,29 +208,6 @@ function updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, var bucket.text.dynamicLayoutVertexBuffer.updateData(dynamicTextLayoutVertexArray); } -function updateVerticalLabels(bucket) { - const placedSymbols = bucket.text.placedSymbolArray; - const dynamicLayoutVertexArray = bucket.text.dynamicLayoutVertexArray; - dynamicLayoutVertexArray.clear(); - for (let s = 0; s < placedSymbols.length; s++) { - const symbol: any = placedSymbols.get(s); - const shouldHide = symbol.hidden || !symbol.placedOrientation; - if (shouldHide) { - // These symbols are from an orientation 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); - } else { - const tileAnchor = new Point(symbol.anchorX, symbol.anchorY); - const angle = (bucket.allowVerticalPlacement && symbol.placedOrientation === WritingMode.vertical) ? Math.PI / 2 : 0; - - for (let g = 0; g < symbol.numGlyphs; g++) { - addDynamicAttributes(dynamicLayoutVertexArray, tileAnchor, angle); - } - } - } - bucket.text.dynamicLayoutVertexBuffer.updateData(dynamicLayoutVertexArray); -} - function drawLayerSymbols(painter, sourceCache, layer, coords, isText, translate, translateAnchor, rotationAlignment, pitchAlignment, keepUpright, stencilMode, colorMode) { @@ -305,8 +282,6 @@ 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 && bucket.allowVerticalPlacement && !variablePlacement) { - updateVerticalLabels(bucket); } const matrix = painter.translatePosMatrix(coord.posMatrix, tile, translate, translateAnchor), diff --git a/src/symbol/placement.js b/src/symbol/placement.js index 0414826a8c2..8e474b7ef94 100644 --- a/src/symbol/placement.js +++ b/src/symbol/placement.js @@ -332,7 +332,7 @@ export class Placement { let shift = null; let placed = {box: null, offscreen: null}; - let placedVertical = {box: null, offscreen: null}; + let placedVerticalText = {box: null, offscreen: null}; let placedGlyphBoxes = null; let placedGlyphCircles = null; @@ -369,7 +369,7 @@ export class Placement { for (const placementMode of bucket.writingModes) { if (placementMode === WritingMode.vertical) { placed = placeVerticalFn(); - placedVertical = placed; + placedVerticalText = placed; } else { placed = placeHorizontalFn(); } @@ -515,17 +515,26 @@ export class Placement { if (collisionArrays.iconFeatureIndex) { iconFeatureIndex = collisionArrays.iconFeatureIndex; } - if (collisionArrays.iconBox) { - const iconBox = hasIconTextFit && shift ? - shiftVariableCollisionBox( - collisionArrays.iconBox, shift.x, shift.y, - rotateWithMap, pitchWithMap, this.transform.angle) : - collisionArrays.iconBox; + if (collisionArrays.iconBox) { - placedIconBoxes = this.collisionIndex.placeCollisionBox(iconBox, + const placeIconFeature = iconBox => { + const shiftedIconBox = hasIconTextFit && shift ? + shiftVariableCollisionBox( + iconBox, shift.x, shift.y, + rotateWithMap, pitchWithMap, this.transform.angle) : + iconBox; + return this.collisionIndex.placeCollisionBox(shiftedIconBox, layout.get('icon-allow-overlap'), textPixelRatio, posMatrix, collisionGroup.predicate); - placeIcon = placedIconBoxes.box.length > 0; + }; + + if (placedVerticalText && placedVerticalText.box && placedVerticalText.box.length && collisionArrays.verticalIconBox) { + placedIconBoxes = placeIconFeature(collisionArrays.verticalIconBox); + placeIcon = placedIconBoxes.box.length > 0; + } else { + placedIconBoxes = placeIconFeature(collisionArrays.iconBox); + placeIcon = placedIconBoxes.box.length > 0; + } offscreen = offscreen && placedIconBoxes.offscreen; } @@ -543,7 +552,7 @@ export class Placement { } if (placeText && placedGlyphBoxes && placedGlyphBoxes.box) { - if (placedVertical && placedVertical.box && verticalTextFeatureIndex) { + if (placedVerticalText && placedVerticalText.box && verticalTextFeatureIndex) { this.collisionIndex.insertCollisionBox(placedGlyphBoxes.box, layout.get('text-ignore-placement'), bucket.bucketInstanceId, verticalTextFeatureIndex, collisionGroup.ID); } else { @@ -739,6 +748,12 @@ export class Placement { bucket.deserializeCollisionBoxes(collisionBoxArray); } + const addOpacities = (iconOrText, numVertices: number, opacity: number) => { + for (let i = 0; i < numVertices / 4; i++) { + iconOrText.opacityVertexArray.emplaceBack(opacity); + } + }; + for (let s = 0; s < bucket.symbolInstances.length; s++) { const symbolInstance = bucket.symbolInstances.get(s); const { @@ -763,34 +778,36 @@ export class Placement { const hasText = numHorizontalGlyphVertices > 0 || numVerticalGlyphVertices > 0; const hasIcon = symbolInstance.numIconVertices > 0; + const placedOrientation = this.placedOrientations[symbolInstance.crossTileID]; + const horizontalHidden = placedOrientation === WritingMode.vertical; + const verticalHidden = placedOrientation === WritingMode.horizontal || placedOrientation === WritingMode.horizontalOnly; + if (hasText) { const packedOpacity = packOpacity(opacityState.text); // Vertical text fades in/out on collision the same way as corresponding // horizontal text. Switch between vertical/horizontal should be instantaneous - const opacityEntryCount = (numHorizontalGlyphVertices + numVerticalGlyphVertices) / 4; - for (let i = 0; i < opacityEntryCount; i++) { - bucket.text.opacityVertexArray.emplaceBack(packedOpacity); - } + const horizontalOpacity = horizontalHidden ? PACKED_HIDDEN_OPACITY : packedOpacity; + addOpacities(bucket.text, numHorizontalGlyphVertices, horizontalOpacity); + const verticalOpacity = verticalHidden ? PACKED_HIDDEN_OPACITY : packedOpacity; + addOpacities(bucket.text, numVerticalGlyphVertices, verticalOpacity); + // If this label is completely faded, mark it so that we don't have to calculate // its position at render time. If this layer has variable placement, shift the various // symbol instances appropriately so that symbols from buckets that have yet to be placed // offset appropriately. - const symbolHidden = opacityState.text.isHidden() ? 1 : 0; - const placedOrientation = this.placedOrientations[symbolInstance.crossTileID]; - const verticalHidden = (placedOrientation === WritingMode.horizontal || placedOrientation === WritingMode.horizontalOnly) ? 1 : 0; - const horizontalHidden = placedOrientation === WritingMode.vertical ? 1 : 0; + const symbolHidden = opacityState.text.isHidden(); [ symbolInstance.rightJustifiedTextSymbolIndex, symbolInstance.centerJustifiedTextSymbolIndex, symbolInstance.leftJustifiedTextSymbolIndex ].forEach(index => { if (index >= 0) { - bucket.text.placedSymbolArray.get(index).hidden = symbolHidden || horizontalHidden; + bucket.text.placedSymbolArray.get(index).hidden = symbolHidden || horizontalHidden ? 1 : 0; } }); if (symbolInstance.verticalPlacedTextSymbolIndex >= 0) { - bucket.text.placedSymbolArray.get(symbolInstance.verticalPlacedTextSymbolIndex).hidden = symbolHidden || verticalHidden; + bucket.text.placedSymbolArray.get(symbolInstance.verticalPlacedTextSymbolIndex).hidden = symbolHidden || verticalHidden ? 1 : 0; } const prevOffset = this.variableOffsets[symbolInstance.crossTileID]; @@ -807,11 +824,22 @@ export class Placement { if (hasIcon) { const packedOpacity = packOpacity(opacityState.icon); - for (let i = 0; i < symbolInstance.numIconVertices / 4; i++) { - bucket.icon.opacityVertexArray.emplaceBack(packedOpacity); + + const useHorizontal = !(hasIconTextFit && symbolInstance.verticalPlacedIconSymbolIndex && horizontalHidden); + + if (symbolInstance.placedIconSymbolIndex >= 0) { + const horizontalOpacity = useHorizontal ? packedOpacity : PACKED_HIDDEN_OPACITY; + addOpacities(bucket.icon, symbolInstance.numIconVertices, horizontalOpacity); + bucket.icon.placedSymbolArray.get(symbolInstance.placedIconSymbolIndex).hidden = + (opacityState.icon.isHidden(): any); + } + + if (symbolInstance.verticalPlacedIconSymbolIndex >= 0) { + const verticalOpacity = !useHorizontal ? packedOpacity : PACKED_HIDDEN_OPACITY; + addOpacities(bucket.icon, symbolInstance.numVerticalIconVertices, verticalOpacity); + bucket.icon.placedSymbolArray.get(symbolInstance.verticalPlacedIconSymbolIndex).hidden = + (opacityState.icon.isHidden(): any); } - bucket.icon.placedSymbolArray.get(s).hidden = - (opacityState.icon.isHidden(): any); } if (bucket.hasIconCollisionBoxData() || bucket.hasIconCollisionCircleData() || @@ -819,7 +847,7 @@ export class Placement { const collisionArrays = bucket.collisionArrays[s]; if (collisionArrays) { let shift = new Point(0, 0); - if (collisionArrays.textBox) { + if (collisionArrays.textBox || collisionArrays.verticalTextBox) { let used = true; if (variablePlacement) { const variableOffset = this.variableOffsets[crossTileID]; @@ -844,11 +872,24 @@ export class Placement { } } - updateCollisionVertices(bucket.textCollisionBox.collisionVertexArray, opacityState.text.placed, !used, shift.x, shift.y); + if (collisionArrays.textBox) { + updateCollisionVertices(bucket.textCollisionBox.collisionVertexArray, opacityState.text.placed, !used || horizontalHidden, shift.x, shift.y); + } + if (collisionArrays.verticalTextBox) { + updateCollisionVertices(bucket.textCollisionBox.collisionVertexArray, opacityState.text.placed, !used || verticalHidden, shift.x, shift.y); + } } + const verticalIconUsed = Boolean(!verticalHidden && collisionArrays.verticalIconBox); + if (collisionArrays.iconBox) { - updateCollisionVertices(bucket.iconCollisionBox.collisionVertexArray, opacityState.icon.placed, false, + updateCollisionVertices(bucket.iconCollisionBox.collisionVertexArray, opacityState.icon.placed, verticalIconUsed, + hasIconTextFit ? shift.x : 0, + hasIconTextFit ? shift.y : 0); + } + + if (collisionArrays.verticalIconBox) { + updateCollisionVertices(bucket.iconCollisionBox.collisionVertexArray, opacityState.icon.placed, !verticalIconUsed, hasIconTextFit ? shift.x : 0, hasIconTextFit ? shift.y : 0); } @@ -928,7 +969,7 @@ export class Placement { } } -function updateCollisionVertices(collisionVertexArray: CollisionVertexArray, placed: boolean, notUsed: boolean, shiftX?: number, shiftY?: number) { +function updateCollisionVertices(collisionVertexArray: CollisionVertexArray, placed: boolean, notUsed: boolean | number, shiftX?: number, shiftY?: number) { collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0); collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0); collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0); @@ -959,3 +1000,5 @@ function packOpacity(opacityState: OpacityState): number { opacityBits * shift9 + targetBit * shift8 + opacityBits * shift1 + targetBit; } + +const PACKED_HIDDEN_OPACITY = 0; diff --git a/src/symbol/symbol_layout.js b/src/symbol/symbol_layout.js index 28cc9146dd5..f07d1a04348 100644 --- a/src/symbol/symbol_layout.js +++ b/src/symbol/symbol_layout.js @@ -378,8 +378,13 @@ function addFeature(bucket: SymbolBucket, textRepeatDistance = symbolMinDistance / 2; const iconTextFit = layout.get('icon-text-fit'); + let verticallyShapedIcon; // Adjust shaped icon size when icon-text-fit is used. if (shapedIcon && iconTextFit !== 'none') { + if (bucket.allowVerticalPlacement && shapedTextOrientations.vertical) { + verticallyShapedIcon = fitIconToText(shapedIcon, shapedTextOrientations.vertical, iconTextFit, + layout.get('icon-text-fit-padding'), iconOffset, fontScale); + } if (defaultHorizontalShaping) { shapedIcon = fitIconToText(shapedIcon, defaultHorizontalShaping, iconTextFit, layout.get('icon-text-fit-padding'), iconOffset, fontScale); @@ -394,7 +399,7 @@ function addFeature(bucket: SymbolBucket, return; } - addSymbol(bucket, anchor, line, shapedTextOrientations, shapedIcon, bucket.layers[0], + addSymbol(bucket, anchor, line, shapedTextOrientations, shapedIcon, verticallyShapedIcon, bucket.layers[0], bucket.collisionBoxArray, feature.index, feature.sourceLayerIndex, bucket.index, textBoxScale, textPadding, textAlongLine, textOffset, iconBoxScale, iconPadding, iconAlongLine, iconOffset, @@ -538,6 +543,7 @@ function addSymbol(bucket: SymbolBucket, line: Array, shapedTextOrientations: any, shapedIcon: PositionedIcon | void, + verticallyShapedIcon: PositionedIcon | void, layer: SymbolStyleLayer, collisionBoxArray: CollisionBoxArray, featureIndex: number, @@ -556,12 +562,14 @@ function addSymbol(bucket: SymbolBucket, sizes: Sizes) { const lineArray = bucket.addToLineVertexArray(anchor, line); - let textCollisionFeature, iconCollisionFeature, verticalTextCollisionFeature; + let textCollisionFeature, iconCollisionFeature, verticalTextCollisionFeature, verticalIconCollisionFeature; let numIconVertices = 0; + let numVerticalIconVertices = 0; let numHorizontalGlyphVertices = 0; let numVerticalGlyphVertices = 0; let placedIconSymbolIndex = -1; + let verticalPlacedIconSymbolIndex = -1; const placedTextSymbolIndices = {}; let key = murmur3(''); @@ -579,6 +587,10 @@ function addSymbol(bucket: SymbolBucket, const verticalTextRotation = textRotation + 90.0; const verticalShaping = shapedTextOrientations.vertical; verticalTextCollisionFeature = new CollisionFeature(collisionBoxArray, line, anchor, featureIndex, sourceLayerIndex, bucketIndex, verticalShaping, textBoxScale, textPadding, textAlongLine, bucket.overscaling, verticalTextRotation); + + if (verticallyShapedIcon) { + verticalIconCollisionFeature = new CollisionFeature(collisionBoxArray, line, anchor, featureIndex, sourceLayerIndex, bucketIndex, verticallyShapedIcon, iconBoxScale, iconPadding, textAlongLine, bucket.overscaling, verticalTextRotation); + } } //Place icon first, so text can have a reference to its index in the placed symbol array. @@ -588,6 +600,7 @@ function addSymbol(bucket: SymbolBucket, if (shapedIcon) { const iconRotate = layer.layout.get('icon-rotate').evaluate(feature, {}); const iconQuads = getIconQuads(shapedIcon, iconRotate); + const verticalIconQuads = verticallyShapedIcon ? getIconQuads(verticallyShapedIcon, iconRotate) : undefined; iconCollisionFeature = new CollisionFeature(collisionBoxArray, line, anchor, featureIndex, sourceLayerIndex, bucketIndex, shapedIcon, iconBoxScale, iconPadding, /*align boxes to line*/false, bucket.overscaling, iconRotate); numIconVertices = iconQuads.length * 4; @@ -627,6 +640,26 @@ function addSymbol(bucket: SymbolBucket, -1); placedIconSymbolIndex = bucket.icon.placedSymbolArray.length - 1; + + if (verticalIconQuads) { + numVerticalIconVertices = verticalIconQuads.length * 4; + + bucket.addSymbols( + bucket.icon, + verticalIconQuads, + iconSizeData, + iconOffset, + iconAlongLine, + feature, + WritingMode.vertical, + anchor, + lineArray.lineStartIndex, + lineArray.lineLength, + // The icon itself does not have an associated symbol since the text isnt placed yet + -1); + + verticalPlacedIconSymbolIndex = bucket.icon.placedSymbolArray.length - 1; + } } for (const justification: any in shapedTextOrientations.horizontal) { @@ -655,7 +688,7 @@ function addSymbol(bucket: SymbolBucket, if (shapedTextOrientations.vertical) { numVerticalGlyphVertices += addTextVertices( bucket, anchor, shapedTextOrientations.vertical, layer, textAlongLine, feature, - textOffset, lineArray, WritingMode.vertical, ['vertical'], placedTextSymbolIndices, glyphPositionMap, placedIconSymbolIndex, sizes); + textOffset, lineArray, WritingMode.vertical, ['vertical'], placedTextSymbolIndices, glyphPositionMap, verticalPlacedIconSymbolIndex, sizes); } const textBoxStartIndex = textCollisionFeature ? textCollisionFeature.boxStartIndex : bucket.collisionBoxArray.length; @@ -667,6 +700,9 @@ function addSymbol(bucket: SymbolBucket, const iconBoxStartIndex = iconCollisionFeature ? iconCollisionFeature.boxStartIndex : bucket.collisionBoxArray.length; const iconBoxEndIndex = iconCollisionFeature ? iconCollisionFeature.boxEndIndex : bucket.collisionBoxArray.length; + const verticalIconBoxStartIndex = verticalIconCollisionFeature ? verticalIconCollisionFeature.boxStartIndex : bucket.collisionBoxArray.length; + const verticalIconBoxEndIndex = verticalIconCollisionFeature ? verticalIconCollisionFeature.boxEndIndex : bucket.collisionBoxArray.length; + if (bucket.glyphOffsetArray.length >= SymbolBucket.MAX_GLYPHS) warnOnce( "Too many glyphs being rendered in a tile. See https://github.com/mapbox/mapbox-gl-js/issues/2907" ); @@ -679,6 +715,7 @@ function addSymbol(bucket: SymbolBucket, placedTextSymbolIndices.left >= 0 ? placedTextSymbolIndices.left : -1, placedTextSymbolIndices.vertical || -1, placedIconSymbolIndex, + verticalPlacedIconSymbolIndex, key, textBoxStartIndex, textBoxEndIndex, @@ -686,10 +723,13 @@ function addSymbol(bucket: SymbolBucket, verticalTextBoxEndIndex, iconBoxStartIndex, iconBoxEndIndex, + verticalIconBoxStartIndex, + verticalIconBoxEndIndex, featureIndex, numHorizontalGlyphVertices, numVerticalGlyphVertices, numIconVertices, + numVerticalIconVertices, 0, textBoxScale, textOffset0, diff --git a/test/ignores.json b/test/ignores.json index c9e88b1e890..fbe4eac6243 100644 --- a/test/ignores.json +++ b/test/ignores.json @@ -14,7 +14,5 @@ "render-tests/fill-extrusion-pattern/tile-buffer": "https://github.com/mapbox/mapbox-gl-js/issues/4403", "render-tests/symbol-sort-key/text-ignore-placement": "skip - text drawn over icons", "render-tests/text-variable-anchor/remember-last-placement": "skip - not sure this is correct behavior", - "render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-horizontal-mode-icon-text-fit": "https://github.com/mapbox/mapbox-gl-js/issues/8583", - "render-tests/text-writing-mode/point_label/mixed-multiline-vertical-horizontal-mode-icon-text-fit": "https://github.com/mapbox/mapbox-gl-js/issues/8583", "render-tests/icon-image/icon-sdf-non-sdf-one-layer": "skip - render sdf icon and normal icon in one layer" }