@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.
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "0.47.2"
2
+ "version": "0.48.0"
3
3
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mapwhit/tilerenderer",
3
3
  "description": "A WebGL interactive maps library",
4
- "version": "0.48.0",
4
+ "version": "0.49.0",
5
5
  "main": "src/index.js",
6
6
  "license": "BSD-3-Clause",
7
7
  "repository": {
@@ -11,7 +11,6 @@
11
11
  "dependencies": {
12
12
  "@mapbox/gl-matrix": "^0.0.1",
13
13
  "@mapbox/point-geometry": "^0.1.0",
14
- "@mapbox/shelf-pack": "^3.0.0",
15
14
  "@mapbox/tiny-sdf": "^1.1.0",
16
15
  "@mapbox/unitbezier": "^0.0.0",
17
16
  "@mapbox/vector-tile": "^1.3.1",
@@ -24,6 +23,8 @@
24
23
  "earcut": "^2.0.3",
25
24
  "geojson-vt": "^3.1.2",
26
25
  "grid-index": "^1.1.0",
26
+ "murmurhash-js": "^1.0.0",
27
+ "potpack": "^1.0.1",
27
28
  "quickselect": "^2.0.0",
28
29
  "supercluster": "^2.0.1",
29
30
  "tinyqueue": "^1.1.0"
@@ -513,6 +513,68 @@ class StructArrayLayout2i2ui3ul3ui2f2ub40 extends StructArray {
513
513
  StructArrayLayout2i2ui3ul3ui2f2ub40.prototype.bytesPerElement = 40;
514
514
  register('StructArrayLayout2i2ui3ul3ui2f2ub40', StructArrayLayout2i2ui3ul3ui2f2ub40);
515
515
 
516
+ /**
517
+ * Implementation of the StructArray layout:
518
+ * [0]: Int16[4]
519
+ * [8]: Uint16[9]
520
+ * [28]: Uint32[1]
521
+ *
522
+ * @private
523
+ */
524
+ class StructArrayLayout4i9ui1ul32 extends StructArray {
525
+ _refreshViews() {
526
+ this.uint8 = new Uint8Array(this.arrayBuffer);
527
+ this.int16 = new Int16Array(this.arrayBuffer);
528
+ this.uint16 = new Uint16Array(this.arrayBuffer);
529
+ this.uint32 = new Uint32Array(this.arrayBuffer);
530
+ }
531
+
532
+ emplaceBack(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) {
533
+ const i = this.length;
534
+ this.resize(i + 1);
535
+ const o2 = i * 16;
536
+ const o4 = i * 8;
537
+ this.int16[o2 + 0] = v0;
538
+ this.int16[o2 + 1] = v1;
539
+ this.int16[o2 + 2] = v2;
540
+ this.int16[o2 + 3] = v3;
541
+ this.uint16[o2 + 4] = v4;
542
+ this.uint16[o2 + 5] = v5;
543
+ this.uint16[o2 + 6] = v6;
544
+ this.uint16[o2 + 7] = v7;
545
+ this.uint16[o2 + 8] = v8;
546
+ this.uint16[o2 + 9] = v9;
547
+ this.uint16[o2 + 10] = v10;
548
+ this.uint16[o2 + 11] = v11;
549
+ this.uint16[o2 + 12] = v12;
550
+ this.uint32[o4 + 7] = v13;
551
+ return i;
552
+ }
553
+
554
+ emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) {
555
+ const o2 = i * 16;
556
+ const o4 = i * 8;
557
+ this.int16[o2 + 0] = v0;
558
+ this.int16[o2 + 1] = v1;
559
+ this.int16[o2 + 2] = v2;
560
+ this.int16[o2 + 3] = v3;
561
+ this.uint16[o2 + 4] = v4;
562
+ this.uint16[o2 + 5] = v5;
563
+ this.uint16[o2 + 6] = v6;
564
+ this.uint16[o2 + 7] = v7;
565
+ this.uint16[o2 + 8] = v8;
566
+ this.uint16[o2 + 9] = v9;
567
+ this.uint16[o2 + 10] = v10;
568
+ this.uint16[o2 + 11] = v11;
569
+ this.uint16[o2 + 12] = v12;
570
+ this.uint32[o4 + 7] = v13;
571
+ return i;
572
+ }
573
+ }
574
+
575
+ StructArrayLayout4i9ui1ul32.prototype.bytesPerElement = 32;
576
+ register('StructArrayLayout4i9ui1ul32', StructArrayLayout4i9ui1ul32);
577
+
516
578
  /**
517
579
  * Implementation of the StructArray layout:
518
580
  * [0]: Float32[1]
@@ -974,6 +1036,111 @@ class PlacedSymbolArray extends StructArrayLayout2i2ui3ul3ui2f2ub40 {
974
1036
 
975
1037
  register('PlacedSymbolArray', PlacedSymbolArray);
976
1038
 
1039
+ class SymbolInstanceStruct extends Struct {
1040
+ get anchorX() {
1041
+ return this._structArray.int16[this._pos2 + 0];
1042
+ }
1043
+ set anchorX(x) {
1044
+ this._structArray.int16[this._pos2 + 0] = x;
1045
+ }
1046
+ get anchorY() {
1047
+ return this._structArray.int16[this._pos2 + 1];
1048
+ }
1049
+ set anchorY(x) {
1050
+ this._structArray.int16[this._pos2 + 1] = x;
1051
+ }
1052
+ get horizontalPlacedTextSymbolIndex() {
1053
+ return this._structArray.int16[this._pos2 + 2];
1054
+ }
1055
+ set horizontalPlacedTextSymbolIndex(x) {
1056
+ this._structArray.int16[this._pos2 + 2] = x;
1057
+ }
1058
+ get verticalPlacedTextSymbolIndex() {
1059
+ return this._structArray.int16[this._pos2 + 3];
1060
+ }
1061
+ set verticalPlacedTextSymbolIndex(x) {
1062
+ this._structArray.int16[this._pos2 + 3] = x;
1063
+ }
1064
+ get key() {
1065
+ return this._structArray.uint16[this._pos2 + 4];
1066
+ }
1067
+ set key(x) {
1068
+ this._structArray.uint16[this._pos2 + 4] = x;
1069
+ }
1070
+ get textBoxStartIndex() {
1071
+ return this._structArray.uint16[this._pos2 + 5];
1072
+ }
1073
+ set textBoxStartIndex(x) {
1074
+ this._structArray.uint16[this._pos2 + 5] = x;
1075
+ }
1076
+ get textBoxEndIndex() {
1077
+ return this._structArray.uint16[this._pos2 + 6];
1078
+ }
1079
+ set textBoxEndIndex(x) {
1080
+ this._structArray.uint16[this._pos2 + 6] = x;
1081
+ }
1082
+ get iconBoxStartIndex() {
1083
+ return this._structArray.uint16[this._pos2 + 7];
1084
+ }
1085
+ set iconBoxStartIndex(x) {
1086
+ this._structArray.uint16[this._pos2 + 7] = x;
1087
+ }
1088
+ get iconBoxEndIndex() {
1089
+ return this._structArray.uint16[this._pos2 + 8];
1090
+ }
1091
+ set iconBoxEndIndex(x) {
1092
+ this._structArray.uint16[this._pos2 + 8] = x;
1093
+ }
1094
+ get featureIndex() {
1095
+ return this._structArray.uint16[this._pos2 + 9];
1096
+ }
1097
+ set featureIndex(x) {
1098
+ this._structArray.uint16[this._pos2 + 9] = x;
1099
+ }
1100
+ get numGlyphVertices() {
1101
+ return this._structArray.uint16[this._pos2 + 10];
1102
+ }
1103
+ set numGlyphVertices(x) {
1104
+ this._structArray.uint16[this._pos2 + 10] = x;
1105
+ }
1106
+ get numVerticalGlyphVertices() {
1107
+ return this._structArray.uint16[this._pos2 + 11];
1108
+ }
1109
+ set numVerticalGlyphVertices(x) {
1110
+ this._structArray.uint16[this._pos2 + 11] = x;
1111
+ }
1112
+ get numIconVertices() {
1113
+ return this._structArray.uint16[this._pos2 + 12];
1114
+ }
1115
+ set numIconVertices(x) {
1116
+ this._structArray.uint16[this._pos2 + 12] = x;
1117
+ }
1118
+ get crossTileID() {
1119
+ return this._structArray.uint32[this._pos4 + 7];
1120
+ }
1121
+ set crossTileID(x) {
1122
+ this._structArray.uint32[this._pos4 + 7] = x;
1123
+ }
1124
+ }
1125
+
1126
+ SymbolInstanceStruct.prototype.size = 32;
1127
+
1128
+ /**
1129
+ * @private
1130
+ */
1131
+ class SymbolInstanceArray extends StructArrayLayout4i9ui1ul32 {
1132
+ /**
1133
+ * Return the SymbolInstanceStruct at the given location in the array.
1134
+ * @param {number} index The index of the element.
1135
+ */
1136
+ get(index) {
1137
+ assert(!this.isTransferred);
1138
+ return new SymbolInstanceStruct(this, index);
1139
+ }
1140
+ }
1141
+
1142
+ register('SymbolInstanceArray', SymbolInstanceArray);
1143
+
977
1144
  class GlyphOffsetStruct extends Struct {
978
1145
  get offsetX() {
979
1146
  return this._structArray.float32[this._pos4 + 0];
@@ -1104,6 +1271,7 @@ module.exports = {
1104
1271
  StructArrayLayout2i2i2i12,
1105
1272
  StructArrayLayout2ub4,
1106
1273
  StructArrayLayout2i2ui3ul3ui2f2ub40,
1274
+ StructArrayLayout4i9ui1ul32,
1107
1275
  StructArrayLayout1f4,
1108
1276
  StructArrayLayout3i6,
1109
1277
  StructArrayLayout1ul2ui8,
@@ -1134,5 +1302,6 @@ module.exports = {
1134
1302
  FeatureIndexArray,
1135
1303
  GlyphOffsetArray,
1136
1304
  PlacedSymbolArray,
1305
+ SymbolInstanceArray,
1137
1306
  SymbolLineVertexArray
1138
1307
  };
@@ -73,6 +73,23 @@ const placement = createLayout([
73
73
  { type: 'Uint8', name: 'hidden' }
74
74
  ]);
75
75
 
76
+ const symbolInstance = createLayout([
77
+ { type: 'Int16', name: 'anchorX' },
78
+ { type: 'Int16', name: 'anchorY' },
79
+ { type: 'Int16', name: 'horizontalPlacedTextSymbolIndex' },
80
+ { type: 'Int16', name: 'verticalPlacedTextSymbolIndex' },
81
+ { type: 'Uint16', name: 'key' },
82
+ { type: 'Uint16', name: 'textBoxStartIndex' },
83
+ { type: 'Uint16', name: 'textBoxEndIndex' },
84
+ { type: 'Uint16', name: 'iconBoxStartIndex' },
85
+ { type: 'Uint16', name: 'iconBoxEndIndex' },
86
+ { type: 'Uint16', name: 'featureIndex' },
87
+ { type: 'Uint16', name: 'numGlyphVertices' },
88
+ { type: 'Uint16', name: 'numVerticalGlyphVertices' },
89
+ { type: 'Uint16', name: 'numIconVertices' },
90
+ { type: 'Uint32', name: 'crossTileID' }
91
+ ]);
92
+
76
93
  const glyphOffset = createLayout([{ type: 'Float32', name: 'offsetX' }]);
77
94
 
78
95
  const lineVertex = createLayout([
@@ -90,6 +107,7 @@ module.exports = {
90
107
  collisionBoxLayout,
91
108
  collisionCircleLayout,
92
109
  placement,
110
+ symbolInstance,
93
111
  glyphOffset,
94
112
  lineVertex
95
113
  };
@@ -14,6 +14,7 @@ const {
14
14
  CollisionCircleLayoutArray,
15
15
  CollisionVertexArray,
16
16
  PlacedSymbolArray,
17
+ SymbolInstanceArray,
17
18
  GlyphOffsetArray,
18
19
  SymbolLineVertexArray
19
20
  } = require('../array_types');
@@ -190,11 +191,13 @@ class SymbolBucket {
190
191
  this.iconSizeData = getSizeData(this.zoom, unevaluatedLayoutValues['icon-size']);
191
192
 
192
193
  const layout = this.layers[0].layout;
194
+ const zOrderByViewportY = layout.get('symbol-z-order') === 'viewport-y';
193
195
  this.sortFeaturesByY =
194
- layout.get('text-allow-overlap') ||
195
- layout.get('icon-allow-overlap') ||
196
- layout.get('text-ignore-placement') ||
197
- layout.get('icon-ignore-placement');
196
+ zOrderByViewportY &&
197
+ (layout.get('text-allow-overlap') ||
198
+ layout.get('icon-allow-overlap') ||
199
+ layout.get('text-ignore-placement') ||
200
+ layout.get('icon-ignore-placement'));
198
201
 
199
202
  this.sourceID = options.sourceID;
200
203
  }
@@ -220,6 +223,7 @@ class SymbolBucket {
220
223
 
221
224
  this.glyphOffsetArray = new GlyphOffsetArray();
222
225
  this.lineVertexArray = new SymbolLineVertexArray();
226
+ this.symbolInstances = new SymbolInstanceArray();
223
227
  }
224
228
 
225
229
  calculateGlyphDependencies(text, stack, textAlongLine, doesAllowVerticalWritingMode) {
@@ -455,15 +459,15 @@ class SymbolBucket {
455
459
  arrays.programConfigurations.populatePaintArrays(arrays.layoutVertexArray.length, feature, feature.index, {});
456
460
  }
457
461
 
458
- _addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, point, anchor, extrude) {
462
+ _addCollisionDebugVertex(layoutVertexArray, collisionVertexArray, point, anchorX, anchorY, extrude) {
459
463
  collisionVertexArray.emplaceBack(0, 0);
460
464
  return layoutVertexArray.emplaceBack(
461
465
  // pos
462
466
  point.x,
463
467
  point.y,
464
468
  // a_anchor_pos
465
- anchor.x,
466
- anchor.y,
469
+ anchorX,
470
+ anchorY,
467
471
  // extrude
468
472
  Math.round(extrude.x),
469
473
  Math.round(extrude.y)
@@ -477,32 +481,39 @@ class SymbolBucket {
477
481
  const layoutVertexArray = arrays.layoutVertexArray;
478
482
  const collisionVertexArray = arrays.collisionVertexArray;
479
483
 
484
+ const anchorX = symbolInstance.anchorX;
485
+ const anchorY = symbolInstance.anchorY;
486
+
480
487
  this._addCollisionDebugVertex(
481
488
  layoutVertexArray,
482
489
  collisionVertexArray,
483
490
  boxAnchorPoint,
484
- symbolInstance.anchor,
491
+ anchorX,
492
+ anchorY,
485
493
  new Point(x1, y1)
486
494
  );
487
495
  this._addCollisionDebugVertex(
488
496
  layoutVertexArray,
489
497
  collisionVertexArray,
490
498
  boxAnchorPoint,
491
- symbolInstance.anchor,
499
+ anchorX,
500
+ anchorY,
492
501
  new Point(x2, y1)
493
502
  );
494
503
  this._addCollisionDebugVertex(
495
504
  layoutVertexArray,
496
505
  collisionVertexArray,
497
506
  boxAnchorPoint,
498
- symbolInstance.anchor,
507
+ anchorX,
508
+ anchorY,
499
509
  new Point(x2, y2)
500
510
  );
501
511
  this._addCollisionDebugVertex(
502
512
  layoutVertexArray,
503
513
  collisionVertexArray,
504
514
  boxAnchorPoint,
505
- symbolInstance.anchor,
515
+ anchorX,
516
+ anchorY,
506
517
  new Point(x1, y2)
507
518
  );
508
519
 
@@ -524,49 +535,41 @@ class SymbolBucket {
524
535
  }
525
536
  }
526
537
 
527
- generateCollisionDebugBuffers() {
528
- for (const symbolInstance of this.symbolInstances) {
529
- symbolInstance.textCollisionFeature = {
530
- boxStartIndex: symbolInstance.textBoxStartIndex,
531
- boxEndIndex: symbolInstance.textBoxEndIndex
532
- };
533
- symbolInstance.iconCollisionFeature = {
534
- boxStartIndex: symbolInstance.iconBoxStartIndex,
535
- boxEndIndex: symbolInstance.iconBoxEndIndex
536
- };
538
+ addDebugCollisionBoxes(startIndex, endIndex, symbolInstance) {
539
+ for (let b = startIndex; b < endIndex; b++) {
540
+ const box = this.collisionBoxArray.get(b);
541
+ const x1 = box.x1;
542
+ const y1 = box.y1;
543
+ const x2 = box.x2;
544
+ const y2 = box.y2;
545
+
546
+ // If the radius > 0, this collision box is actually a circle
547
+ // The data we add to the buffers is exactly the same, but we'll render with a different shader.
548
+ const isCircle = box.radius > 0;
549
+ this.addCollisionDebugVertices(
550
+ x1,
551
+ y1,
552
+ x2,
553
+ y2,
554
+ isCircle ? this.collisionCircle : this.collisionBox,
555
+ box.anchorPoint,
556
+ symbolInstance,
557
+ isCircle
558
+ );
559
+ }
560
+ }
537
561
 
538
- for (let i = 0; i < 2; i++) {
539
- const feature = symbolInstance[i === 0 ? 'textCollisionFeature' : 'iconCollisionFeature'];
540
- if (!feature) continue;
541
-
542
- for (let b = feature.boxStartIndex; b < feature.boxEndIndex; b++) {
543
- const box = this.collisionBoxArray.get(b);
544
- const x1 = box.x1;
545
- const y1 = box.y1;
546
- const x2 = box.x2;
547
- const y2 = box.y2;
548
-
549
- // If the radius > 0, this collision box is actually a circle
550
- // The data we add to the buffers is exactly the same, but we'll render with a different shader.
551
- const isCircle = box.radius > 0;
552
- this.addCollisionDebugVertices(
553
- x1,
554
- y1,
555
- x2,
556
- y2,
557
- isCircle ? this.collisionCircle : this.collisionBox,
558
- box.anchorPoint,
559
- symbolInstance,
560
- isCircle
561
- );
562
- }
563
- }
562
+ generateCollisionDebugBuffers() {
563
+ for (let i = 0; i < this.symbolInstances.length; i++) {
564
+ const symbolInstance = this.symbolInstances.get(i);
565
+ this.addDebugCollisionBoxes(symbolInstance.textBoxStartIndex, symbolInstance.textBoxEndIndex, symbolInstance);
566
+ this.addDebugCollisionBoxes(symbolInstance.iconBoxStartIndex, symbolInstance.iconBoxEndIndex, symbolInstance);
564
567
  }
565
568
  }
566
569
 
567
570
  // These flat arrays are meant to be quicker to iterate over than the source
568
571
  // CollisionBoxArray
569
- deserializeCollisionBoxes(collisionBoxArray, textStartIndex, textEndIndex, iconStartIndex, iconEndIndex) {
572
+ _deserializeCollisionBoxesForSymbol(collisionBoxArray, textStartIndex, textEndIndex, iconStartIndex, iconEndIndex) {
570
573
  const collisionArrays = {};
571
574
  for (let k = textStartIndex; k < textEndIndex; k++) {
572
575
  const box = collisionBoxArray.get(k);
@@ -614,6 +617,22 @@ class SymbolBucket {
614
617
  return collisionArrays;
615
618
  }
616
619
 
620
+ deserializeCollisionBoxes(collisionBoxArray) {
621
+ this.collisionArrays = [];
622
+ for (let i = 0; i < this.symbolInstances.length; i++) {
623
+ const symbolInstance = this.symbolInstances.get(i);
624
+ this.collisionArrays.push(
625
+ this._deserializeCollisionBoxesForSymbol(
626
+ collisionBoxArray,
627
+ symbolInstance.textBoxStartIndex,
628
+ symbolInstance.textBoxEndIndex,
629
+ symbolInstance.iconBoxStartIndex,
630
+ symbolInstance.iconBoxEndIndex
631
+ )
632
+ );
633
+ }
634
+ }
635
+
617
636
  hasTextData() {
618
637
  return this.text.segments.get().length > 0;
619
638
  }
@@ -630,6 +649,16 @@ class SymbolBucket {
630
649
  return this.collisionCircle.segments.get().length > 0;
631
650
  }
632
651
 
652
+ addIndicesForPlacedTextSymbol(placedTextSymbolIndex) {
653
+ const placedSymbol = this.text.placedSymbolArray.get(placedTextSymbolIndex);
654
+
655
+ const endIndex = placedSymbol.vertexStartIndex + placedSymbol.numGlyphs * 4;
656
+ for (let vertexIndex = placedSymbol.vertexStartIndex; vertexIndex < endIndex; vertexIndex += 4) {
657
+ this.text.indexArray.emplaceBack(vertexIndex, vertexIndex + 1, vertexIndex + 2);
658
+ this.text.indexArray.emplaceBack(vertexIndex + 1, vertexIndex + 2, vertexIndex + 3);
659
+ }
660
+ }
661
+
633
662
  sortFeatures(angle) {
634
663
  if (!this.sortFeaturesByY) return;
635
664
 
@@ -653,12 +682,16 @@ class SymbolBucket {
653
682
  const sin = Math.sin(angle);
654
683
  const cos = Math.cos(angle);
655
684
 
685
+ const rotatedYs = [];
686
+ const featureIndexes = [];
687
+ for (let i = 0; i < this.symbolInstances.length; i++) {
688
+ const symbolInstance = this.symbolInstances.get(i);
689
+ rotatedYs.push(Math.round(sin * symbolInstance.anchorX + cos * symbolInstance.anchorY) | 0);
690
+ featureIndexes.push(symbolInstance.featureIndex);
691
+ }
692
+
656
693
  symbolInstanceIndexes.sort((aIndex, bIndex) => {
657
- const a = this.symbolInstances[aIndex];
658
- const b = this.symbolInstances[bIndex];
659
- const aRotated = Math.round(sin * a.anchor.x + cos * a.anchor.y) | 0;
660
- const bRotated = Math.round(sin * b.anchor.x + cos * b.anchor.y) | 0;
661
- return aRotated - bRotated || b.featureIndex - a.featureIndex;
694
+ return rotatedYs[aIndex] - rotatedYs[bIndex] || featureIndexes[bIndex] - featureIndexes[aIndex];
662
695
  });
663
696
 
664
697
  this.text.indexArray.clear();
@@ -667,17 +700,14 @@ class SymbolBucket {
667
700
  this.featureSortOrder = [];
668
701
 
669
702
  for (const i of symbolInstanceIndexes) {
670
- const symbolInstance = this.symbolInstances[i];
703
+ const symbolInstance = this.symbolInstances.get(i);
671
704
  this.featureSortOrder.push(symbolInstance.featureIndex);
672
705
 
673
- for (const placedTextSymbolIndex of symbolInstance.placedTextSymbolIndices) {
674
- const placedSymbol = this.text.placedSymbolArray.get(placedTextSymbolIndex);
675
-
676
- const endIndex = placedSymbol.vertexStartIndex + placedSymbol.numGlyphs * 4;
677
- for (let vertexIndex = placedSymbol.vertexStartIndex; vertexIndex < endIndex; vertexIndex += 4) {
678
- this.text.indexArray.emplaceBack(vertexIndex, vertexIndex + 1, vertexIndex + 2);
679
- this.text.indexArray.emplaceBack(vertexIndex + 1, vertexIndex + 2, vertexIndex + 3);
680
- }
706
+ if (symbolInstance.horizontalPlacedTextSymbolIndex >= 0) {
707
+ this.addIndicesForPlacedTextSymbol(symbolInstance.horizontalPlacedTextSymbolIndex);
708
+ }
709
+ if (symbolInstance.verticalPlacedTextSymbolIndex >= 0) {
710
+ this.addIndicesForPlacedTextSymbol(symbolInstance.verticalPlacedTextSymbolIndex);
681
711
  }
682
712
 
683
713
  const placedIcon = this.icon.placedSymbolArray.get(i);
@@ -694,8 +724,7 @@ class SymbolBucket {
694
724
  }
695
725
 
696
726
  register('SymbolBucket', SymbolBucket, {
697
- omit: ['layers', 'collisionBoxArray', 'features', 'compareText'],
698
- shallow: ['symbolInstances']
727
+ omit: ['layers', 'collisionBoxArray', 'features', 'compareText']
699
728
  });
700
729
 
701
730
  // this constant is based on the size of StructArray indexes used in a symbol
@@ -177,13 +177,21 @@ class Transform {
177
177
  * @private
178
178
  */
179
179
  getVisibleUnwrappedCoordinates(tileID) {
180
- const ul = this.pointCoordinate(new Point(0, 0), 0);
181
- const ur = this.pointCoordinate(new Point(this.width, 0), 0);
182
- const w0 = Math.floor(ul.column);
183
- const w1 = Math.floor(ur.column);
184
180
  const result = [new UnwrappedTileID(0, tileID)];
185
181
  if (this._renderWorldCopies) {
186
- for (let w = w0; w <= w1; w++) {
182
+ const utl = this.pointCoordinate(new Point(0, 0), 0);
183
+ const utr = this.pointCoordinate(new Point(this.width, 0), 0);
184
+ const ubl = this.pointCoordinate(new Point(this.width, this.height), 0);
185
+ const ubr = this.pointCoordinate(new Point(0, this.height), 0);
186
+ const w0 = Math.floor(Math.min(utl.column, utr.column, ubl.column, ubr.column));
187
+ const w1 = Math.floor(Math.max(utl.column, utr.column, ubl.column, ubr.column));
188
+
189
+ // Add an extra copy of the world on each side to properly render ImageSources and CanvasSources.
190
+ // Both sources draw outside the tile boundaries of the tile that "contains them" so we need
191
+ // to add extra copies on both sides in case offscreen tiles need to draw into on-screen ones.
192
+ const extraWorldCopy = 1;
193
+
194
+ for (let w = w0 - extraWorldCopy; w <= w1 + extraWorldCopy; w++) {
187
195
  if (w === 0) continue;
188
196
  result.push(new UnwrappedTileID(w, tileID));
189
197
  }
@@ -1,14 +1,12 @@
1
- const ShelfPack = require('@mapbox/shelf-pack');
2
-
3
1
  const { AlphaImage } = require('../util/image');
4
2
  const { register } = require('../util/transfer_registry');
3
+ const potpack = require('potpack');
5
4
 
6
5
  const padding = 1;
7
6
 
8
7
  class GlyphAtlas {
9
8
  constructor(stacks) {
10
9
  const positions = {};
11
- const pack = new ShelfPack(0, 0, { autoResize: true });
12
10
  const bins = [];
13
11
 
14
12
  for (const stack in stacks) {
@@ -30,9 +28,8 @@ class GlyphAtlas {
30
28
  }
31
29
  }
32
30
 
33
- pack.pack(bins, { inPlace: true });
34
-
35
- const image = new AlphaImage({ width: pack.w, height: pack.h });
31
+ const { w, h } = potpack(bins);
32
+ const image = new AlphaImage({ width: w ?? 1, height: h ?? 1 });
36
33
 
37
34
  for (const stack in stacks) {
38
35
  const glyphs = stacks[stack];
@@ -1,7 +1,6 @@
1
- const ShelfPack = require('@mapbox/shelf-pack');
2
-
3
1
  const { RGBAImage } = require('../util/image');
4
2
  const { register } = require('../util/transfer_registry');
3
+ const potpack = require('potpack');
5
4
 
6
5
  const padding = 1;
7
6
 
@@ -32,7 +31,6 @@ class ImageAtlas {
32
31
  constructor(icons, patterns) {
33
32
  const iconPositions = {};
34
33
  const patternPositions = {};
35
- const pack = new ShelfPack(0, 0, { autoResize: true });
36
34
  const bins = [];
37
35
  for (const id in icons) {
38
36
  const src = icons[id];
@@ -58,9 +56,8 @@ class ImageAtlas {
58
56
  patternPositions[id] = new ImagePosition(bin, src);
59
57
  }
60
58
 
61
- pack.pack(bins, { inPlace: true });
62
-
63
- const image = new RGBAImage({ width: pack.w, height: pack.h });
59
+ const { w, h } = potpack(bins);
60
+ const image = new RGBAImage({ width: w || 1, height: h || 1 });
64
61
 
65
62
  for (const id in icons) {
66
63
  const src = icons[id];