@mapwhit/tilerenderer 0.48.0 → 0.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,85 +3,64 @@ const assert = require('assert');
3
3
  module.exports = convertFunction;
4
4
 
5
5
  function convertFunction(parameters, propertySpec) {
6
- let expression;
7
-
8
- parameters = { ...parameters };
9
- let defaultExpression;
10
- if (typeof parameters.default !== 'undefined') {
11
- defaultExpression = convertValue(parameters.default, propertySpec);
12
- } else {
13
- defaultExpression = convertValue(propertySpec.default, propertySpec);
14
- if (defaultExpression === null) {
15
- defaultExpression = ['error', 'No default property value available.'];
16
- }
6
+ let stops = parameters.stops;
7
+ if (!stops) {
8
+ // identity function
9
+ return convertIdentityFunction(parameters, propertySpec);
17
10
  }
18
11
 
19
- if (parameters.stops) {
20
- const zoomAndFeatureDependent = parameters.stops && typeof parameters.stops[0][0] === 'object';
21
- const featureDependent = zoomAndFeatureDependent || parameters.property !== undefined;
22
- const zoomDependent = zoomAndFeatureDependent || !featureDependent;
23
-
24
- const stops = parameters.stops.map(stop => {
25
- if (!featureDependent && propertySpec.tokens && typeof stop[1] === 'string') {
26
- return [stop[0], convertTokenString(stop[1])];
27
- }
28
- return [stop[0], convertValue(stop[1], propertySpec)];
29
- });
12
+ const zoomAndFeatureDependent = stops && typeof stops[0][0] === 'object';
13
+ const featureDependent = zoomAndFeatureDependent || parameters.property !== undefined;
14
+ const zoomDependent = zoomAndFeatureDependent || !featureDependent;
30
15
 
31
- if (parameters.colorSpace && parameters.colorSpace !== 'rgb') {
32
- throw new Error('Unimplemented');
16
+ stops = stops.map(stop => {
17
+ if (!featureDependent && propertySpec.tokens && typeof stop[1] === 'string') {
18
+ return [stop[0], convertTokenString(stop[1])];
33
19
  }
20
+ return [stop[0], ['literal', stop[1]]];
21
+ });
34
22
 
35
- if (zoomAndFeatureDependent) {
36
- expression = convertZoomAndPropertyFunction(parameters, propertySpec, stops, defaultExpression);
37
- } else if (zoomDependent) {
38
- expression = convertZoomFunction(parameters, propertySpec, stops);
39
- } else {
40
- expression = convertPropertyFunction(parameters, propertySpec, stops, defaultExpression);
41
- }
42
- } else {
43
- // identity function
44
- expression = convertIdentityFunction(parameters, propertySpec, defaultExpression);
23
+ if (zoomAndFeatureDependent) {
24
+ return convertZoomAndPropertyFunction(parameters, propertySpec, stops);
45
25
  }
46
-
47
- return expression;
26
+ if (zoomDependent) {
27
+ return convertZoomFunction(parameters, propertySpec, stops);
28
+ }
29
+ return convertPropertyFunction(parameters, propertySpec, stops);
48
30
  }
49
31
 
50
- function convertIdentityFunction(parameters, propertySpec, defaultExpression) {
32
+ function convertIdentityFunction(parameters, propertySpec) {
51
33
  const get = ['get', parameters.property];
52
34
 
53
- if (propertySpec.type === 'color') {
54
- return parameters.default === undefined ? get : ['to-color', get, parameters.default];
35
+ if (parameters.default === undefined) {
36
+ return get;
55
37
  }
56
- if (propertySpec.type === 'array' && typeof propertySpec.length === 'number') {
57
- return ['array', propertySpec.value, propertySpec.length, get];
38
+ if (propertySpec.type === 'enum') {
39
+ return ['match', get, Object.keys(propertySpec.values), get, parameters.default];
58
40
  }
41
+ const expression = [
42
+ propertySpec.type === 'color' ? 'to-color' : propertySpec.type,
43
+ get,
44
+ ['literal', parameters.default]
45
+ ];
59
46
  if (propertySpec.type === 'array') {
60
- return ['array', propertySpec.value, get];
61
- }
62
- if (propertySpec.type === 'enum') {
63
- return [
64
- 'let',
65
- 'property_value',
66
- ['string', get],
67
- ['match', ['var', 'property_value'], propertySpec.values, ['var', 'property_value'], defaultExpression]
68
- ];
47
+ expression.splice(1, 0, propertySpec.value, propertySpec.length || null);
69
48
  }
70
- return parameters.default === undefined ? get : [propertySpec.type, get, parameters.default];
49
+ return expression;
71
50
  }
72
51
 
73
- function convertValue(value, spec) {
74
- if (typeof value === 'undefined' || value === null) return null;
75
- if (spec.type === 'color') {
76
- return value;
77
- }
78
- if (spec.type === 'array') {
79
- return ['literal', value];
52
+ function getInterpolateOperator(parameters) {
53
+ switch (parameters.colorSpace) {
54
+ case 'hcl':
55
+ return 'interpolate-hcl';
56
+ case 'lab':
57
+ return 'interpolate-lab';
58
+ default:
59
+ return 'interpolate';
80
60
  }
81
- return value;
82
61
  }
83
62
 
84
- function convertZoomAndPropertyFunction(parameters, propertySpec, stops, defaultExpression) {
63
+ function convertZoomAndPropertyFunction(parameters, propertySpec, stops) {
85
64
  const featureFunctionParameters = {};
86
65
  const featureFunctionStops = {};
87
66
  const zoomStops = [];
@@ -107,15 +86,10 @@ function convertZoomAndPropertyFunction(parameters, propertySpec, stops, default
107
86
  // otherwise.
108
87
  const functionType = getFunctionType({}, propertySpec);
109
88
  if (functionType === 'exponential') {
110
- const expression = ['interpolate', ['linear'], ['zoom']];
89
+ const expression = [getInterpolateOperator(parameters), ['linear'], ['zoom']];
111
90
 
112
91
  for (const z of zoomStops) {
113
- const output = convertPropertyFunction(
114
- featureFunctionParameters[z],
115
- propertySpec,
116
- featureFunctionStops[z],
117
- defaultExpression
118
- );
92
+ const output = convertPropertyFunction(featureFunctionParameters[z], propertySpec, featureFunctionStops[z]);
119
93
  appendStopPair(expression, z, output, false);
120
94
  }
121
95
 
@@ -124,12 +98,7 @@ function convertZoomAndPropertyFunction(parameters, propertySpec, stops, default
124
98
  const expression = ['step', ['zoom']];
125
99
 
126
100
  for (const z of zoomStops) {
127
- const output = convertPropertyFunction(
128
- featureFunctionParameters[z],
129
- propertySpec,
130
- featureFunctionStops[z],
131
- defaultExpression
132
- );
101
+ const output = convertPropertyFunction(featureFunctionParameters[z], propertySpec, featureFunctionStops[z]);
133
102
  appendStopPair(expression, z, output, true);
134
103
  }
135
104
 
@@ -138,43 +107,52 @@ function convertZoomAndPropertyFunction(parameters, propertySpec, stops, default
138
107
  return expression;
139
108
  }
140
109
 
141
- function convertPropertyFunction(parameters, propertySpec, stops, defaultExpression) {
142
- const type = getFunctionType(parameters, propertySpec);
110
+ function coalesce(a, b) {
111
+ if (a !== undefined) return a;
112
+ if (b !== undefined) return b;
113
+ }
143
114
 
144
- let expression;
145
- let isStep = false;
115
+ function convertPropertyFunction(parameters, propertySpec, stops) {
116
+ const type = getFunctionType(parameters, propertySpec);
117
+ const get = ['get', parameters.property];
146
118
  if (type === 'categorical' && typeof stops[0][0] === 'boolean') {
147
119
  assert(parameters.stops.length > 0 && parameters.stops.length <= 2);
148
- expression = ['case'];
120
+ const expression = ['case'];
149
121
  for (const stop of stops) {
150
- expression.push(['==', ['get', parameters.property], stop[0]], stop[1]);
122
+ expression.push(['==', get, stop[0]], stop[1]);
151
123
  }
152
- expression.push(defaultExpression);
124
+ expression.push(['literal', coalesce(parameters.default, propertySpec.default)]);
153
125
  return expression;
154
126
  }
155
127
  if (type === 'categorical') {
156
- expression = ['match', ['get', parameters.property]];
157
- } else if (type === 'interval') {
158
- expression = ['step', ['number', ['get', parameters.property]]];
159
- isStep = true;
160
- } else if (type === 'exponential') {
161
- const base = parameters.base !== undefined ? parameters.base : 1;
162
- expression = ['interpolate', ['exponential', base], ['number', ['get', parameters.property]]];
163
- } else {
164
- throw new Error(`Unknown property function type ${type}`);
128
+ const expression = ['match', get];
129
+ for (const stop of stops) {
130
+ appendStopPair(expression, stop[0], stop[1], false);
131
+ }
132
+ expression.push(['literal', coalesce(parameters.default, propertySpec.default)]);
133
+ return expression;
165
134
  }
166
-
167
- for (const stop of stops) {
168
- appendStopPair(expression, stop[0], stop[1], isStep);
135
+ if (type === 'interval') {
136
+ const expression = ['step', ['number', get]];
137
+ for (const stop of stops) {
138
+ appendStopPair(expression, stop[0], stop[1], true);
139
+ }
140
+ fixupDegenerateStepCurve(expression);
141
+ return parameters.default === undefined
142
+ ? expression
143
+ : ['case', ['==', ['typeof', get], 'number'], expression, ['literal', parameters.default]];
169
144
  }
170
-
171
- if (expression[0] === 'match') {
172
- expression.push(defaultExpression);
145
+ if (type === 'exponential') {
146
+ const base = parameters.base !== undefined ? parameters.base : 1;
147
+ const expression = [getInterpolateOperator(parameters), ['exponential', base], ['number', get]];
148
+ for (const stop of stops) {
149
+ appendStopPair(expression, stop[0], stop[1], false);
150
+ }
151
+ return parameters.default === undefined
152
+ ? expression
153
+ : ['case', ['==', ['typeof', get], 'number'], expression, ['literal', parameters.default]];
173
154
  }
174
-
175
- fixupDegenerateStepCurve(expression);
176
-
177
- return expression;
155
+ throw new Error(`Unknown property function type ${type}`);
178
156
  }
179
157
 
180
158
  function convertZoomFunction(parameters, propertySpec, stops, input = ['zoom']) {
@@ -186,7 +164,7 @@ function convertZoomFunction(parameters, propertySpec, stops, input = ['zoom'])
186
164
  isStep = true;
187
165
  } else if (type === 'exponential') {
188
166
  const base = parameters.base !== undefined ? parameters.base : 1;
189
- expression = ['interpolate', ['exponential', base], input];
167
+ expression = [getInterpolateOperator(parameters), ['exponential', base], input];
190
168
  } else {
191
169
  throw new Error(`Unknown zoom function type "${type}"`);
192
170
  }
@@ -70,12 +70,12 @@
70
70
  },
71
71
  "sprite": {
72
72
  "type": "string",
73
- "doc": "A base URL for retrieving the sprite image and metadata. The extensions `.png`, `.json` and scale factor `@2x.png` will be automatically appended. This property is required if any layer uses the `background-pattern`, `fill-pattern`, `line-pattern`, `fill-extrusion-pattern`, or `icon-image` properties.",
73
+ "doc": "A base URL for retrieving the sprite image and metadata. The extensions `.png`, `.json` and scale factor `@2x.png` will be automatically appended. This property is required if any layer uses the `background-pattern`, `fill-pattern`, `line-pattern`, `fill-extrusion-pattern`, or `icon-image` properties. The URL must be absolute, containing the [scheme, authority and path components](https://en.wikipedia.org/wiki/URL#Syntax).",
74
74
  "example": "mapbox://sprites/mapbox/bright-v8"
75
75
  },
76
76
  "glyphs": {
77
77
  "type": "string",
78
- "doc": "A URL template for loading signed-distance-field glyph sets in PBF format. The URL must include `{fontstack}` and `{range}` tokens. This property is required if any layer uses the `text-field` layout property.",
78
+ "doc": "A URL template for loading signed-distance-field glyph sets in PBF format. The URL must include `{fontstack}` and `{range}` tokens. This property is required if any layer uses the `text-field` layout property. The URL must be absolute, containing the [scheme, authority and path components](https://en.wikipedia.org/wiki/URL#Syntax).",
79
79
  "example": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf"
80
80
  },
81
81
  "transition": {
@@ -946,6 +946,32 @@
946
946
  },
947
947
  "property-type": "data-constant"
948
948
  },
949
+ "symbol-z-order": {
950
+ "type": "enum",
951
+ "values": {
952
+ "viewport-y": {
953
+ "doc": "Symbols will be sorted by their y-position relative to the viewport."
954
+ },
955
+ "source": {
956
+ "doc": "Symbols will be rendered in the same order as the source data with no sorting applied."
957
+ }
958
+ },
959
+ "default": "viewport-y",
960
+ "doc": "Controls the order in which overlapping symbols in the same layer are rendered",
961
+ "sdk-support": {
962
+ "basic functionality": {
963
+ "js": "0.49.0"
964
+ },
965
+ "data-driven styling": {}
966
+ },
967
+ "expression": {
968
+ "interpolated": false,
969
+ "parameters": [
970
+ "zoom"
971
+ ]
972
+ },
973
+ "property-type": "data-constant"
974
+ },
949
975
  "icon-allow-overlap": {
950
976
  "type": "boolean",
951
977
  "default": false,
@@ -3458,7 +3484,10 @@
3458
3484
  "macos": "0.1.0"
3459
3485
  },
3460
3486
  "data-driven styling": {
3461
- "js": "0.46.0"
3487
+ "js": "0.49.0",
3488
+ "android": "6.5.0",
3489
+ "macos": "0.11.0",
3490
+ "ios": "4.4.0"
3462
3491
  }
3463
3492
  },
3464
3493
  "expression": {
@@ -3600,7 +3629,12 @@
3600
3629
  "ios": "3.6.0",
3601
3630
  "macos": "0.5.0"
3602
3631
  },
3603
- "data-driven styling": {}
3632
+ "data-driven styling": {
3633
+ "js": "0.49.0",
3634
+ "android": "6.5.0",
3635
+ "macos": "0.11.0",
3636
+ "ios": "4.4.0"
3637
+ }
3604
3638
  },
3605
3639
  "expression": {
3606
3640
  "interpolated": false,
@@ -3980,7 +4014,10 @@
3980
4014
  "macos": "0.1.0"
3981
4015
  },
3982
4016
  "data-driven styling": {
3983
- "js": "0.46.0"
4017
+ "js": "0.49.0",
4018
+ "android": "6.5.0",
4019
+ "macos": "0.11.0",
4020
+ "ios": "4.4.0"
3984
4021
  }
3985
4022
  },
3986
4023
  "expression": {
@@ -99,7 +99,6 @@ class CollisionIndex {
99
99
  allowOverlap,
100
100
  scale,
101
101
  textPixelRatio,
102
- key,
103
102
  symbol,
104
103
  lineVertexArray,
105
104
  glyphOffsetArray,
@@ -1,5 +1,7 @@
1
1
  const EXTENT = require('../data/extent');
2
2
 
3
+ const { SymbolInstanceArray } = require('../data/array_types');
4
+
3
5
  /*
4
6
  The CrossTileSymbolIndex generally works on the assumption that
5
7
  a conceptual "unique symbol" can be identified by the text of
@@ -23,7 +25,8 @@ class TileLayerIndex {
23
25
  this.indexedSymbolInstances = {};
24
26
  this.bucketInstanceId = bucketInstanceId;
25
27
 
26
- for (const symbolInstance of symbolInstances) {
28
+ for (let i = 0; i < symbolInstances.length; i++) {
29
+ const symbolInstance = symbolInstances.get(i);
27
30
  const key = symbolInstance.key;
28
31
  if (!this.indexedSymbolInstances[key]) {
29
32
  this.indexedSymbolInstances[key] = [];
@@ -46,10 +49,9 @@ class TileLayerIndex {
46
49
  getScaledCoordinates(symbolInstance, childTileID) {
47
50
  const zDifference = childTileID.canonical.z - this.tileID.canonical.z;
48
51
  const scale = roundingFactor / 2 ** zDifference;
49
- const anchor = symbolInstance.anchor;
50
52
  return {
51
- x: Math.floor((childTileID.canonical.x * EXTENT + anchor.x) * scale),
52
- y: Math.floor((childTileID.canonical.y * EXTENT + anchor.y) * scale)
53
+ x: Math.floor((childTileID.canonical.x * EXTENT + symbolInstance.anchorX) * scale),
54
+ y: Math.floor((childTileID.canonical.y * EXTENT + symbolInstance.anchorY) * scale)
53
55
  };
54
56
  }
55
57
 
@@ -57,7 +59,8 @@ class TileLayerIndex {
57
59
  const tolerance =
58
60
  this.tileID.canonical.z < newTileID.canonical.z ? 1 : 2 ** (this.tileID.canonical.z - newTileID.canonical.z);
59
61
 
60
- for (const symbolInstance of symbolInstances) {
62
+ for (let i = 0; i < symbolInstances.length; i++) {
63
+ const symbolInstance = symbolInstances.get(i);
61
64
  if (symbolInstance.crossTileID) {
62
65
  // already has a match, skip
63
66
  continue;
@@ -143,7 +146,8 @@ class CrossTileSymbolLayerIndex {
143
146
  this.removeBucketCrossTileIDs(tileID.overscaledZ, this.indexes[tileID.overscaledZ][tileID.key]);
144
147
  }
145
148
 
146
- for (const symbolInstance of bucket.symbolInstances) {
149
+ for (let i = 0; i < bucket.symbolInstances.length; i++) {
150
+ const symbolInstance = bucket.symbolInstances.get(i);
147
151
  symbolInstance.crossTileID = 0;
148
152
  }
149
153
 
@@ -170,7 +174,8 @@ class CrossTileSymbolLayerIndex {
170
174
  }
171
175
  }
172
176
 
173
- for (const symbolInstance of bucket.symbolInstances) {
177
+ for (let i = 0; i < bucket.symbolInstances.length; i++) {
178
+ const symbolInstance = bucket.symbolInstances.get(i);
174
179
  if (!symbolInstance.crossTileID) {
175
180
  // symbol did not match any known symbol, assign a new id
176
181
  symbolInstance.crossTileID = crossTileIDs.generate();
@@ -172,7 +172,12 @@ class Placement {
172
172
 
173
173
  const collisionGroup = this.collisionGroups.get(bucket.sourceID);
174
174
 
175
- for (const symbolInstance of bucket.symbolInstances) {
175
+ if (!bucket.collisionArrays && collisionBoxArray) {
176
+ bucket.deserializeCollisionBoxes(collisionBoxArray);
177
+ }
178
+
179
+ for (let i = 0; i < bucket.symbolInstances.length; i++) {
180
+ const symbolInstance = bucket.symbolInstances.get(i);
176
181
  if (!seenCrossTileIDs[symbolInstance.crossTileID]) {
177
182
  if (holdingForFade) {
178
183
  // Mark all symbols from this tile as "not placed", but don't add to seenCrossTileIDs, because we don't
@@ -192,22 +197,14 @@ class Placement {
192
197
  let textFeatureIndex = 0;
193
198
  let iconFeatureIndex = 0;
194
199
 
195
- if (!symbolInstance.collisionArrays) {
196
- symbolInstance.collisionArrays = bucket.deserializeCollisionBoxes(
197
- collisionBoxArray,
198
- symbolInstance.textBoxStartIndex,
199
- symbolInstance.textBoxEndIndex,
200
- symbolInstance.iconBoxStartIndex,
201
- symbolInstance.iconBoxEndIndex
202
- );
203
- }
200
+ const collisionArrays = bucket.collisionArrays[i];
204
201
 
205
- if (symbolInstance.collisionArrays.textFeatureIndex) {
206
- textFeatureIndex = symbolInstance.collisionArrays.textFeatureIndex;
202
+ if (collisionArrays.textFeatureIndex) {
203
+ textFeatureIndex = collisionArrays.textFeatureIndex;
207
204
  }
208
- if (symbolInstance.collisionArrays.textBox) {
205
+ if (collisionArrays.textBox) {
209
206
  placedGlyphBoxes = this.collisionIndex.placeCollisionBox(
210
- symbolInstance.collisionArrays.textBox,
207
+ collisionArrays.textBox,
211
208
  layout.get('text-allow-overlap'),
212
209
  textPixelRatio,
213
210
  posMatrix,
@@ -216,9 +213,9 @@ class Placement {
216
213
  placeText = placedGlyphBoxes.box.length > 0;
217
214
  offscreen = offscreen && placedGlyphBoxes.offscreen;
218
215
  }
219
- const textCircles = symbolInstance.collisionArrays.textCircles;
216
+ const textCircles = collisionArrays.textCircles;
220
217
  if (textCircles) {
221
- const placedSymbol = bucket.text.placedSymbolArray.get(symbolInstance.placedTextSymbolIndices[0]);
218
+ const placedSymbol = bucket.text.placedSymbolArray.get(symbolInstance.horizontalPlacedTextSymbolIndex);
222
219
  const fontSize = symbolSize.evaluateSizeForFeature(
223
220
  bucket.textSizeData,
224
221
  partiallyEvaluatedTextSize,
@@ -229,7 +226,6 @@ class Placement {
229
226
  layout.get('text-allow-overlap'),
230
227
  scale,
231
228
  textPixelRatio,
232
- symbolInstance.key,
233
229
  placedSymbol,
234
230
  bucket.lineVertexArray,
235
231
  bucket.glyphOffsetArray,
@@ -248,12 +244,12 @@ class Placement {
248
244
  offscreen = offscreen && placedGlyphCircles.offscreen;
249
245
  }
250
246
 
251
- if (symbolInstance.collisionArrays.iconFeatureIndex) {
252
- iconFeatureIndex = symbolInstance.collisionArrays.iconFeatureIndex;
247
+ if (collisionArrays.iconFeatureIndex) {
248
+ iconFeatureIndex = collisionArrays.iconFeatureIndex;
253
249
  }
254
- if (symbolInstance.collisionArrays.iconBox) {
250
+ if (collisionArrays.iconBox) {
255
251
  placedIconBoxes = this.collisionIndex.placeCollisionBox(
256
- symbolInstance.collisionArrays.iconBox,
252
+ collisionArrays.iconBox,
257
253
  layout.get('icon-allow-overlap'),
258
254
  textPixelRatio,
259
255
  posMatrix,
@@ -412,8 +408,16 @@ class Placement {
412
408
  true
413
409
  );
414
410
 
411
+ if (
412
+ !bucket.collisionArrays &&
413
+ collisionBoxArray &&
414
+ (bucket.hasCollisionBoxData() || bucket.hasCollisionCircleData())
415
+ ) {
416
+ bucket.deserializeCollisionBoxes(collisionBoxArray);
417
+ }
418
+
415
419
  for (let s = 0; s < bucket.symbolInstances.length; s++) {
416
- const symbolInstance = bucket.symbolInstances[s];
420
+ const symbolInstance = bucket.symbolInstances.get(s);
417
421
  const isDuplicate = seenCrossTileIDs[symbolInstance.crossTileID];
418
422
 
419
423
  let opacityState = this.opacities[symbolInstance.crossTileID];
@@ -438,11 +442,14 @@ class Placement {
438
442
  for (let i = 0; i < opacityEntryCount; i++) {
439
443
  bucket.text.opacityVertexArray.emplaceBack(packedOpacity);
440
444
  }
441
- for (const placedTextSymbolIndex of symbolInstance.placedTextSymbolIndices) {
442
- const placedSymbol = bucket.text.placedSymbolArray.get(placedTextSymbolIndex);
443
- // If this label is completely faded, mark it so that we don't have to calculate
444
- // its position at render time
445
- placedSymbol.hidden = opacityState.text.isHidden();
445
+ // If this label is completely faded, mark it so that we don't have to calculate
446
+ // its position at render time
447
+ bucket.text.placedSymbolArray.get(symbolInstance.horizontalPlacedTextSymbolIndex).hidden =
448
+ opacityState.text.isHidden();
449
+
450
+ if (symbolInstance.verticalPlacedTextSymbolIndex >= 0) {
451
+ bucket.text.placedSymbolArray.get(symbolInstance.verticalPlacedTextSymbolIndex).hidden =
452
+ opacityState.text.isHidden();
446
453
  }
447
454
  }
448
455
 
@@ -451,35 +458,26 @@ class Placement {
451
458
  for (let i = 0; i < symbolInstance.numIconVertices / 4; i++) {
452
459
  bucket.icon.opacityVertexArray.emplaceBack(packedOpacity);
453
460
  }
454
- const placedSymbol = bucket.icon.placedSymbolArray.get(s);
455
- placedSymbol.hidden = opacityState.icon.isHidden();
461
+ bucket.icon.placedSymbolArray.get(s).hidden = opacityState.icon.isHidden();
456
462
  }
457
463
 
458
- if (!symbolInstance.collisionArrays) {
459
- symbolInstance.collisionArrays = bucket.deserializeCollisionBoxes(
460
- collisionBoxArray,
461
- symbolInstance.textBoxStartIndex,
462
- symbolInstance.textBoxEndIndex,
463
- symbolInstance.iconBoxStartIndex,
464
- symbolInstance.iconBoxEndIndex
465
- );
466
- }
467
-
468
- const collisionArrays = symbolInstance.collisionArrays;
469
- if (collisionArrays) {
470
- if (collisionArrays.textBox && bucket.hasCollisionBoxData()) {
471
- updateCollisionVertices(bucket.collisionBox.collisionVertexArray, opacityState.text.placed, false);
472
- }
464
+ if (bucket.hasCollisionBoxData() || bucket.hasCollisionCircleData()) {
465
+ const collisionArrays = bucket.collisionArrays[s];
466
+ if (collisionArrays) {
467
+ if (collisionArrays.textBox) {
468
+ updateCollisionVertices(bucket.collisionBox.collisionVertexArray, opacityState.text.placed, false);
469
+ }
473
470
 
474
- if (collisionArrays.iconBox && bucket.hasCollisionBoxData()) {
475
- updateCollisionVertices(bucket.collisionBox.collisionVertexArray, opacityState.icon.placed, false);
476
- }
471
+ if (collisionArrays.iconBox) {
472
+ updateCollisionVertices(bucket.collisionBox.collisionVertexArray, opacityState.icon.placed, false);
473
+ }
477
474
 
478
- const textCircles = collisionArrays.textCircles;
479
- if (textCircles && bucket.hasCollisionCircleData()) {
480
- for (let k = 0; k < textCircles.length; k += 5) {
481
- const notUsed = isDuplicate || textCircles[k + 4] === 0;
482
- updateCollisionVertices(bucket.collisionCircle.collisionVertexArray, opacityState.text.placed, notUsed);
475
+ const textCircles = collisionArrays.textCircles;
476
+ if (textCircles && bucket.hasCollisionCircleData()) {
477
+ for (let k = 0; k < textCircles.length; k += 5) {
478
+ const notUsed = isDuplicate || textCircles[k + 4] === 0;
479
+ updateCollisionVertices(bucket.collisionCircle.collisionVertexArray, opacityState.text.placed, notUsed);
480
+ }
483
481
  }
484
482
  }
485
483
  }