@mapwhit/tilerenderer 1.2.2 → 1.4.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.
- package/build/min/package.json +1 -1
- package/package.json +1 -1
- package/src/data/array_types.js +115 -64
- package/src/data/bucket/circle_bucket.js +42 -5
- package/src/data/bucket/fill_bucket.js +31 -13
- package/src/data/bucket/fill_extrusion_bucket.js +8 -6
- package/src/data/bucket/line_bucket.js +38 -14
- package/src/data/bucket/symbol_attributes.js +13 -5
- package/src/data/bucket/symbol_bucket.js +87 -33
- package/src/data/bucket/symbol_collision_buffers.js +1 -1
- package/src/data/bucket.js +3 -1
- package/src/data/feature_index.js +24 -11
- package/src/data/segment.js +15 -7
- package/src/render/draw_circle.js +45 -4
- package/src/render/draw_symbol.js +190 -22
- package/src/render/painter.js +1 -1
- package/src/source/geojson_source.js +118 -21
- package/src/source/geojson_source_diff.js +148 -0
- package/src/source/geojson_tiler.js +89 -0
- package/src/source/source.js +16 -5
- package/src/source/source_cache.js +6 -6
- package/src/source/source_state.js +4 -2
- package/src/source/tile.js +5 -3
- package/src/source/vector_tile_source.js +2 -0
- package/src/source/worker_tile.js +4 -2
- package/src/style/pauseable_placement.js +39 -7
- package/src/style/style.js +86 -34
- package/src/style/style_layer/circle_style_layer_properties.js +8 -1
- package/src/style/style_layer/fill_style_layer_properties.js +8 -1
- package/src/style/style_layer/line_style_layer_properties.js +4 -0
- package/src/style/style_layer/symbol_style_layer_properties.js +17 -2
- package/src/style-spec/reference/v8.json +161 -4
- package/src/symbol/one_em.js +4 -0
- package/src/symbol/placement.js +406 -173
- package/src/symbol/projection.js +3 -3
- package/src/symbol/quads.js +1 -6
- package/src/symbol/shaping.js +16 -27
- package/src/symbol/symbol_layout.js +243 -81
- package/src/util/vectortile_to_geojson.js +3 -4
- package/src/source/geojson_worker_source.js +0 -97
|
@@ -9,7 +9,10 @@ export const dynamicLayoutAttributes = createLayout([{ name: 'a_projected_pos',
|
|
|
9
9
|
|
|
10
10
|
export const placementOpacityAttributes = createLayout([{ name: 'a_fade_opacity', components: 1, type: 'Uint32' }], 4);
|
|
11
11
|
|
|
12
|
-
export const collisionVertexAttributes = createLayout([
|
|
12
|
+
export const collisionVertexAttributes = createLayout([
|
|
13
|
+
{ name: 'a_placed', components: 2, type: 'Uint8' },
|
|
14
|
+
{ name: 'a_shift', components: 2, type: 'Float32' }
|
|
15
|
+
]);
|
|
13
16
|
|
|
14
17
|
export const collisionBox = createLayout([
|
|
15
18
|
// the box is centered around the anchor point
|
|
@@ -70,13 +73,16 @@ export const placement = createLayout([
|
|
|
70
73
|
{ type: 'Float32', name: 'lineOffsetX' },
|
|
71
74
|
{ type: 'Float32', name: 'lineOffsetY' },
|
|
72
75
|
{ type: 'Uint8', name: 'writingMode' },
|
|
73
|
-
{ type: 'Uint8', name: 'hidden' }
|
|
76
|
+
{ type: 'Uint8', name: 'hidden' },
|
|
77
|
+
{ type: 'Uint32', name: 'crossTileID' }
|
|
74
78
|
]);
|
|
75
79
|
|
|
76
80
|
export const symbolInstance = createLayout([
|
|
77
81
|
{ type: 'Int16', name: 'anchorX' },
|
|
78
82
|
{ type: 'Int16', name: 'anchorY' },
|
|
79
|
-
{ type: 'Int16', name: '
|
|
83
|
+
{ type: 'Int16', name: 'rightJustifiedTextSymbolIndex' },
|
|
84
|
+
{ type: 'Int16', name: 'centerJustifiedTextSymbolIndex' },
|
|
85
|
+
{ type: 'Int16', name: 'leftJustifiedTextSymbolIndex' },
|
|
80
86
|
{ type: 'Int16', name: 'verticalPlacedTextSymbolIndex' },
|
|
81
87
|
{ type: 'Uint16', name: 'key' },
|
|
82
88
|
{ type: 'Uint16', name: 'textBoxStartIndex' },
|
|
@@ -84,10 +90,12 @@ export const symbolInstance = createLayout([
|
|
|
84
90
|
{ type: 'Uint16', name: 'iconBoxStartIndex' },
|
|
85
91
|
{ type: 'Uint16', name: 'iconBoxEndIndex' },
|
|
86
92
|
{ type: 'Uint16', name: 'featureIndex' },
|
|
87
|
-
{ type: 'Uint16', name: '
|
|
93
|
+
{ type: 'Uint16', name: 'numHorizontalGlyphVertices' },
|
|
88
94
|
{ type: 'Uint16', name: 'numVerticalGlyphVertices' },
|
|
89
95
|
{ type: 'Uint16', name: 'numIconVertices' },
|
|
90
|
-
{ type: 'Uint32', name: 'crossTileID' }
|
|
96
|
+
{ type: 'Uint32', name: 'crossTileID' },
|
|
97
|
+
{ type: 'Float32', name: 'textBoxScale' },
|
|
98
|
+
{ type: 'Float32', name: 'radialTextOffset' }
|
|
91
99
|
]);
|
|
92
100
|
|
|
93
101
|
export const glyphOffset = createLayout([{ type: 'Float32', name: 'offsetX' }]);
|
|
@@ -86,6 +86,7 @@ export default class SymbolBucket {
|
|
|
86
86
|
this.pixelRatio = options.pixelRatio;
|
|
87
87
|
this.sourceLayerIndex = options.sourceLayerIndex;
|
|
88
88
|
this.hasPattern = false;
|
|
89
|
+
this.sortKeyRanges = [];
|
|
89
90
|
|
|
90
91
|
const layer = this.layers[0];
|
|
91
92
|
const unevaluatedLayoutValues = layer._unevaluatedLayout._values;
|
|
@@ -94,7 +95,10 @@ export default class SymbolBucket {
|
|
|
94
95
|
this.iconSizeData = getSizeData(this.zoom, unevaluatedLayoutValues['icon-size']);
|
|
95
96
|
|
|
96
97
|
const layout = this.layers[0]._layout;
|
|
97
|
-
const
|
|
98
|
+
const sortKey = layout.get('symbol-sort-key');
|
|
99
|
+
const zOrder = layout.get('symbol-z-order');
|
|
100
|
+
this.sortFeaturesByKey = zOrder !== 'viewport-y' && sortKey.constantOr(1) !== undefined;
|
|
101
|
+
const zOrderByViewportY = zOrder === 'viewport-y' || (zOrder === 'auto' && !this.sortFeaturesByKey);
|
|
98
102
|
this.sortFeaturesByY =
|
|
99
103
|
zOrderByViewportY &&
|
|
100
104
|
(layout.get('text-allow-overlap') ||
|
|
@@ -102,6 +106,8 @@ export default class SymbolBucket {
|
|
|
102
106
|
layout.get('text-ignore-placement') ||
|
|
103
107
|
layout.get('icon-ignore-placement'));
|
|
104
108
|
|
|
109
|
+
this.stateDependentLayerIds = this.layers.filter(l => l.isStateDependent()).map(l => l.id);
|
|
110
|
+
|
|
105
111
|
this.sourceID = options.sourceID;
|
|
106
112
|
}
|
|
107
113
|
|
|
@@ -140,6 +146,7 @@ export default class SymbolBucket {
|
|
|
140
146
|
(textField.value.kind !== 'constant' || textField.value.value.toString().length > 0) &&
|
|
141
147
|
(textFont.value.kind !== 'constant' || textFont.value.value.length > 0);
|
|
142
148
|
const hasIcon = iconImage.value.kind !== 'constant' || iconImage.value.value?.length > 0;
|
|
149
|
+
const symbolSortKey = layout.get('symbol-sort-key');
|
|
143
150
|
|
|
144
151
|
this.features = [];
|
|
145
152
|
|
|
@@ -151,7 +158,7 @@ export default class SymbolBucket {
|
|
|
151
158
|
const stacks = options.glyphDependencies;
|
|
152
159
|
const globalProperties = new EvaluationParameters(this.zoom);
|
|
153
160
|
|
|
154
|
-
for (const { feature, index, sourceLayerIndex } of features) {
|
|
161
|
+
for (const { feature, id, index, sourceLayerIndex } of features) {
|
|
155
162
|
if (!layer._featureFilter(globalProperties, feature)) {
|
|
156
163
|
continue;
|
|
157
164
|
}
|
|
@@ -178,18 +185,19 @@ export default class SymbolBucket {
|
|
|
178
185
|
continue;
|
|
179
186
|
}
|
|
180
187
|
|
|
188
|
+
const sortKey = this.sortFeaturesByKey ? symbolSortKey.evaluate(feature, {}) : undefined;
|
|
189
|
+
|
|
181
190
|
const symbolFeature = {
|
|
191
|
+
id,
|
|
182
192
|
text,
|
|
183
193
|
icon,
|
|
184
194
|
index,
|
|
185
195
|
sourceLayerIndex,
|
|
186
196
|
geometry: loadGeometry(feature),
|
|
187
197
|
properties: feature.properties,
|
|
188
|
-
type: VectorTileFeature.types[feature.type]
|
|
198
|
+
type: VectorTileFeature.types[feature.type],
|
|
199
|
+
sortKey
|
|
189
200
|
};
|
|
190
|
-
if (typeof feature.id !== 'undefined') {
|
|
191
|
-
symbolFeature.id = feature.id;
|
|
192
|
-
}
|
|
193
201
|
this.features.push(symbolFeature);
|
|
194
202
|
|
|
195
203
|
if (icon) {
|
|
@@ -214,6 +222,13 @@ export default class SymbolBucket {
|
|
|
214
222
|
// It's better to place labels on one long line than on many short segments.
|
|
215
223
|
this.features = mergeLines(this.features);
|
|
216
224
|
}
|
|
225
|
+
|
|
226
|
+
if (this.sortFeaturesByKey) {
|
|
227
|
+
this.features.sort((a, b) => {
|
|
228
|
+
// a.sortKey is always a number when sortFeaturesByKey is true
|
|
229
|
+
return a.sortKey - b.sortKey;
|
|
230
|
+
});
|
|
231
|
+
}
|
|
217
232
|
}
|
|
218
233
|
|
|
219
234
|
update(states, vtLayer, imagePositions) {
|
|
@@ -295,7 +310,7 @@ export default class SymbolBucket {
|
|
|
295
310
|
lineLength
|
|
296
311
|
) {
|
|
297
312
|
const { indexArray, layoutVertexArray, dynamicLayoutVertexArray, segments } = arrays;
|
|
298
|
-
const segment = segments.prepareSegment(4 * quads.length, layoutVertexArray, indexArray);
|
|
313
|
+
const segment = segments.prepareSegment(4 * quads.length, layoutVertexArray, indexArray, feature.sortKey);
|
|
299
314
|
const glyphOffsetArrayStart = this.glyphOffsetArray.length;
|
|
300
315
|
const vertexStartIndex = segment.vertexLength;
|
|
301
316
|
const { x: lax, y: lay } = labelAnchor;
|
|
@@ -335,7 +350,9 @@ export default class SymbolBucket {
|
|
|
335
350
|
lineOffset[0],
|
|
336
351
|
lineOffset[1],
|
|
337
352
|
writingMode,
|
|
338
|
-
false
|
|
353
|
+
false,
|
|
354
|
+
// The crossTileID is only filled/used on the foreground for dynamic text anchors
|
|
355
|
+
0
|
|
339
356
|
);
|
|
340
357
|
|
|
341
358
|
arrays.programConfigurations.populatePaintArrays(arrays.layoutVertexArray.length, feature, feature.index, {
|
|
@@ -439,6 +456,43 @@ export default class SymbolBucket {
|
|
|
439
456
|
}
|
|
440
457
|
}
|
|
441
458
|
|
|
459
|
+
getSortedSymbolIndexes(angle) {
|
|
460
|
+
if (this.sortedAngle === angle && this.symbolInstanceIndexes !== undefined) {
|
|
461
|
+
return this.symbolInstanceIndexes;
|
|
462
|
+
}
|
|
463
|
+
const sin = Math.sin(angle);
|
|
464
|
+
const cos = Math.cos(angle);
|
|
465
|
+
const rotatedYs = [];
|
|
466
|
+
const featureIndexes = [];
|
|
467
|
+
const result = [];
|
|
468
|
+
|
|
469
|
+
for (let i = 0; i < this.symbolInstances.length; ++i) {
|
|
470
|
+
result.push(i);
|
|
471
|
+
const symbolInstance = this.symbolInstances.get(i);
|
|
472
|
+
rotatedYs.push(Math.round(sin * symbolInstance.anchorX + cos * symbolInstance.anchorY) | 0);
|
|
473
|
+
featureIndexes.push(symbolInstance.featureIndex);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
result.sort((aIndex, bIndex) => {
|
|
477
|
+
return rotatedYs[aIndex] - rotatedYs[bIndex] || featureIndexes[bIndex] - featureIndexes[aIndex];
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
return result;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
addToSortKeyRanges(symbolInstanceIndex, sortKey) {
|
|
484
|
+
const last = this.sortKeyRanges[this.sortKeyRanges.length - 1];
|
|
485
|
+
if (last && last.sortKey === sortKey) {
|
|
486
|
+
last.symbolInstanceEnd = symbolInstanceIndex + 1;
|
|
487
|
+
} else {
|
|
488
|
+
this.sortKeyRanges.push({
|
|
489
|
+
sortKey,
|
|
490
|
+
symbolInstanceStart: symbolInstanceIndex,
|
|
491
|
+
symbolInstanceEnd: symbolInstanceIndex + 1
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
442
496
|
sortFeatures(angle) {
|
|
443
497
|
if (!this.sortFeaturesByY) {
|
|
444
498
|
return;
|
|
@@ -447,7 +501,6 @@ export default class SymbolBucket {
|
|
|
447
501
|
if (this.sortedAngle === angle) {
|
|
448
502
|
return;
|
|
449
503
|
}
|
|
450
|
-
this.sortedAngle = angle;
|
|
451
504
|
|
|
452
505
|
// The current approach to sorting doesn't sort across segments so don't try.
|
|
453
506
|
// Sorting within segments separately seemed not to be worth the complexity.
|
|
@@ -460,35 +513,36 @@ export default class SymbolBucket {
|
|
|
460
513
|
// sorted order.
|
|
461
514
|
|
|
462
515
|
// To avoid sorting the actual symbolInstance array we sort an array of indexes.
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
const rotatedYs = new Array(slen);
|
|
466
|
-
const featureIndexes = new Array(slen);
|
|
467
|
-
const sin = Math.sin(angle);
|
|
468
|
-
const cos = Math.cos(angle);
|
|
469
|
-
for (let i = 0; i < slen; i++) {
|
|
470
|
-
symbolInstanceIndexes[i] = i;
|
|
471
|
-
const { anchorX, anchorY, featureIndex } = this.symbolInstances.get(i);
|
|
472
|
-
rotatedYs[i] = Math.round(sin * anchorX + cos * anchorY) | 0;
|
|
473
|
-
featureIndexes[i] = featureIndex;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
symbolInstanceIndexes.sort((a, b) => rotatedYs[a] - rotatedYs[b] || featureIndexes[b] - featureIndexes[a]);
|
|
516
|
+
this.symbolInstanceIndexes = this.getSortedSymbolIndexes(angle);
|
|
517
|
+
this.sortedAngle = angle;
|
|
477
518
|
|
|
478
519
|
this.text.indexArray.clear();
|
|
479
520
|
this.icon.indexArray.clear();
|
|
480
521
|
|
|
481
|
-
this.featureSortOrder = new Array(
|
|
482
|
-
|
|
483
|
-
for (let i = 0; i <
|
|
484
|
-
const index = symbolInstanceIndexes[i];
|
|
485
|
-
const {
|
|
486
|
-
|
|
487
|
-
|
|
522
|
+
this.featureSortOrder = new Array(this.symbolInstanceIndexes.length);
|
|
523
|
+
|
|
524
|
+
for (let i = 0; i < this.symbolInstanceIndexes.length; i++) {
|
|
525
|
+
const index = this.symbolInstanceIndexes[i];
|
|
526
|
+
const {
|
|
527
|
+
centerJustifiedTextSymbolIndex,
|
|
528
|
+
featureIndex,
|
|
529
|
+
leftJustifiedTextSymbolIndex,
|
|
530
|
+
rightJustifiedTextSymbolIndex,
|
|
531
|
+
verticalPlacedTextSymbolIndex
|
|
532
|
+
} = this.symbolInstances.get(index);
|
|
533
|
+
this.featureSortOrder.push(featureIndex);
|
|
534
|
+
|
|
535
|
+
[rightJustifiedTextSymbolIndex, centerJustifiedTextSymbolIndex, leftJustifiedTextSymbolIndex].forEach(
|
|
536
|
+
(index, i, array) => {
|
|
537
|
+
// Only add a given index the first time it shows up,
|
|
538
|
+
// to avoid duplicate opacity entries when multiple justifications
|
|
539
|
+
// share the same glyphs.
|
|
540
|
+
if (index >= 0 && array.indexOf(index) === i) {
|
|
541
|
+
this.addIndicesForPlacedTextSymbol(index);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
);
|
|
488
545
|
|
|
489
|
-
if (horizontalPlacedTextSymbolIndex >= 0) {
|
|
490
|
-
this.addIndicesForPlacedTextSymbol(horizontalPlacedTextSymbolIndex);
|
|
491
|
-
}
|
|
492
546
|
if (verticalPlacedTextSymbolIndex >= 0) {
|
|
493
547
|
this.addIndicesForPlacedTextSymbol(verticalPlacedTextSymbolIndex);
|
|
494
548
|
}
|
package/src/data/bucket.js
CHANGED
|
@@ -49,6 +49,8 @@ export function updateBuckets(buckets, style) {
|
|
|
49
49
|
|
|
50
50
|
// swap out the layers in the bucket with the current style layers
|
|
51
51
|
bucket.layers = layers;
|
|
52
|
-
|
|
52
|
+
if (bucket.stateDependentLayerIds?.length) {
|
|
53
|
+
bucket.stateDependentLayers = layers.filter(layer => layer.isStateDependent());
|
|
54
|
+
}
|
|
53
55
|
}
|
|
54
56
|
}
|
|
@@ -9,12 +9,13 @@ import { FeatureIndexArray } from './array_types.js';
|
|
|
9
9
|
import EXTENT from './extent.js';
|
|
10
10
|
import loadGeometry from './load_geometry.js';
|
|
11
11
|
|
|
12
|
-
class FeatureIndex {
|
|
13
|
-
constructor(tileID,
|
|
12
|
+
export default class FeatureIndex {
|
|
13
|
+
constructor(tileID, promoteId) {
|
|
14
14
|
this.tileID = tileID;
|
|
15
|
-
this.grid =
|
|
15
|
+
this.grid = new Grid(EXTENT, 16, 0);
|
|
16
16
|
this.grid3D = new Grid(EXTENT, 16, 0);
|
|
17
|
-
this.featureIndexArray =
|
|
17
|
+
this.featureIndexArray = new FeatureIndexArray();
|
|
18
|
+
this.promoteId = promoteId;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
insert(feature, geometry, featureIndex, sourceLayerIndex, bucketIndex, is3D) {
|
|
@@ -91,14 +92,14 @@ class FeatureIndex {
|
|
|
91
92
|
|
|
92
93
|
const match = this.featureIndexArray.get(index);
|
|
93
94
|
let featureGeometry = null;
|
|
94
|
-
const intersectionTest = (feature, styleLayer) => {
|
|
95
|
+
const intersectionTest = (feature, styleLayer, id) => {
|
|
95
96
|
if (!featureGeometry) {
|
|
96
97
|
featureGeometry = loadGeometry(feature);
|
|
97
98
|
}
|
|
98
99
|
let featureState = {};
|
|
99
|
-
if (
|
|
100
|
+
if (id !== undefined) {
|
|
100
101
|
// `feature-state` expression evaluation requires feature state to be available
|
|
101
|
-
featureState = sourceFeatureState.getState(styleLayer.sourceLayer || '_geojsonTileLayer',
|
|
102
|
+
featureState = sourceFeatureState.getState(styleLayer.sourceLayer || '_geojsonTileLayer', id);
|
|
102
103
|
}
|
|
103
104
|
return styleLayer.queryIntersectsFeature(
|
|
104
105
|
queryGeometry,
|
|
@@ -149,6 +150,8 @@ class FeatureIndex {
|
|
|
149
150
|
return;
|
|
150
151
|
}
|
|
151
152
|
|
|
153
|
+
const id = this.getId(feature, sourceLayerName);
|
|
154
|
+
|
|
152
155
|
const { x, y, z } = this.tileID.canonical;
|
|
153
156
|
for (const layerID of layerIDs) {
|
|
154
157
|
if (filterLayerIDs && !filterLayerIDs.includes(layerID)) {
|
|
@@ -160,13 +163,13 @@ class FeatureIndex {
|
|
|
160
163
|
continue;
|
|
161
164
|
}
|
|
162
165
|
|
|
163
|
-
const intersectionZ = !intersectionTest || intersectionTest(feature, styleLayer);
|
|
166
|
+
const intersectionZ = !intersectionTest || intersectionTest(feature, styleLayer, id);
|
|
164
167
|
if (!intersectionZ) {
|
|
165
168
|
// Only applied for non-symbol features
|
|
166
169
|
continue;
|
|
167
170
|
}
|
|
168
171
|
|
|
169
|
-
const geojsonFeature = new GeoJSONFeature(feature, z, x, y);
|
|
172
|
+
const geojsonFeature = new GeoJSONFeature(feature, z, x, y, id);
|
|
170
173
|
geojsonFeature.layer = styleLayer;
|
|
171
174
|
const layerResult = (result[layerID] ??= []);
|
|
172
175
|
layerResult.push({ featureIndex, feature: geojsonFeature, intersectionZ });
|
|
@@ -201,9 +204,19 @@ class FeatureIndex {
|
|
|
201
204
|
}
|
|
202
205
|
return result;
|
|
203
206
|
}
|
|
204
|
-
}
|
|
205
207
|
|
|
206
|
-
|
|
208
|
+
getId(feature, sourceLayerId) {
|
|
209
|
+
let id = feature.id;
|
|
210
|
+
if (this.promoteId) {
|
|
211
|
+
const propName = typeof this.promoteId === 'string' ? this.promoteId : this.promoteId[sourceLayerId];
|
|
212
|
+
id = feature.properties[propName];
|
|
213
|
+
if (typeof id === 'boolean') {
|
|
214
|
+
id = Number(id);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return id;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
207
220
|
|
|
208
221
|
function getBounds(geometry) {
|
|
209
222
|
let minX = Number.POSITIVE_INFINITY;
|
package/src/data/segment.js
CHANGED
|
@@ -5,20 +5,27 @@ class SegmentVector {
|
|
|
5
5
|
this.segments = segments;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
prepareSegment(numVertices, layoutVertexArray, indexArray) {
|
|
8
|
+
prepareSegment(numVertices, layoutVertexArray, indexArray, sortKey) {
|
|
9
9
|
if (numVertices > SegmentVector.MAX_VERTEX_ARRAY_LENGTH) {
|
|
10
10
|
warn.once(
|
|
11
11
|
`Max vertices per segment is ${SegmentVector.MAX_VERTEX_ARRAY_LENGTH}: bucket requested ${numVertices}`
|
|
12
12
|
);
|
|
13
13
|
}
|
|
14
14
|
let segment = this.segments.at(-1);
|
|
15
|
-
if (
|
|
15
|
+
if (
|
|
16
|
+
!segment ||
|
|
17
|
+
segment.vertexLength + numVertices > SegmentVector.MAX_VERTEX_ARRAY_LENGTH ||
|
|
18
|
+
segment.sortKey !== sortKey
|
|
19
|
+
) {
|
|
16
20
|
segment = {
|
|
17
21
|
vertexOffset: layoutVertexArray.length,
|
|
18
22
|
primitiveOffset: indexArray.length,
|
|
19
23
|
vertexLength: 0,
|
|
20
24
|
primitiveLength: 0
|
|
21
25
|
};
|
|
26
|
+
if (sortKey !== undefined) {
|
|
27
|
+
segment.sortKey = sortKey;
|
|
28
|
+
}
|
|
22
29
|
this.segments.push(segment);
|
|
23
30
|
}
|
|
24
31
|
return segment;
|
|
@@ -39,11 +46,12 @@ class SegmentVector {
|
|
|
39
46
|
static simpleSegment(vertexOffset, primitiveOffset, vertexLength, primitiveLength) {
|
|
40
47
|
return new SegmentVector([
|
|
41
48
|
{
|
|
42
|
-
vertexOffset
|
|
43
|
-
primitiveOffset
|
|
44
|
-
vertexLength
|
|
45
|
-
primitiveLength
|
|
46
|
-
vaos: {}
|
|
49
|
+
vertexOffset,
|
|
50
|
+
primitiveOffset,
|
|
51
|
+
vertexLength,
|
|
52
|
+
primitiveLength,
|
|
53
|
+
vaos: {},
|
|
54
|
+
sortKey: 0
|
|
47
55
|
}
|
|
48
56
|
]);
|
|
49
57
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import SegmentVector from '../data/segment.js';
|
|
1
2
|
import CullFaceMode from '../gl/cull_face_mode.js';
|
|
2
3
|
import DepthMode from '../gl/depth_mode.js';
|
|
3
4
|
import StencilMode from '../gl/stencil_mode.js';
|
|
@@ -12,6 +13,7 @@ function drawCircles(painter, sourceCache, layer, coords) {
|
|
|
12
13
|
const opacity = layer._paint.get('circle-opacity');
|
|
13
14
|
const strokeWidth = layer._paint.get('circle-stroke-width');
|
|
14
15
|
const strokeOpacity = layer._paint.get('circle-stroke-opacity');
|
|
16
|
+
const sortFeaturesByKey = layer._layout.get('circle-sort-key').constantOr(1) !== undefined;
|
|
15
17
|
|
|
16
18
|
if (opacity.constantOr(1) === 0 && (strokeWidth.constantOr(1) === 0 || strokeOpacity.constantOr(1) === 0)) {
|
|
17
19
|
return;
|
|
@@ -26,6 +28,8 @@ function drawCircles(painter, sourceCache, layer, coords) {
|
|
|
26
28
|
const stencilMode = StencilMode.disabled;
|
|
27
29
|
const colorMode = painter.colorModeForRenderPass();
|
|
28
30
|
|
|
31
|
+
const segmentsRenderStates = [];
|
|
32
|
+
|
|
29
33
|
for (let i = 0; i < coords.length; i++) {
|
|
30
34
|
const coord = coords[i];
|
|
31
35
|
|
|
@@ -37,6 +41,43 @@ function drawCircles(painter, sourceCache, layer, coords) {
|
|
|
37
41
|
|
|
38
42
|
const programConfiguration = bucket.programConfigurations.get(layer.id);
|
|
39
43
|
const program = painter.useProgram('circle', programConfiguration);
|
|
44
|
+
const layoutVertexBuffer = bucket.layoutVertexBuffer;
|
|
45
|
+
const indexBuffer = bucket.indexBuffer;
|
|
46
|
+
const uniformValues = circleUniformValues(painter, coord, tile, layer);
|
|
47
|
+
|
|
48
|
+
const state = {
|
|
49
|
+
programConfiguration,
|
|
50
|
+
program,
|
|
51
|
+
layoutVertexBuffer,
|
|
52
|
+
indexBuffer,
|
|
53
|
+
uniformValues
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
if (sortFeaturesByKey) {
|
|
57
|
+
const oldSegments = bucket.segments.get();
|
|
58
|
+
for (const segment of oldSegments) {
|
|
59
|
+
segmentsRenderStates.push({
|
|
60
|
+
segments: new SegmentVector([segment]),
|
|
61
|
+
sortKey: segment.sortKey,
|
|
62
|
+
state
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
segmentsRenderStates.push({
|
|
67
|
+
segments: bucket.segments,
|
|
68
|
+
sortKey: 0,
|
|
69
|
+
state
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (sortFeaturesByKey) {
|
|
75
|
+
segmentsRenderStates.sort((a, b) => a.sortKey - b.sortKey);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
for (const segmentsState of segmentsRenderStates) {
|
|
79
|
+
const { programConfiguration, program, layoutVertexBuffer, indexBuffer, uniformValues } = segmentsState.state;
|
|
80
|
+
const segments = segmentsState.segments;
|
|
40
81
|
|
|
41
82
|
program.draw(
|
|
42
83
|
context,
|
|
@@ -45,11 +86,11 @@ function drawCircles(painter, sourceCache, layer, coords) {
|
|
|
45
86
|
stencilMode,
|
|
46
87
|
colorMode,
|
|
47
88
|
CullFaceMode.disabled,
|
|
48
|
-
|
|
89
|
+
uniformValues,
|
|
49
90
|
layer.id,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
91
|
+
layoutVertexBuffer,
|
|
92
|
+
indexBuffer,
|
|
93
|
+
segments,
|
|
53
94
|
layer._paint,
|
|
54
95
|
painter.transform.zoom,
|
|
55
96
|
programConfiguration
|