diff --git a/build/generate-struct-arrays.js b/build/generate-struct-arrays.js index 071f41e738c..e60bb10369f 100644 --- a/build/generate-struct-arrays.js +++ b/build/generate-struct-arrays.js @@ -125,7 +125,7 @@ createStructArrayType('raster_bounds', rasterBoundsAttributes); const circleAttributes = require('../src/data/bucket/circle_attributes').default; const fillAttributes = require('../src/data/bucket/fill_attributes').default; -const fillExtrusionAttributes = require('../src/data/bucket/fill_extrusion_attributes').default ; +const fillExtrusionAttributes = require('../src/data/bucket/fill_extrusion_attributes').default; const lineAttributes = require('../src/data/bucket/line_attributes').default; // layout vertex arrays @@ -150,6 +150,7 @@ const { collisionCircleLayout, collisionVertexAttributes, placement, + symbolInstance, glyphOffset, lineVertex } = require('../src/data/bucket/symbol_attributes'); @@ -162,6 +163,7 @@ createStructArrayType(`collision_box_layout`, collisionBoxLayout); createStructArrayType(`collision_circle_layout`, collisionCircleLayout); createStructArrayType(`collision_vertex`, collisionVertexAttributes); createStructArrayType('placed_symbol', placement, true); +createStructArrayType('symbol_instance', symbolInstance, true); createStructArrayType('glyph_offset', glyphOffset, true); createStructArrayType('symbol_line_vertex', lineVertex, true); @@ -233,4 +235,3 @@ export { ${[...arrayTypeEntries].join(',\n ')} }; `); - diff --git a/src/data/array_types.js b/src/data/array_types.js index 635c0f83f10..c50a3c72285 100644 --- a/src/data/array_types.js +++ b/src/data/array_types.js @@ -520,6 +520,74 @@ StructArrayLayout2i2ui3ul3ui2f2ub40.prototype.bytesPerElement = 40; register('StructArrayLayout2i2ui3ul3ui2f2ub40', StructArrayLayout2i2ui3ul3ui2f2ub40); +/** + * Implementation of the StructArray layout: + * [0]: Int16[4] + * [8]: Uint16[9] + * [28]: Uint32[1] + * + * @private + */ +class StructArrayLayout4i9ui1ul32 extends StructArray { + uint8: Uint8Array; + int16: Int16Array; + uint16: Uint16Array; + uint32: Uint32Array; + + _refreshViews() { + this.uint8 = new Uint8Array(this.arrayBuffer); + this.int16 = new Int16Array(this.arrayBuffer); + this.uint16 = new Uint16Array(this.arrayBuffer); + this.uint32 = new Uint32Array(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) { + const i = this.length; + this.resize(i + 1); + const o2 = i * 16; + const o4 = i * 8; + this.int16[o2 + 0] = v0; + this.int16[o2 + 1] = v1; + this.int16[o2 + 2] = v2; + this.int16[o2 + 3] = v3; + this.uint16[o2 + 4] = v4; + this.uint16[o2 + 5] = v5; + this.uint16[o2 + 6] = v6; + this.uint16[o2 + 7] = v7; + this.uint16[o2 + 8] = v8; + this.uint16[o2 + 9] = v9; + this.uint16[o2 + 10] = v10; + this.uint16[o2 + 11] = v11; + this.uint16[o2 + 12] = v12; + this.uint32[o4 + 7] = v13; + return i; + } + + 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) { + const o2 = i * 16; + const o4 = i * 8; + this.int16[o2 + 0] = v0; + this.int16[o2 + 1] = v1; + this.int16[o2 + 2] = v2; + this.int16[o2 + 3] = v3; + this.uint16[o2 + 4] = v4; + this.uint16[o2 + 5] = v5; + this.uint16[o2 + 6] = v6; + this.uint16[o2 + 7] = v7; + this.uint16[o2 + 8] = v8; + this.uint16[o2 + 9] = v9; + this.uint16[o2 + 10] = v10; + this.uint16[o2 + 11] = v11; + this.uint16[o2 + 12] = v12; + this.uint32[o4 + 7] = v13; + return i; + } +} + +StructArrayLayout4i9ui1ul32.prototype.bytesPerElement = 32; +register('StructArrayLayout4i9ui1ul32', StructArrayLayout4i9ui1ul32); + + /** * Implementation of the StructArray layout: * [0]: Float32[1] @@ -732,6 +800,11 @@ class StructArrayLayout1ui2 extends StructArray { return i; } + emplace(i: number, v0: number) { + const o2 = i * 1; + this.uint16[o2 + 0] = v0; + return i; + } } StructArrayLayout1ui2.prototype.bytesPerElement = 2; @@ -941,6 +1014,73 @@ export class PlacedSymbolArray extends StructArrayLayout2i2ui3ul3ui2f2ub40 { register('PlacedSymbolArray', PlacedSymbolArray); +class SymbolInstanceStruct extends Struct { + _structArray: SymbolInstanceArray; + anchorX: number; + anchorY: number; + horizontalPlacedTextSymbolIndex: number; + verticalPlacedTextSymbolIndex: number; + key: number; + textBoxStartIndex: number; + textBoxEndIndex: number; + iconBoxStartIndex: number; + iconBoxEndIndex: number; + featureIndex: number; + numGlyphVertices: number; + numVerticalGlyphVertices: number; + numIconVertices: number; + crossTileID: number; + get anchorX() { return this._structArray.int16[this._pos2 + 0]; } + set anchorX(x) { this._structArray.int16[this._pos2 + 0] = x; } + get anchorY() { return this._structArray.int16[this._pos2 + 1]; } + set anchorY(x) { this._structArray.int16[this._pos2 + 1] = x; } + get horizontalPlacedTextSymbolIndex() { return this._structArray.int16[this._pos2 + 2]; } + set horizontalPlacedTextSymbolIndex(x) { this._structArray.int16[this._pos2 + 2] = x; } + get verticalPlacedTextSymbolIndex() { return this._structArray.int16[this._pos2 + 3]; } + set verticalPlacedTextSymbolIndex(x) { this._structArray.int16[this._pos2 + 3] = x; } + get key() { return this._structArray.uint16[this._pos2 + 4]; } + set key(x) { this._structArray.uint16[this._pos2 + 4] = x; } + get textBoxStartIndex() { return this._structArray.uint16[this._pos2 + 5]; } + set textBoxStartIndex(x) { this._structArray.uint16[this._pos2 + 5] = x; } + get textBoxEndIndex() { return this._structArray.uint16[this._pos2 + 6]; } + set textBoxEndIndex(x) { this._structArray.uint16[this._pos2 + 6] = x; } + get iconBoxStartIndex() { return this._structArray.uint16[this._pos2 + 7]; } + set iconBoxStartIndex(x) { this._structArray.uint16[this._pos2 + 7] = x; } + get iconBoxEndIndex() { return this._structArray.uint16[this._pos2 + 8]; } + set iconBoxEndIndex(x) { this._structArray.uint16[this._pos2 + 8] = x; } + get featureIndex() { return this._structArray.uint16[this._pos2 + 9]; } + set featureIndex(x) { this._structArray.uint16[this._pos2 + 9] = x; } + get numGlyphVertices() { return this._structArray.uint16[this._pos2 + 10]; } + set numGlyphVertices(x) { this._structArray.uint16[this._pos2 + 10] = x; } + get numVerticalGlyphVertices() { return this._structArray.uint16[this._pos2 + 11]; } + set numVerticalGlyphVertices(x) { this._structArray.uint16[this._pos2 + 11] = x; } + get numIconVertices() { return this._structArray.uint16[this._pos2 + 12]; } + set numIconVertices(x) { this._structArray.uint16[this._pos2 + 12] = x; } + get crossTileID() { return this._structArray.uint32[this._pos4 + 7]; } + set crossTileID(x) { this._structArray.uint32[this._pos4 + 7] = x; } +} + +SymbolInstanceStruct.prototype.size = 32; + +export type SymbolInstance = SymbolInstanceStruct; + + +/** + * @private + */ +export class SymbolInstanceArray extends StructArrayLayout4i9ui1ul32 { + /** + * Return the SymbolInstanceStruct at the given location in the array. + * @param {number} index The index of the element. + */ + get(index: number): SymbolInstanceStruct { + assert(!this.isTransferred); + return new SymbolInstanceStruct(this, index); + } +} + +register('SymbolInstanceArray', SymbolInstanceArray); + class GlyphOffsetStruct extends Struct { _structArray: GlyphOffsetArray; offsetX: number; @@ -1054,6 +1194,7 @@ export { StructArrayLayout2i2i2i12, StructArrayLayout2ub4, StructArrayLayout2i2ui3ul3ui2f2ub40, + StructArrayLayout4i9ui1ul32, StructArrayLayout1f4, StructArrayLayout3i6, StructArrayLayout1ul2ui8, diff --git a/src/data/bucket/symbol_attributes.js b/src/data/bucket/symbol_attributes.js index e76fd544175..ea07cbd61cb 100644 --- a/src/data/bucket/symbol_attributes.js +++ b/src/data/bucket/symbol_attributes.js @@ -73,6 +73,23 @@ export const placement = createLayout([ { type: 'Uint8', name: 'hidden' } ]); +export const symbolInstance = createLayout([ + { type: 'Int16', name: 'anchorX' }, + { type: 'Int16', name: 'anchorY' }, + { type: 'Int16', name: 'horizontalPlacedTextSymbolIndex' }, + { type: 'Int16', name: 'verticalPlacedTextSymbolIndex' }, + { type: 'Uint16', name: 'key' }, + { type: 'Uint16', name: 'textBoxStartIndex' }, + { type: 'Uint16', name: 'textBoxEndIndex' }, + { type: 'Uint16', name: 'iconBoxStartIndex' }, + { type: 'Uint16', name: 'iconBoxEndIndex' }, + { type: 'Uint16', name: 'featureIndex' }, + { type: 'Uint16', name: 'numGlyphVertices' }, + { type: 'Uint16', name: 'numVerticalGlyphVertices' }, + { type: 'Uint16', name: 'numIconVertices' }, + { type: 'Uint32', name: 'crossTileID' } +]); + export const glyphOffset = createLayout([ { type: 'Float32', name: 'offsetX' } ]); diff --git a/src/data/bucket/symbol_bucket.js b/src/data/bucket/symbol_bucket.js index cabedd1e85c..21152f93710 100644 --- a/src/data/bucket/symbol_bucket.js +++ b/src/data/bucket/symbol_bucket.js @@ -2,7 +2,7 @@ import { symbolLayoutAttributes, collisionVertexAttributes, collisionBoxLayout, collisionCircleLayout, dynamicLayoutAttributes } from './symbol_attributes'; -import { SymbolLayoutArray, SymbolDynamicLayoutArray, SymbolOpacityArray, CollisionBoxLayoutArray, CollisionCircleLayoutArray, CollisionVertexArray, PlacedSymbolArray, GlyphOffsetArray, SymbolLineVertexArray } from '../array_types'; +import { SymbolLayoutArray, SymbolDynamicLayoutArray, SymbolOpacityArray, CollisionBoxLayoutArray, CollisionCircleLayoutArray, CollisionVertexArray, PlacedSymbolArray, SymbolInstanceArray, GlyphOffsetArray, SymbolLineVertexArray } from '../array_types'; import Point from '@mapbox/point-geometry'; import SegmentVector from '../segment'; import { ProgramConfigurationSet } from '../program_configuration'; @@ -26,7 +26,7 @@ import type { IndexedFeature, PopulateParameters } from '../bucket'; -import type {CollisionBoxArray, CollisionBox} from '../array_types'; +import type {CollisionBoxArray, CollisionBox, SymbolInstance} from '../array_types'; import type { StructArray, StructArrayMember } from '../../util/struct_array'; import type SymbolStyleLayer from '../../style/style_layer/symbol_style_layer'; import type Context from '../../gl/context'; @@ -64,29 +64,6 @@ export type SymbolFeature = {| id?: any |}; -export type SymbolInstance = { - key: number, - textBoxStartIndex: number, - textBoxEndIndex: number, - iconBoxStartIndex: number, - iconBoxEndIndex: number, - anchor: Anchor, - featureIndex: number, - textCollisionFeature?: {boxStartIndex: number, boxEndIndex: number}, - iconCollisionFeature?: {boxStartIndex: number, boxEndIndex: number}, - horizontalPlacedTextSymbolIndex: number; - verticalPlacedTextSymbolIndex: number; - numGlyphVertices: number; - numVerticalGlyphVertices: number; - numIconVertices: number; - // Populated/modified on foreground during placement - crossTileID: number; - collisionArrays?: CollisionArrays; - placedText?: boolean; - placedIcon?: boolean; - hidden?: boolean; -}; - // Opacity arrays are frequently updated but don't contain a lot of information, so we pack them // tight. Each Uint32 is actually four duplicate Uint8s for the four corners of a glyph // 7 bits are for the current opacity, and the lowest bit is the target opacity @@ -275,7 +252,8 @@ class SymbolBucket implements Bucket { glyphOffsetArray: GlyphOffsetArray; lineVertexArray: SymbolLineVertexArray; features: Array; - symbolInstances: Array; + symbolInstances: SymbolInstanceArray; + collisionArrays: Array; pixelRatio: number; tilePixelRatio: number; compareText: {[string]: Array}; @@ -324,6 +302,7 @@ class SymbolBucket implements Bucket { this.glyphOffsetArray = new GlyphOffsetArray(); this.lineVertexArray = new SymbolLineVertexArray(); + this.symbolInstances = new SymbolInstanceArray(); } calculateGlyphDependencies(text: string, stack: {[number]: boolean}, textAlongLine: boolean, doesAllowVerticalWritingMode: boolean) { @@ -539,15 +518,15 @@ class SymbolBucket implements Bucket { arrays.programConfigurations.populatePaintArrays(arrays.layoutVertexArray.length, feature, feature.index); } - _addCollisionDebugVertex(layoutVertexArray: StructArray, collisionVertexArray: StructArray, point: Point, anchor: Point, extrude: Point) { + _addCollisionDebugVertex(layoutVertexArray: StructArray, collisionVertexArray: StructArray, point: Point, anchorX: number, anchorY: number, extrude: Point) { collisionVertexArray.emplaceBack(0, 0); return layoutVertexArray.emplaceBack( // pos point.x, point.y, // a_anchor_pos - anchor.x, - anchor.y, + anchorX, + anchorY, // extrude Math.round(extrude.x), Math.round(extrude.y)); @@ -561,10 +540,13 @@ class SymbolBucket implements Bucket { const layoutVertexArray = arrays.layoutVertexArray; const collisionVertexArray = arrays.collisionVertexArray; - this._addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, boxAnchorPoint, symbolInstance.anchor, new Point(x1, y1)); - this._addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, boxAnchorPoint, symbolInstance.anchor, new Point(x2, y1)); - this._addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, boxAnchorPoint, symbolInstance.anchor, new Point(x2, y2)); - this._addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, boxAnchorPoint, symbolInstance.anchor, new Point(x1, y2)); + const anchorX = symbolInstance.anchorX; + const anchorY = symbolInstance.anchorY; + + this._addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, boxAnchorPoint, anchorX, anchorY, new Point(x1, y1)); + this._addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, boxAnchorPoint, anchorX, anchorY, new Point(x2, y1)); + this._addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, boxAnchorPoint, anchorX, anchorY, new Point(x2, y2)); + this._addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, boxAnchorPoint, anchorX, anchorY, new Point(x1, y2)); segment.vertexLength += 4; if (isCircle) { @@ -584,34 +566,32 @@ class SymbolBucket implements Bucket { } } + addDebugCollisionBoxes(startIndex: number, endIndex: number, symbolInstance: SymbolInstance) { + for (let b = startIndex; b < endIndex; b++) { + const box: CollisionBox = (this.collisionBoxArray.get(b): any); + const x1 = box.x1; + const y1 = box.y1; + const x2 = box.x2; + const y2 = box.y2; + + // If the radius > 0, this collision box is actually a circle + // The data we add to the buffers is exactly the same, but we'll render with a different shader. + const isCircle = box.radius > 0; + this.addCollisionDebugVertices(x1, y1, x2, y2, isCircle ? this.collisionCircle : this.collisionBox, box.anchorPoint, symbolInstance, isCircle); + } + } + generateCollisionDebugBuffers() { - for (const symbolInstance of this.symbolInstances) { - symbolInstance.textCollisionFeature = {boxStartIndex: symbolInstance.textBoxStartIndex, boxEndIndex: symbolInstance.textBoxEndIndex}; - symbolInstance.iconCollisionFeature = {boxStartIndex: symbolInstance.iconBoxStartIndex, boxEndIndex: symbolInstance.iconBoxEndIndex}; - - for (let i = 0; i < 2; i++) { - const feature = symbolInstance[i === 0 ? 'textCollisionFeature' : 'iconCollisionFeature']; - if (!feature) continue; - - for (let b = feature.boxStartIndex; b < feature.boxEndIndex; b++) { - const box: CollisionBox = (this.collisionBoxArray.get(b): any); - const x1 = box.x1; - const y1 = box.y1; - const x2 = box.x2; - const y2 = box.y2; - - // If the radius > 0, this collision box is actually a circle - // The data we add to the buffers is exactly the same, but we'll render with a different shader. - const isCircle = box.radius > 0; - this.addCollisionDebugVertices(x1, y1, x2, y2, isCircle ? this.collisionCircle : this.collisionBox, box.anchorPoint, symbolInstance, isCircle); - } - } + for (let i = 0; i < this.symbolInstances.length; i++) { + const symbolInstance = this.symbolInstances.get(i); + this.addDebugCollisionBoxes(symbolInstance.textBoxStartIndex, symbolInstance.textBoxEndIndex, symbolInstance); + this.addDebugCollisionBoxes(symbolInstance.iconBoxStartIndex, symbolInstance.iconBoxEndIndex, symbolInstance); } } // These flat arrays are meant to be quicker to iterate over than the source // CollisionBoxArray - deserializeCollisionBoxes(collisionBoxArray: CollisionBoxArray, textStartIndex: number, textEndIndex: number, iconStartIndex: number, iconEndIndex: number): CollisionArrays { + _deserializeCollisionBoxesForSymbol(collisionBoxArray: CollisionBoxArray, textStartIndex: number, textEndIndex: number, iconStartIndex: number, iconEndIndex: number): CollisionArrays { const collisionArrays = {}; for (let k = textStartIndex; k < textEndIndex; k++) { const box: CollisionBox = (collisionBoxArray.get(k): any); @@ -640,6 +620,20 @@ class SymbolBucket implements Bucket { return collisionArrays; } + deserializeCollisionBoxes(collisionBoxArray: CollisionBoxArray) { + this.collisionArrays = []; + for (let i = 0; i < this.symbolInstances.length; i++) { + const symbolInstance = this.symbolInstances.get(i); + this.collisionArrays.push(this._deserializeCollisionBoxesForSymbol( + collisionBoxArray, + symbolInstance.textBoxStartIndex, + symbolInstance.textBoxEndIndex, + symbolInstance.iconBoxStartIndex, + symbolInstance.iconBoxEndIndex + )); + } + } + hasTextData() { return this.text.segments.get().length > 0; } @@ -690,10 +684,10 @@ class SymbolBucket implements Bucket { cos = Math.cos(angle); symbolInstanceIndexes.sort((aIndex, bIndex) => { - const a = this.symbolInstances[aIndex]; - const b = this.symbolInstances[bIndex]; - const aRotated = Math.round(sin * a.anchor.x + cos * a.anchor.y) | 0; - const bRotated = Math.round(sin * b.anchor.x + cos * b.anchor.y) | 0; + const a = this.symbolInstances.get(aIndex); + const b = this.symbolInstances.get(bIndex); + const aRotated = Math.round(sin * a.anchorX + cos * a.anchorY) | 0; + const bRotated = Math.round(sin * b.anchorX + cos * b.anchorY) | 0; return (aRotated - bRotated) || (b.featureIndex - a.featureIndex); }); @@ -703,7 +697,7 @@ class SymbolBucket implements Bucket { this.featureSortOrder = []; for (const i of symbolInstanceIndexes) { - const symbolInstance = this.symbolInstances[i]; + const symbolInstance = this.symbolInstances.get(i); this.featureSortOrder.push(symbolInstance.featureIndex); if (symbolInstance.horizontalPlacedTextSymbolIndex >= 0) { @@ -727,8 +721,7 @@ class SymbolBucket implements Bucket { } register('SymbolBucket', SymbolBucket, { - omit: ['layers', 'collisionBoxArray', 'features', 'compareText'], - shallow: ['symbolInstances'] + omit: ['layers', 'collisionBoxArray', 'features', 'compareText'] }); // this constant is based on the size of StructArray indexes used in a symbol diff --git a/src/symbol/cross_tile_symbol_index.js b/src/symbol/cross_tile_symbol_index.js index 17fde69712e..ade2eacdbc3 100644 --- a/src/symbol/cross_tile_symbol_index.js +++ b/src/symbol/cross_tile_symbol_index.js @@ -2,8 +2,11 @@ import EXTENT from '../data/extent'; +import { SymbolInstanceArray } from '../data/array_types'; + +import type { SymbolInstance } from '../data/array_types'; import type {OverscaledTileID} from '../source/tile_id'; -import type SymbolBucket, {SymbolInstance} from '../data/bucket/symbol_bucket'; +import type SymbolBucket from '../data/bucket/symbol_bucket'; import type StyleLayer from '../style/style_layer'; import type Tile from '../source/tile'; @@ -35,12 +38,13 @@ class TileLayerIndex { }>}; bucketInstanceId: number; - constructor(tileID: OverscaledTileID, symbolInstances: Array, bucketInstanceId: number) { + constructor(tileID: OverscaledTileID, symbolInstances: SymbolInstanceArray, bucketInstanceId: number) { this.tileID = tileID; this.indexedSymbolInstances = {}; this.bucketInstanceId = bucketInstanceId; - for (const symbolInstance of symbolInstances) { + for (let i = 0; i < symbolInstances.length; i++) { + const symbolInstance = symbolInstances.get(i); const key = symbolInstance.key; if (!this.indexedSymbolInstances[key]) { this.indexedSymbolInstances[key] = []; @@ -63,17 +67,17 @@ class TileLayerIndex { getScaledCoordinates(symbolInstance: SymbolInstance, childTileID: OverscaledTileID) { const zDifference = childTileID.canonical.z - this.tileID.canonical.z; const scale = roundingFactor / Math.pow(2, zDifference); - const anchor = symbolInstance.anchor; return { - x: Math.floor((childTileID.canonical.x * EXTENT + anchor.x) * scale), - y: Math.floor((childTileID.canonical.y * EXTENT + anchor.y) * scale) + x: Math.floor((childTileID.canonical.x * EXTENT + symbolInstance.anchorX) * scale), + y: Math.floor((childTileID.canonical.y * EXTENT + symbolInstance.anchorY) * scale) }; } - findMatches(symbolInstances: Array, newTileID: OverscaledTileID, zoomCrossTileIDs: {[crossTileID: number]: boolean}) { + findMatches(symbolInstances: SymbolInstanceArray, newTileID: OverscaledTileID, zoomCrossTileIDs: {[crossTileID: number]: boolean}) { const tolerance = this.tileID.canonical.z < newTileID.canonical.z ? 1 : Math.pow(2, this.tileID.canonical.z - newTileID.canonical.z); - for (const symbolInstance of symbolInstances) { + for (let i = 0; i < symbolInstances.length; i++) { + const symbolInstance = symbolInstances.get(i); if (symbolInstance.crossTileID) { // already has a match, skip continue; @@ -166,7 +170,8 @@ class CrossTileSymbolLayerIndex { } } - for (const symbolInstance of bucket.symbolInstances) { + for (let i = 0; i < bucket.symbolInstances.length; i++) { + const symbolInstance = bucket.symbolInstances.get(i); symbolInstance.crossTileID = 0; } @@ -193,7 +198,8 @@ class CrossTileSymbolLayerIndex { } } - for (const symbolInstance of bucket.symbolInstances) { + for (let i = 0; i < bucket.symbolInstances.length; i++) { + const symbolInstance = bucket.symbolInstances.get(i); if (!symbolInstance.crossTileID) { // symbol did not match any known symbol, assign a new id symbolInstance.crossTileID = crossTileIDs.generate(); diff --git a/src/symbol/placement.js b/src/symbol/placement.js index 9476434b97f..607f5316844 100644 --- a/src/symbol/placement.js +++ b/src/symbol/placement.js @@ -190,7 +190,12 @@ export class Placement { const collisionGroup = this.collisionGroups.get(bucket.sourceID); - for (const symbolInstance of bucket.symbolInstances) { + if (!bucket.collisionArrays && collisionBoxArray) { + bucket.deserializeCollisionBoxes(collisionBoxArray); + } + + for (let i = 0; i < bucket.symbolInstances.length; i++) { + const symbolInstance = bucket.symbolInstances.get(i); if (!seenCrossTileIDs[symbolInstance.crossTileID]) { if (holdingForFade) { // Mark all symbols from this tile as "not placed", but don't add to seenCrossTileIDs, because we don't @@ -210,22 +215,18 @@ export class Placement { let textFeatureIndex = 0; let iconFeatureIndex = 0; - if (!symbolInstance.collisionArrays) { - symbolInstance.collisionArrays = bucket.deserializeCollisionBoxes( - ((collisionBoxArray: any): CollisionBoxArray), - symbolInstance.textBoxStartIndex, symbolInstance.textBoxEndIndex, symbolInstance.iconBoxStartIndex, symbolInstance.iconBoxEndIndex); - } + const collisionArrays = bucket.collisionArrays[i]; - if (symbolInstance.collisionArrays.textFeatureIndex) { - textFeatureIndex = symbolInstance.collisionArrays.textFeatureIndex; + if (collisionArrays.textFeatureIndex) { + textFeatureIndex = collisionArrays.textFeatureIndex; } - if (symbolInstance.collisionArrays.textBox) { - placedGlyphBoxes = this.collisionIndex.placeCollisionBox(symbolInstance.collisionArrays.textBox, + if (collisionArrays.textBox) { + placedGlyphBoxes = this.collisionIndex.placeCollisionBox(collisionArrays.textBox, layout.get('text-allow-overlap'), textPixelRatio, posMatrix, collisionGroup.predicate); placeText = placedGlyphBoxes.box.length > 0; offscreen = offscreen && placedGlyphBoxes.offscreen; } - const textCircles = symbolInstance.collisionArrays.textCircles; + const textCircles = collisionArrays.textCircles; if (textCircles) { const placedSymbol = bucket.text.placedSymbolArray.get(symbolInstance.horizontalPlacedTextSymbolIndex); const fontSize = symbolSize.evaluateSizeForFeature(bucket.textSizeData, partiallyEvaluatedTextSize, placedSymbol); @@ -250,11 +251,11 @@ export class Placement { offscreen = offscreen && placedGlyphCircles.offscreen; } - if (symbolInstance.collisionArrays.iconFeatureIndex) { - iconFeatureIndex = symbolInstance.collisionArrays.iconFeatureIndex; + if (collisionArrays.iconFeatureIndex) { + iconFeatureIndex = collisionArrays.iconFeatureIndex; } - if (symbolInstance.collisionArrays.iconBox) { - placedIconBoxes = this.collisionIndex.placeCollisionBox(symbolInstance.collisionArrays.iconBox, + if (collisionArrays.iconBox) { + placedIconBoxes = this.collisionIndex.placeCollisionBox(collisionArrays.iconBox, layout.get('icon-allow-overlap'), textPixelRatio, posMatrix, collisionGroup.predicate); placeIcon = placedIconBoxes.box.length > 0; offscreen = offscreen && placedIconBoxes.offscreen; @@ -375,8 +376,12 @@ export class Placement { iconAllowOverlap && (textAllowOverlap || !bucket.hasTextData() || layout.get('text-optional')), true); + if (!bucket.collisionArrays && collisionBoxArray && (bucket.hasCollisionBoxData() || bucket.hasCollisionCircleData())) { + bucket.deserializeCollisionBoxes(collisionBoxArray); + } + for (let s = 0; s < bucket.symbolInstances.length; s++) { - const symbolInstance = bucket.symbolInstances[s]; + const symbolInstance = bucket.symbolInstances.get(s); const isDuplicate = seenCrossTileIDs[symbolInstance.crossTileID]; let opacityState = this.opacities[symbolInstance.crossTileID]; @@ -417,31 +422,27 @@ export class Placement { for (let i = 0; i < symbolInstance.numIconVertices / 4; i++) { bucket.icon.opacityVertexArray.emplaceBack(packedOpacity); } - const placedSymbol = bucket.icon.placedSymbolArray.get(s); - placedSymbol.hidden = (opacityState.icon.isHidden(): any); + bucket.icon.placedSymbolArray.get(s).hidden = + (opacityState.icon.isHidden(): any); } - if (!symbolInstance.collisionArrays) { - symbolInstance.collisionArrays = bucket.deserializeCollisionBoxes( - ((collisionBoxArray: any): CollisionBoxArray), - symbolInstance.textBoxStartIndex, symbolInstance.textBoxEndIndex, symbolInstance.iconBoxStartIndex, symbolInstance.iconBoxEndIndex); - } - - const collisionArrays = symbolInstance.collisionArrays; - if (collisionArrays) { - if (collisionArrays.textBox && bucket.hasCollisionBoxData()) { - updateCollisionVertices(bucket.collisionBox.collisionVertexArray, opacityState.text.placed, false); - } + if (bucket.hasCollisionBoxData() || bucket.hasCollisionCircleData()) { + const collisionArrays = bucket.collisionArrays[s]; + if (collisionArrays) { + if (collisionArrays.textBox) { + updateCollisionVertices(bucket.collisionBox.collisionVertexArray, opacityState.text.placed, false); + } - if (collisionArrays.iconBox && bucket.hasCollisionBoxData()) { - updateCollisionVertices(bucket.collisionBox.collisionVertexArray, opacityState.icon.placed, false); - } + if (collisionArrays.iconBox) { + updateCollisionVertices(bucket.collisionBox.collisionVertexArray, opacityState.icon.placed, false); + } - const textCircles = collisionArrays.textCircles; - if (textCircles && bucket.hasCollisionCircleData()) { - for (let k = 0; k < textCircles.length; k += 5) { - const notUsed = isDuplicate || textCircles[k + 4] === 0; - updateCollisionVertices(bucket.collisionCircle.collisionVertexArray, opacityState.text.placed, notUsed); + const textCircles = collisionArrays.textCircles; + if (textCircles && bucket.hasCollisionCircleData()) { + for (let k = 0; k < textCircles.length; k += 5) { + const notUsed = isDuplicate || textCircles[k + 4] === 0; + updateCollisionVertices(bucket.collisionCircle.collisionVertexArray, opacityState.text.placed, notUsed); + } } } } diff --git a/src/symbol/symbol_layout.js b/src/symbol/symbol_layout.js index ec6ccb95969..8aef10f1228 100644 --- a/src/symbol/symbol_layout.js +++ b/src/symbol/symbol_layout.js @@ -62,7 +62,6 @@ export function performSymbolLayout(bucket: SymbolBucket, imagePositions: {[string]: ImagePosition}, showCollisionBoxes: boolean) { bucket.createArrays(); - bucket.symbolInstances = []; const tileSize = 512 * bucket.overscaling; bucket.tilePixelRatio = EXTENT / tileSize; @@ -206,11 +205,11 @@ function addFeature(bucket: SymbolBucket, return; } - bucket.symbolInstances.push(addSymbol(bucket, anchor, line, shapedTextOrientations, shapedIcon, bucket.layers[0], + addSymbol(bucket, anchor, line, shapedTextOrientations, shapedIcon, bucket.layers[0], bucket.collisionBoxArray, feature.index, feature.sourceLayerIndex, bucket.index, textBoxScale, textPadding, textAlongLine, textOffset, iconBoxScale, iconPadding, iconAlongLine, iconOffset, - feature, glyphPositionMap, sizes)); + feature, glyphPositionMap, sizes); }; if (symbolPlacement === 'line') { @@ -426,23 +425,21 @@ function addSymbol(bucket: SymbolBucket, "Too many glyphs being rendered in a tile. See https://github.com/mapbox/mapbox-gl-js/issues/2907" ); - return { + bucket.symbolInstances.emplaceBack( + anchor.x, + anchor.y, + placedTextSymbolIndices.length > 0 ? placedTextSymbolIndices[0] : -1, + placedTextSymbolIndices.length > 1 ? placedTextSymbolIndices[1] : -1, key, textBoxStartIndex, textBoxEndIndex, iconBoxStartIndex, iconBoxEndIndex, - anchor, featureIndex, numGlyphVertices, numVerticalGlyphVertices, numIconVertices, - horizontalPlacedTextSymbolIndex: - placedTextSymbolIndices.length > 0 ? placedTextSymbolIndices[0] : -1, - verticalPlacedTextSymbolIndex: - placedTextSymbolIndices.length > 1 ? placedTextSymbolIndices[1] : -1, - crossTileID: 0 - }; + 0); } function anchorIsTooClose(bucket: any, text: string, repeatDistance: number, anchor: Point) { diff --git a/test/unit/symbol/cross_tile_symbol_index.js b/test/unit/symbol/cross_tile_symbol_index.js index 0e42dabb76b..659a02a6db9 100644 --- a/test/unit/symbol/cross_tile_symbol_index.js +++ b/test/unit/symbol/cross_tile_symbol_index.js @@ -1,5 +1,4 @@ import { test } from 'mapbox-gl-js-test'; -import Anchor from '../../../src/symbol/anchor'; import CrossTileSymbolIndex from '../../../src/symbol/cross_tile_symbol_index'; import { OverscaledTileID } from '../../../src/source/tile_id'; @@ -9,14 +8,18 @@ const styleLayer = { function makeSymbolInstance(x, y, key) { return { - anchor: new Anchor(x, y), + anchorX: x, + anchorY: y, key: key }; } function makeTile(tileID, symbolInstances) { const bucket = { - symbolInstances: symbolInstances, + symbolInstances: { + get: function(i) { return symbolInstances[i]; }, + length: symbolInstances.length + }, layerIds: ['test'] }; return {