@mapwhit/tilerenderer 0.47.1 → 0.48.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.
Files changed (76) hide show
  1. package/build/min/package.json +1 -1
  2. package/package.json +1 -2
  3. package/src/data/array_types.js +1 -1
  4. package/src/data/bucket/circle_bucket.js +1 -1
  5. package/src/data/bucket/fill_bucket.js +1 -1
  6. package/src/data/bucket/fill_extrusion_bucket.js +1 -1
  7. package/src/data/bucket/heatmap_bucket.js +1 -1
  8. package/src/data/bucket/line_bucket.js +1 -1
  9. package/src/data/bucket/symbol_bucket.js +26 -12
  10. package/src/data/dem_data.js +1 -1
  11. package/src/data/feature_index.js +43 -82
  12. package/src/data/program_configuration.js +19 -11
  13. package/src/data/segment.js +2 -2
  14. package/src/geo/transform.js +4 -2
  15. package/src/gl/color_mode.js +6 -6
  16. package/src/index.js +3 -1
  17. package/src/render/glyph_atlas.js +1 -1
  18. package/src/render/glyph_manager.js +43 -48
  19. package/src/render/image_atlas.js +1 -1
  20. package/src/render/image_manager.js +9 -37
  21. package/src/source/geojson_source.js +49 -93
  22. package/src/source/geojson_worker_source.js +33 -134
  23. package/src/source/image_source.js +9 -14
  24. package/src/source/load_tilejson.js +27 -34
  25. package/src/source/raster_dem_tile_source.js +27 -40
  26. package/src/source/raster_tile_source.js +53 -62
  27. package/src/source/rtl_text_plugin.js +3 -1
  28. package/src/source/source_cache.js +23 -21
  29. package/src/source/source_state.js +17 -26
  30. package/src/source/tile.js +6 -5
  31. package/src/source/tile_id.js +1 -1
  32. package/src/source/vector_tile_source.js +56 -73
  33. package/src/source/vector_tile_worker_source.js +20 -85
  34. package/src/source/worker.js +37 -103
  35. package/src/source/worker_tile.js +39 -84
  36. package/src/style/load_sprite.js +14 -17
  37. package/src/style/properties.js +1 -1
  38. package/src/style/style.js +22 -37
  39. package/src/style/style_layer/symbol_style_layer_properties.js +1 -1
  40. package/src/style/style_layer_index.js +17 -23
  41. package/src/style-spec/expression/compound_expression.js +30 -16
  42. package/src/style-spec/expression/definitions/coercion.js +13 -0
  43. package/src/style-spec/expression/definitions/comparison.js +193 -0
  44. package/src/style-spec/expression/definitions/formatted.js +123 -0
  45. package/src/style-spec/expression/definitions/index.js +10 -60
  46. package/src/style-spec/expression/definitions/interpolate.js +17 -7
  47. package/src/style-spec/expression/definitions/literal.js +5 -0
  48. package/src/style-spec/expression/parsing_context.js +4 -0
  49. package/src/style-spec/expression/types.js +12 -1
  50. package/src/style-spec/feature_filter/index.js +1 -1
  51. package/src/style-spec/reference/v8.json +120 -49
  52. package/src/symbol/anchor.js +1 -1
  53. package/src/symbol/collision_index.js +23 -16
  54. package/src/symbol/get_anchors.js +11 -22
  55. package/src/symbol/grid_index.js +176 -182
  56. package/src/symbol/mergelines.js +51 -48
  57. package/src/symbol/opacity_state.js +1 -1
  58. package/src/symbol/placement.js +8 -2
  59. package/src/symbol/quads.js +7 -6
  60. package/src/symbol/shaping.js +185 -40
  61. package/src/symbol/symbol_layout.js +9 -6
  62. package/src/symbol/transform_text.js +12 -1
  63. package/src/ui/camera.js +82 -85
  64. package/src/ui/map.js +13 -57
  65. package/src/util/actor.js +46 -42
  66. package/src/util/browser.js +6 -0
  67. package/src/util/dictionary_coder.js +13 -21
  68. package/src/util/dispatcher.js +14 -17
  69. package/src/util/image.js +1 -1
  70. package/src/util/loader/image.js +11 -11
  71. package/src/util/polyfill.js +16 -0
  72. package/src/util/task_queue.js +39 -43
  73. package/src/util/transfer_registry.js +167 -0
  74. package/src/util/web_worker_transfer.js +5 -190
  75. package/src/source/raster_dem_tile_worker_source.js +0 -26
  76. package/src/style-spec/expression/definitions/equals.js +0 -93
@@ -17,7 +17,7 @@ function getAngleWindowSize(shapedText, glyphSize, boxScale) {
17
17
  return shapedText ? (3 / 5) * glyphSize * boxScale : 0;
18
18
  }
19
19
 
20
- function getLabelLength(shapedText, shapedIcon) {
20
+ function getShapedLabelLength(shapedText, shapedIcon) {
21
21
  return Math.max(
22
22
  shapedText ? shapedText.right - shapedText.left : 0,
23
23
  shapedIcon ? shapedIcon.right - shapedIcon.left : 0
@@ -26,7 +26,7 @@ function getLabelLength(shapedText, shapedIcon) {
26
26
 
27
27
  function getCenterAnchor(line, maxAngle, shapedText, shapedIcon, glyphSize, boxScale) {
28
28
  const angleWindowSize = getAngleWindowSize(shapedText, glyphSize, boxScale);
29
- const labelLength = getLabelLength(shapedText, shapedIcon);
29
+ const labelLength = getShapedLabelLength(shapedText, shapedIcon) * boxScale;
30
30
 
31
31
  let prevDistance = 0;
32
32
  const centerDistance = getLineLength(line) / 2;
@@ -45,11 +45,10 @@ function getCenterAnchor(line, maxAngle, shapedText, shapedIcon, glyphSize, boxS
45
45
 
46
46
  const anchor = new Anchor(x, y, b.angleTo(a), i);
47
47
  anchor._round();
48
- if (angleWindowSize && !checkMaxAngle(line, anchor, labelLength, angleWindowSize, maxAngle)) {
49
- return;
48
+ if (!angleWindowSize || checkMaxAngle(line, anchor, labelLength, angleWindowSize, maxAngle)) {
49
+ return anchor;
50
50
  }
51
-
52
- return anchor;
51
+ return;
53
52
  }
54
53
 
55
54
  prevDistance += segmentDistance;
@@ -62,16 +61,16 @@ function getAnchors(line, spacing, maxAngle, shapedText, shapedIcon, glyphSize,
62
61
  // on the line.
63
62
 
64
63
  const angleWindowSize = getAngleWindowSize(shapedText, glyphSize, boxScale);
65
-
66
- const labelLength = getLabelLength(shapedText, shapedIcon);
64
+ const shapedLabelLength = getShapedLabelLength(shapedText, shapedIcon);
65
+ const labelLength = shapedLabelLength * boxScale;
67
66
 
68
67
  // Is the line continued from outside the tile boundary?
69
68
  const isLineContinued = line[0].x === 0 || line[0].x === tileExtent || line[0].y === 0 || line[0].y === tileExtent;
70
69
 
71
70
  // Is the label long, relative to the spacing?
72
71
  // If so, adjust the spacing so there is always a minimum space of `spacing / 4` between label edges.
73
- if (spacing - labelLength * boxScale < spacing / 4) {
74
- spacing = labelLength * boxScale + spacing / 4;
72
+ if (spacing - labelLength < spacing / 4) {
73
+ spacing = labelLength + spacing / 4;
75
74
  }
76
75
 
77
76
  // Offset the first anchor by:
@@ -82,20 +81,10 @@ function getAnchors(line, spacing, maxAngle, shapedText, shapedIcon, glyphSize,
82
81
  const fixedExtraOffset = glyphSize * 2;
83
82
 
84
83
  const offset = !isLineContinued
85
- ? ((labelLength / 2 + fixedExtraOffset) * boxScale * overscaling) % spacing
84
+ ? ((shapedLabelLength / 2 + fixedExtraOffset) * boxScale * overscaling) % spacing
86
85
  : ((spacing / 2) * overscaling) % spacing;
87
86
 
88
- return resample(
89
- line,
90
- offset,
91
- spacing,
92
- angleWindowSize,
93
- maxAngle,
94
- labelLength * boxScale,
95
- isLineContinued,
96
- false,
97
- tileExtent
98
- );
87
+ return resample(line, offset, spacing, angleWindowSize, maxAngle, labelLength, isLineContinued, false, tileExtent);
99
88
  }
100
89
 
101
90
  function resample(
@@ -1,3 +1,5 @@
1
+ const { clamp } = require('../util/util');
2
+
1
3
  /**
2
4
  * GridIndex is a data structure for testing the intersection of
3
5
  * circles and rectangles in a 2d plane.
@@ -8,24 +10,21 @@
8
10
  * at least one cell. As long as the geometries are relatively
9
11
  * uniformly distributed across the plane, this greatly reduces
10
12
  * the number of comparisons necessary.
11
- *
12
- * @private
13
13
  */
14
14
  class GridIndex {
15
- constructor(width, height, cellSize) {
16
- const boxCells = (this.boxCells = []);
17
- const circleCells = (this.circleCells = []);
15
+ #boxUid = 0;
16
+ #circleUid = 0;
18
17
 
18
+ constructor(width, height, cellSize) {
19
19
  // More cells -> fewer geometries to check per cell, but items tend
20
20
  // to be split across more cells.
21
21
  // Sweet spot allows most small items to fit in one cell
22
22
  this.xCellCount = Math.ceil(width / cellSize);
23
23
  this.yCellCount = Math.ceil(height / cellSize);
24
24
 
25
- for (let i = 0; i < this.xCellCount * this.yCellCount; i++) {
26
- boxCells.push([]);
27
- circleCells.push([]);
28
- }
25
+ const size = this.xCellCount * this.yCellCount;
26
+ this.boxCells = new Array(size);
27
+ this.circleCells = new Array(size);
29
28
  this.circleKeys = [];
30
29
  this.boxKeys = [];
31
30
  this.bboxes = [];
@@ -35,8 +34,6 @@ class GridIndex {
35
34
  this.height = height;
36
35
  this.xScale = this.xCellCount / width;
37
36
  this.yScale = this.yCellCount / height;
38
- this.boxUid = 0;
39
- this.circleUid = 0;
40
37
  }
41
38
 
42
39
  keysLength() {
@@ -44,33 +41,30 @@ class GridIndex {
44
41
  }
45
42
 
46
43
  insert(key, x1, y1, x2, y2) {
47
- this._forEachCell(x1, y1, x2, y2, this._insertBoxCell, this.boxUid++);
44
+ const { boxCells } = this;
45
+ this.#forEachCell(x1, y1, x2, y2, insertBoxCell, this.#boxUid++);
48
46
  this.boxKeys.push(key);
49
- this.bboxes.push(x1);
50
- this.bboxes.push(y1);
51
- this.bboxes.push(x2);
52
- this.bboxes.push(y2);
47
+ this.bboxes.push(x1, y1, x2, y2);
48
+
49
+ function insertBoxCell(x1, y1, x2, y2, cellIndex, uid) {
50
+ (boxCells[cellIndex] ??= []).push(uid);
51
+ }
53
52
  }
54
53
 
55
54
  insertCircle(key, x, y, radius) {
55
+ const { circleCells } = this;
56
56
  // Insert circle into grid for all cells in the circumscribing square
57
57
  // It's more than necessary (by a factor of 4/PI), but fast to insert
58
- this._forEachCell(x - radius, y - radius, x + radius, y + radius, this._insertCircleCell, this.circleUid++);
58
+ this.#forEachCell(x - radius, y - radius, x + radius, y + radius, insertCircleCell, this.#circleUid++);
59
59
  this.circleKeys.push(key);
60
- this.circles.push(x);
61
- this.circles.push(y);
62
- this.circles.push(radius);
63
- }
64
-
65
- _insertBoxCell(x1, y1, x2, y2, cellIndex, uid) {
66
- this.boxCells[cellIndex].push(uid);
67
- }
60
+ this.circles.push(x, y, radius);
68
61
 
69
- _insertCircleCell(x1, y1, x2, y2, cellIndex, uid) {
70
- this.circleCells[cellIndex].push(uid);
62
+ function insertCircleCell(x1, y1, x2, y2, cellIndex, uid) {
63
+ (circleCells[cellIndex] ??= []).push(uid);
64
+ }
71
65
  }
72
66
 
73
- _query(x1, y1, x2, y2, hitTest, predicate) {
67
+ #query(x1, y1, x2, y2, hitTest, predicate) {
74
68
  if (x2 < 0 || x1 > this.width || y2 < 0 || y1 > this.height) {
75
69
  return hitTest ? false : [];
76
70
  }
@@ -106,11 +100,73 @@ class GridIndex {
106
100
  hitTest,
107
101
  seenUids: { box: {}, circle: {} }
108
102
  };
109
- this._forEachCell(x1, y1, x2, y2, this._queryCell, result, queryArgs, predicate);
103
+ this.#forEachCell(x1, y1, x2, y2, queryCell, result, queryArgs, predicate);
110
104
  return hitTest ? result.length > 0 : result;
105
+
106
+ function queryCell(x1, y1, x2, y2, cellIndex, result, queryArgs, predicate) {
107
+ const { seenUids } = queryArgs;
108
+ const boxCell = this.boxCells[cellIndex];
109
+ if (boxCell != null) {
110
+ const { bboxes } = this;
111
+ for (const boxUid of boxCell) {
112
+ if (!seenUids.box[boxUid]) {
113
+ seenUids.box[boxUid] = true;
114
+ const offset = boxUid * 4;
115
+ if (
116
+ x1 <= bboxes[offset + 2] &&
117
+ y1 <= bboxes[offset + 3] &&
118
+ x2 >= bboxes[offset + 0] &&
119
+ y2 >= bboxes[offset + 1] &&
120
+ (!predicate || predicate(this.boxKeys[boxUid]))
121
+ ) {
122
+ if (queryArgs.hitTest) {
123
+ result.push(true);
124
+ return true;
125
+ }
126
+ result.push({
127
+ key: this.boxKeys[boxUid],
128
+ x1: bboxes[offset],
129
+ y1: bboxes[offset + 1],
130
+ x2: bboxes[offset + 2],
131
+ y2: bboxes[offset + 3]
132
+ });
133
+ }
134
+ }
135
+ }
136
+ }
137
+ const circleCell = this.circleCells[cellIndex];
138
+ if (circleCell != null) {
139
+ const { circles } = this;
140
+ for (const circleUid of circleCell) {
141
+ if (!seenUids.circle[circleUid]) {
142
+ seenUids.circle[circleUid] = true;
143
+ const offset = circleUid * 3;
144
+ if (
145
+ circleAndRectCollide(circles[offset], circles[offset + 1], circles[offset + 2], x1, y1, x2, y2) &&
146
+ (!predicate || predicate(this.circleKeys[circleUid]))
147
+ ) {
148
+ if (queryArgs.hitTest) {
149
+ result.push(true);
150
+ return true;
151
+ }
152
+ const x = circles[offset];
153
+ const y = circles[offset + 1];
154
+ const radius = circles[offset + 2];
155
+ result.push({
156
+ key: this.circleKeys[circleUid],
157
+ x1: x - radius,
158
+ y1: y - radius,
159
+ x2: x + radius,
160
+ y2: y + radius
161
+ });
162
+ }
163
+ }
164
+ }
165
+ }
166
+ }
111
167
  }
112
168
 
113
- _queryCircle(x, y, radius, hitTest, predicate) {
169
+ #queryCircle(x, y, radius, hitTest, predicate) {
114
170
  // Insert circle into grid for all cells in the circumscribing square
115
171
  // It's more than necessary (by a factor of 4/PI), but fast to insert
116
172
  const x1 = x - radius;
@@ -127,192 +183,130 @@ class GridIndex {
127
183
  const result = [];
128
184
  const queryArgs = {
129
185
  hitTest,
130
- circle: { x: x, y: y, radius: radius },
186
+ circle: { x, y, radius },
131
187
  seenUids: { box: {}, circle: {} }
132
188
  };
133
- this._forEachCell(x1, y1, x2, y2, this._queryCellCircle, result, queryArgs, predicate);
189
+ this.#forEachCell(x1, y1, x2, y2, queryCellCircle, result, queryArgs, predicate);
134
190
  return hitTest ? result.length > 0 : result;
135
- }
136
-
137
- query(x1, y1, x2, y2, predicate) {
138
- return this._query(x1, y1, x2, y2, false, predicate);
139
- }
140
191
 
141
- hitTest(x1, y1, x2, y2, predicate) {
142
- return this._query(x1, y1, x2, y2, true, predicate);
143
- }
144
-
145
- hitTestCircle(x, y, radius, predicate) {
146
- return this._queryCircle(x, y, radius, true, predicate);
147
- }
148
-
149
- _queryCell(x1, y1, x2, y2, cellIndex, result, queryArgs, predicate) {
150
- const seenUids = queryArgs.seenUids;
151
- const boxCell = this.boxCells[cellIndex];
152
- if (boxCell !== null) {
153
- const bboxes = this.bboxes;
154
- for (const boxUid of boxCell) {
155
- if (!seenUids.box[boxUid]) {
156
- seenUids.box[boxUid] = true;
157
- const offset = boxUid * 4;
158
- if (
159
- x1 <= bboxes[offset + 2] &&
160
- y1 <= bboxes[offset + 3] &&
161
- x2 >= bboxes[offset + 0] &&
162
- y2 >= bboxes[offset + 1] &&
163
- (!predicate || predicate(this.boxKeys[boxUid]))
164
- ) {
165
- if (queryArgs.hitTest) {
192
+ function queryCellCircle(x1, y1, x2, y2, cellIndex, result, queryArgs, predicate) {
193
+ const { circle, seenUids } = queryArgs;
194
+ const boxCell = this.boxCells[cellIndex];
195
+ if (boxCell != null) {
196
+ const { bboxes } = this;
197
+ for (const boxUid of boxCell) {
198
+ if (!seenUids.box[boxUid]) {
199
+ seenUids.box[boxUid] = true;
200
+ const offset = boxUid * 4;
201
+ if (
202
+ circleAndRectCollide(
203
+ circle.x,
204
+ circle.y,
205
+ circle.radius,
206
+ bboxes[offset + 0],
207
+ bboxes[offset + 1],
208
+ bboxes[offset + 2],
209
+ bboxes[offset + 3]
210
+ ) &&
211
+ (!predicate || predicate(this.boxKeys[boxUid]))
212
+ ) {
166
213
  result.push(true);
167
214
  return true;
168
215
  }
169
- result.push({
170
- key: this.boxKeys[boxUid],
171
- x1: bboxes[offset],
172
- y1: bboxes[offset + 1],
173
- x2: bboxes[offset + 2],
174
- y2: bboxes[offset + 3]
175
- });
176
216
  }
177
217
  }
178
218
  }
179
- }
180
- const circleCell = this.circleCells[cellIndex];
181
- if (circleCell !== null) {
182
- const circles = this.circles;
183
- for (const circleUid of circleCell) {
184
- if (!seenUids.circle[circleUid]) {
185
- seenUids.circle[circleUid] = true;
186
- const offset = circleUid * 3;
187
- if (
188
- this._circleAndRectCollide(circles[offset], circles[offset + 1], circles[offset + 2], x1, y1, x2, y2) &&
189
- (!predicate || predicate(this.circleKeys[circleUid]))
190
- ) {
191
- if (queryArgs.hitTest) {
219
+
220
+ const circleCell = this.circleCells[cellIndex];
221
+ if (circleCell != null) {
222
+ const { circles } = this;
223
+ for (const circleUid of circleCell) {
224
+ if (!seenUids.circle[circleUid]) {
225
+ seenUids.circle[circleUid] = true;
226
+ const offset = circleUid * 3;
227
+ if (
228
+ circlesCollide(
229
+ circles[offset],
230
+ circles[offset + 1],
231
+ circles[offset + 2],
232
+ circle.x,
233
+ circle.y,
234
+ circle.radius
235
+ ) &&
236
+ (!predicate || predicate(this.circleKeys[circleUid]))
237
+ ) {
192
238
  result.push(true);
193
239
  return true;
194
240
  }
195
- const x = circles[offset];
196
- const y = circles[offset + 1];
197
- const radius = circles[offset + 2];
198
- result.push({
199
- key: this.circleKeys[circleUid],
200
- x1: x - radius,
201
- y1: y - radius,
202
- x2: x + radius,
203
- y2: y + radius
204
- });
205
241
  }
206
242
  }
207
243
  }
208
244
  }
209
245
  }
210
246
 
211
- _queryCellCircle(x1, y1, x2, y2, cellIndex, result, queryArgs, predicate) {
212
- const circle = queryArgs.circle;
213
- const seenUids = queryArgs.seenUids;
214
- const boxCell = this.boxCells[cellIndex];
215
- if (boxCell !== null) {
216
- const bboxes = this.bboxes;
217
- for (const boxUid of boxCell) {
218
- if (!seenUids.box[boxUid]) {
219
- seenUids.box[boxUid] = true;
220
- const offset = boxUid * 4;
221
- if (
222
- this._circleAndRectCollide(
223
- circle.x,
224
- circle.y,
225
- circle.radius,
226
- bboxes[offset + 0],
227
- bboxes[offset + 1],
228
- bboxes[offset + 2],
229
- bboxes[offset + 3]
230
- ) &&
231
- (!predicate || predicate(this.boxKeys[boxUid]))
232
- ) {
233
- result.push(true);
234
- return true;
235
- }
236
- }
237
- }
238
- }
247
+ query(x1, y1, x2, y2, predicate) {
248
+ return this.#query(x1, y1, x2, y2, false, predicate);
249
+ }
239
250
 
240
- const circleCell = this.circleCells[cellIndex];
241
- if (circleCell !== null) {
242
- const circles = this.circles;
243
- for (const circleUid of circleCell) {
244
- if (!seenUids.circle[circleUid]) {
245
- seenUids.circle[circleUid] = true;
246
- const offset = circleUid * 3;
247
- if (
248
- this._circlesCollide(
249
- circles[offset],
250
- circles[offset + 1],
251
- circles[offset + 2],
252
- circle.x,
253
- circle.y,
254
- circle.radius
255
- ) &&
256
- (!predicate || predicate(this.circleKeys[circleUid]))
257
- ) {
258
- result.push(true);
259
- return true;
260
- }
261
- }
262
- }
263
- }
251
+ hitTest(x1, y1, x2, y2, predicate) {
252
+ return this.#query(x1, y1, x2, y2, true, predicate);
264
253
  }
265
254
 
266
- _forEachCell(x1, y1, x2, y2, fn, arg1, arg2, predicate) {
267
- const cx1 = this._convertToXCellCoord(x1);
268
- const cy1 = this._convertToYCellCoord(y1);
269
- const cx2 = this._convertToXCellCoord(x2);
270
- const cy2 = this._convertToYCellCoord(y2);
255
+ hitTestCircle(x, y, radius, predicate) {
256
+ return this.#queryCircle(x, y, radius, true, predicate);
257
+ }
258
+
259
+ #forEachCell(x1, y1, x2, y2, fn, ...args) {
260
+ const { xCellCount, yCellCount, xScale, yScale } = this;
261
+ const cx1 = cellX(x1);
262
+ const cy1 = cellY(y1);
263
+ const cx2 = cellX(x2);
264
+ const cy2 = cellY(y2);
271
265
 
272
266
  for (let x = cx1; x <= cx2; x++) {
273
267
  for (let y = cy1; y <= cy2; y++) {
274
- const cellIndex = this.xCellCount * y + x;
275
- if (fn.call(this, x1, y1, x2, y2, cellIndex, arg1, arg2, predicate)) return;
268
+ const cellIndex = xCellCount * y + x;
269
+ if (fn.call(this, x1, y1, x2, y2, cellIndex, ...args)) return;
276
270
  }
277
271
  }
278
- }
279
272
 
280
- _convertToXCellCoord(x) {
281
- return Math.max(0, Math.min(this.xCellCount - 1, Math.floor(x * this.xScale)));
282
- }
273
+ function cellX(x) {
274
+ return clamp(Math.floor(x * xScale), 0, xCellCount - 1);
275
+ }
283
276
 
284
- _convertToYCellCoord(y) {
285
- return Math.max(0, Math.min(this.yCellCount - 1, Math.floor(y * this.yScale)));
277
+ function cellY(y) {
278
+ return clamp(Math.floor(y * yScale), 0, yCellCount - 1);
279
+ }
286
280
  }
281
+ }
287
282
 
288
- _circlesCollide(x1, y1, r1, x2, y2, r2) {
289
- const dx = x2 - x1;
290
- const dy = y2 - y1;
291
- const bothRadii = r1 + r2;
292
- return bothRadii * bothRadii > dx * dx + dy * dy;
293
- }
283
+ module.exports = GridIndex;
294
284
 
295
- _circleAndRectCollide(circleX, circleY, radius, x1, y1, x2, y2) {
296
- const halfRectWidth = (x2 - x1) / 2;
297
- const distX = Math.abs(circleX - (x1 + halfRectWidth));
298
- if (distX > halfRectWidth + radius) {
299
- return false;
300
- }
285
+ function circlesCollide(x1, y1, r1, x2, y2, r2) {
286
+ const dx = x2 - x1;
287
+ const dy = y2 - y1;
288
+ const bothRadii = r1 + r2;
289
+ return bothRadii * bothRadii > dx * dx + dy * dy;
290
+ }
301
291
 
302
- const halfRectHeight = (y2 - y1) / 2;
303
- const distY = Math.abs(circleY - (y1 + halfRectHeight));
304
- if (distY > halfRectHeight + radius) {
305
- return false;
306
- }
292
+ function circleAndRectCollide(circleX, circleY, radius, x1, y1, x2, y2) {
293
+ const halfRectWidth = (x2 - x1) / 2;
294
+ const distX = Math.abs(circleX - (x1 + halfRectWidth));
295
+ if (distX > halfRectWidth + radius) {
296
+ return false;
297
+ }
307
298
 
308
- if (distX <= halfRectWidth || distY <= halfRectHeight) {
309
- return true;
310
- }
299
+ const halfRectHeight = (y2 - y1) / 2;
300
+ const distY = Math.abs(circleY - (y1 + halfRectHeight));
301
+ if (distY > halfRectHeight + radius) {
302
+ return false;
303
+ }
311
304
 
312
- const dx = distX - halfRectWidth;
313
- const dy = distY - halfRectHeight;
314
- return dx * dx + dy * dy <= radius * radius;
305
+ if (distX <= halfRectWidth || distY <= halfRectHeight) {
306
+ return true;
315
307
  }
316
- }
317
308
 
318
- module.exports = GridIndex;
309
+ const dx = distX - halfRectWidth;
310
+ const dy = distY - halfRectHeight;
311
+ return dx * dx + dy * dy <= radius * radius;
312
+ }
@@ -1,75 +1,78 @@
1
+ const { Formatted } = require('../style-spec/expression/definitions/formatted');
2
+
1
3
  module.exports = function (features) {
2
- const leftIndex = {};
3
- const rightIndex = {};
4
+ const leftIndex = new Map();
5
+ const rightIndex = new Map();
4
6
  const mergedFeatures = [];
5
7
  let mergedIndex = 0;
6
8
 
7
- function add(k) {
8
- mergedFeatures.push(features[k]);
9
- mergedIndex++;
10
- }
11
-
12
- function mergeFromRight(leftKey, rightKey, geom) {
13
- const i = rightIndex[leftKey];
14
- delete rightIndex[leftKey];
15
- rightIndex[rightKey] = i;
16
-
17
- mergedFeatures[i].geometry[0].pop();
18
- mergedFeatures[i].geometry[0] = mergedFeatures[i].geometry[0].concat(geom[0]);
19
- return i;
20
- }
21
-
22
- function mergeFromLeft(leftKey, rightKey, geom) {
23
- const i = leftIndex[rightKey];
24
- delete leftIndex[rightKey];
25
- leftIndex[leftKey] = i;
26
-
27
- mergedFeatures[i].geometry[0].shift();
28
- mergedFeatures[i].geometry[0] = geom[0].concat(mergedFeatures[i].geometry[0]);
29
- return i;
30
- }
31
-
32
- function getKey(text, geom, onRight) {
33
- const point = onRight ? geom[0][geom[0].length - 1] : geom[0][0];
34
- return `${text}:${point.x}:${point.y}`;
35
- }
36
-
37
9
  for (let k = 0; k < features.length; k++) {
38
- const feature = features[k];
39
- const geom = feature.geometry;
40
- const text = feature.text;
10
+ const { geometry, text: featureText } = features[k];
11
+ const text = featureText instanceof Formatted ? featureText.toString() : featureText;
41
12
 
42
13
  if (!text) {
43
14
  add(k);
44
15
  continue;
45
16
  }
46
17
 
47
- const leftKey = getKey(text, geom);
48
- const rightKey = getKey(text, geom, true);
18
+ const leftKey = getKey(text, geometry);
19
+ const rightKey = getKey(text, geometry, true);
49
20
 
50
- if (leftKey in rightIndex && rightKey in leftIndex && rightIndex[leftKey] !== leftIndex[rightKey]) {
21
+ if (rightIndex.has(leftKey) && leftIndex.has(rightKey) && rightIndex.get(leftKey) !== leftIndex.get(rightKey)) {
51
22
  // found lines with the same text adjacent to both ends of the current line, merge all three
52
- const j = mergeFromLeft(leftKey, rightKey, geom);
23
+ const j = mergeFromLeft(leftKey, rightKey, geometry);
53
24
  const i = mergeFromRight(leftKey, rightKey, mergedFeatures[j].geometry);
54
25
 
55
- delete leftIndex[leftKey];
56
- delete rightIndex[rightKey];
26
+ leftIndex.delete(leftKey);
27
+ rightIndex.delete(rightKey);
57
28
 
58
- rightIndex[getKey(text, mergedFeatures[i].geometry, true)] = i;
29
+ rightIndex.set(getKey(text, mergedFeatures[i].geometry, true), i);
59
30
  mergedFeatures[j].geometry = null;
60
- } else if (leftKey in rightIndex) {
31
+ } else if (rightIndex.has(leftKey)) {
61
32
  // found mergeable line adjacent to the start of the current line, merge
62
- mergeFromRight(leftKey, rightKey, geom);
63
- } else if (rightKey in leftIndex) {
33
+ mergeFromRight(leftKey, rightKey, geometry);
34
+ } else if (leftIndex.has(rightKey)) {
64
35
  // found mergeable line adjacent to the end of the current line, merge
65
- mergeFromLeft(leftKey, rightKey, geom);
36
+ mergeFromLeft(leftKey, rightKey, geometry);
66
37
  } else {
67
38
  // no adjacent lines, add as a new item
68
39
  add(k);
69
- leftIndex[leftKey] = mergedIndex - 1;
70
- rightIndex[rightKey] = mergedIndex - 1;
40
+ leftIndex.set(leftKey, mergedIndex - 1);
41
+ rightIndex.set(rightKey, mergedIndex - 1);
71
42
  }
72
43
  }
73
44
 
74
45
  return mergedFeatures.filter(f => f.geometry);
46
+
47
+ function add(k) {
48
+ mergedFeatures.push(features[k]);
49
+ mergedIndex++;
50
+ }
51
+
52
+ function mergeFromRight(leftKey, rightKey, [geom]) {
53
+ const i = rightIndex.get(leftKey);
54
+ rightIndex.delete(leftKey);
55
+ rightIndex.set(rightKey, i);
56
+
57
+ const feature = mergedFeatures[i];
58
+ feature.geometry[0].pop();
59
+ feature.geometry[0].push(...geom);
60
+ return i;
61
+ }
62
+
63
+ function mergeFromLeft(leftKey, rightKey, [geom]) {
64
+ const i = leftIndex.get(rightKey);
65
+ leftIndex.delete(rightKey);
66
+ leftIndex.set(leftKey, i);
67
+
68
+ const feature = mergedFeatures[i];
69
+ feature.geometry[0].shift();
70
+ feature.geometry[0].unshift(...geom);
71
+ return i;
72
+ }
73
+
74
+ function getKey(text, [geom], onRight) {
75
+ const { x, y } = geom.at(onRight ? -1 : 0);
76
+ return `${text}:${x}:${y}`;
77
+ }
75
78
  };
@@ -1,4 +1,4 @@
1
- const { register } = require('../util/web_worker_transfer');
1
+ const { register } = require('../util/transfer_registry');
2
2
 
3
3
  class OpacityState {
4
4
  constructor() {