Skip to content

Commit 53e622b

Browse files
Molly Lloydmollymerp
Molly Lloyd
authored andcommitted
add feature expression support for line-pattern, fill-pattern, and fill-extrusion-pattern properties
allow multiple attributes per style-spec property add CrossFadedDataDrivenProp for line-pattern DDS" convert line_pattern shaders to use pragmas create layouts for data-driven line-pattern vertex buffers add source function support for line-pattern to line bucket population and draw code use min, mid, max images for cross-fading data-driven patterns also use tile's IconAtlas for data constant line-pattern extend Binders to support line-pattern properties add initial render test nit fix ensure all possible icons for line-pattern camera funcs are added to the icon atlas make arguments needed for ddpattern required set binder type on property make pattern attributes independent of line layer implement data-driven styling for fill-pattern add dds render test for fill-pattern eliminate black flash on setPaintProperty with a pattern value extend integer-only evaluation to CrossFadedDataDrivenProps address review comments remove getPossibleOutputs and fix rendering extend feature state updating to CrossFadedCompositeBinder use getPossibleOutputs instead of iterating over all features add 1px padding wrap to sprites separate icon and pattern sprites in ImageAtlas to fix wrapping in -pattern properties rename imageAtlas --> iconAtlas now that it holds both icons and pattern images update to use new style-spec expression schema implement fill-extrusion-pattern dds address review comments simplify imageAtlas check remove redundant CrossFaded properties backport mapbox#6665 remove unpack function for pattern attrs backport mapbox#6745 and fix rebase flubs update with uniform binding state management expose possibleOutputs() at the StylePropertyExpression level add some query tests rebase fix Don't wait for pattern images to layout layers no -pattern property set bonus: remove limitation on non-deterministic expression outputs for pattern properties and reliance on `possibleOutputs()` state remove getPossibleOutputs from CrossFadedDataDrivenProperty refactor CrossFaded and CrossfadeParameters DRY bucket code with util function refactor pattern bucket functions
1 parent a02b6c0 commit 53e622b

File tree

81 files changed

+2173
-467
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+2173
-467
lines changed

build/generate-struct-arrays.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -127,14 +127,16 @@ const circleAttributes = require('../src/data/bucket/circle_attributes').default
127127
const fillAttributes = require('../src/data/bucket/fill_attributes').default;
128128
const fillExtrusionAttributes = require('../src/data/bucket/fill_extrusion_attributes').default;
129129
const lineAttributes = require('../src/data/bucket/line_attributes').default;
130+
const patternAttributes = require('../src/data/bucket/pattern_attributes').default;
130131

131132
// layout vertex arrays
132133
const layoutAttributes = {
133134
circle: circleAttributes,
134135
fill: fillAttributes,
135136
'fill-extrusion': fillExtrusionAttributes,
136137
heatmap: circleAttributes,
137-
line: lineAttributes
138+
line: lineAttributes,
139+
pattern: patternAttributes
138140
};
139141
for (const name in layoutAttributes) {
140142
createStructArrayType(`${name.replace(/-/g, '_')}_layout`, layoutAttributes[name]);

build/generate-style-code.js

+4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ global.propertyType = function (property) {
4242
return `DataDrivenProperty<${flowType(property)}>`;
4343
case 'cross-faded':
4444
return `CrossFadedProperty<${flowType(property)}>`;
45+
case 'cross-faded-data-driven':
46+
return `CrossFadedDataDrivenProperty<${flowType(property)}>`;
4547
case 'color-ramp':
4648
return `ColorRampProperty`;
4749
case 'data-constant':
@@ -100,6 +102,8 @@ global.propertyValue = function (property, type) {
100102
return `new DataDrivenProperty(styleSpec["${type}_${property.layerType}"]["${property.name}"])`;
101103
case 'cross-faded':
102104
return `new CrossFadedProperty(styleSpec["${type}_${property.layerType}"]["${property.name}"])`;
105+
case 'cross-faded-data-driven':
106+
return `new CrossFadedDataDrivenProperty(styleSpec["${type}_${property.layerType}"]["${property.name}"])`;
103107
case 'color-ramp':
104108
return `new ColorRampProperty(styleSpec["${type}_${property.layerType}"]["${property.name}"])`;
105109
case 'data-constant':

src/data/array_types.js

+50
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,54 @@ StructArrayLayout4i4ub12.prototype.bytesPerElement = 12;
179179
register('StructArrayLayout4i4ub12', StructArrayLayout4i4ub12);
180180

181181

182+
/**
183+
* Implementation of the StructArray layout:
184+
* [0]: Uint16[8]
185+
*
186+
* @private
187+
*/
188+
class StructArrayLayout8ui16 extends StructArray {
189+
uint8: Uint8Array;
190+
uint16: Uint16Array;
191+
192+
_refreshViews() {
193+
this.uint8 = new Uint8Array(this.arrayBuffer);
194+
this.uint16 = new Uint16Array(this.arrayBuffer);
195+
}
196+
197+
emplaceBack(v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number) {
198+
const i = this.length;
199+
this.resize(i + 1);
200+
const o2 = i * 8;
201+
this.uint16[o2 + 0] = v0;
202+
this.uint16[o2 + 1] = v1;
203+
this.uint16[o2 + 2] = v2;
204+
this.uint16[o2 + 3] = v3;
205+
this.uint16[o2 + 4] = v4;
206+
this.uint16[o2 + 5] = v5;
207+
this.uint16[o2 + 6] = v6;
208+
this.uint16[o2 + 7] = v7;
209+
return i;
210+
}
211+
212+
emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number) {
213+
const o2 = i * 8;
214+
this.uint16[o2 + 0] = v0;
215+
this.uint16[o2 + 1] = v1;
216+
this.uint16[o2 + 2] = v2;
217+
this.uint16[o2 + 3] = v3;
218+
this.uint16[o2 + 4] = v4;
219+
this.uint16[o2 + 5] = v5;
220+
this.uint16[o2 + 6] = v6;
221+
this.uint16[o2 + 7] = v7;
222+
return i;
223+
}
224+
}
225+
226+
StructArrayLayout8ui16.prototype.bytesPerElement = 16;
227+
register('StructArrayLayout8ui16', StructArrayLayout8ui16);
228+
229+
182230
/**
183231
* Implementation of the StructArray layout:
184232
* [0]: Int16[4]
@@ -1187,6 +1235,7 @@ export {
11871235
StructArrayLayout4i8,
11881236
StructArrayLayout2i4i12,
11891237
StructArrayLayout4i4ub12,
1238+
StructArrayLayout8ui16,
11901239
StructArrayLayout4i4ui16,
11911240
StructArrayLayout3f12,
11921241
StructArrayLayout1ul4,
@@ -1210,6 +1259,7 @@ export {
12101259
StructArrayLayout2i4i12 as FillExtrusionLayoutArray,
12111260
StructArrayLayout2i4 as HeatmapLayoutArray,
12121261
StructArrayLayout4i4ub12 as LineLayoutArray,
1262+
StructArrayLayout8ui16 as PatternLayoutArray,
12131263
StructArrayLayout4i4ui16 as SymbolLayoutArray,
12141264
StructArrayLayout3f12 as SymbolDynamicLayoutArray,
12151265
StructArrayLayout1ul4 as SymbolOpacityArray,

src/data/bucket.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {TypedStyleLayer} from '../style/style_layer/typed_style_layer';
66
import type FeatureIndex from './feature_index';
77
import type Context from '../gl/context';
88
import type {FeatureStates} from '../source/source_state';
9+
import type {ImagePosition} from '../render/image_atlas';
910

1011
export type BucketParameters<Layer: TypedStyleLayer> = {
1112
index: number,
@@ -21,6 +22,7 @@ export type BucketParameters<Layer: TypedStyleLayer> = {
2122
export type PopulateParameters = {
2223
featureIndex: FeatureIndex,
2324
iconDependencies: {},
25+
patternDependencies: {},
2426
glyphDependencies: {}
2527
}
2628

@@ -30,6 +32,16 @@ export type IndexedFeature = {
3032
sourceLayerIndex: number,
3133
}
3234

35+
export type BucketFeature = {|
36+
index: number,
37+
sourceLayerIndex: number,
38+
geometry: Array<Array<Point>>,
39+
properties: Object,
40+
type: 1 | 2 | 3,
41+
id?: any,
42+
+patterns: {[string]: {"min": string, "mid": string, "max": string}}
43+
|};
44+
3345
/**
3446
* The `Bucket` interface is the single point of knowledge about turning vector
3547
* tiles into WebGL buffers.
@@ -55,11 +67,12 @@ export type IndexedFeature = {
5567
*/
5668
export interface Bucket {
5769
layerIds: Array<string>;
70+
hasPattern: boolean;
5871
+layers: Array<any>;
5972
+stateDependentLayers: Array<any>;
6073

6174
populate(features: Array<IndexedFeature>, options: PopulateParameters): void;
62-
update(states: FeatureStates, vtLayer: VectorTileLayer): void;
75+
update(states: FeatureStates, vtLayer: VectorTileLayer, imagePositions: {[string]: ImagePosition}): void;
6376
isEmpty(): boolean;
6477

6578
upload(context: Context): void;

src/data/bucket/circle_bucket.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import type IndexBuffer from '../../gl/index_buffer';
2424
import type VertexBuffer from '../../gl/vertex_buffer';
2525
import type Point from '@mapbox/point-geometry';
2626
import type {FeatureStates} from '../../source/source_state';
27+
import type {ImagePosition} from '../../render/image_atlas';
28+
2729

2830
function addCircleVertex(layoutVertexArray, x, y, extrudeX, extrudeY) {
2931
layoutVertexArray.emplaceBack(
@@ -53,6 +55,7 @@ class CircleBucket<Layer: CircleStyleLayer | HeatmapStyleLayer> implements Bucke
5355
indexArray: TriangleIndexArray;
5456
indexBuffer: IndexBuffer;
5557

58+
hasPattern: boolean;
5659
programConfigurations: ProgramConfigurationSet<Layer>;
5760
segments: SegmentVector;
5861
uploaded: boolean;
@@ -63,6 +66,7 @@ class CircleBucket<Layer: CircleStyleLayer | HeatmapStyleLayer> implements Bucke
6366
this.layers = options.layers;
6467
this.layerIds = this.layers.map(layer => layer.id);
6568
this.index = options.index;
69+
this.hasPattern = false;
6670

6771
this.layoutVertexArray = new CircleLayoutArray();
6872
this.indexArray = new TriangleIndexArray();
@@ -80,9 +84,9 @@ class CircleBucket<Layer: CircleStyleLayer | HeatmapStyleLayer> implements Bucke
8084
}
8185
}
8286

83-
update(states: FeatureStates, vtLayer: VectorTileLayer) {
87+
update(states: FeatureStates, vtLayer: VectorTileLayer, imagePositions: {[string]: ImagePosition}) {
8488
if (!this.stateDependentLayers.length) return;
85-
this.programConfigurations.updatePaintArrays(states, vtLayer, this.stateDependentLayers);
89+
this.programConfigurations.updatePaintArrays(states, vtLayer, this.stateDependentLayers, imagePositions);
8690
}
8791

8892
isEmpty() {
@@ -144,7 +148,7 @@ class CircleBucket<Layer: CircleStyleLayer | HeatmapStyleLayer> implements Bucke
144148
}
145149
}
146150

147-
this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index);
151+
this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index, {});
148152
}
149153
}
150154

src/data/bucket/fill_bucket.js

+45-11
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@ import { members as layoutAttributes } from './fill_attributes';
66
import SegmentVector from '../segment';
77
import { ProgramConfigurationSet } from '../program_configuration';
88
import { LineIndexArray, TriangleIndexArray } from '../index_array_type';
9-
import loadGeometry from '../load_geometry';
109
import earcut from 'earcut';
1110
import classifyRings from '../../util/classify_rings';
1211
import assert from 'assert';
1312
const EARCUT_MAX_RINGS = 500;
1413
import { register } from '../../util/web_worker_transfer';
14+
import {hasPattern, addPatternDependencies} from './pattern_bucket_features';
15+
import loadGeometry from '../load_geometry';
1516
import EvaluationParameters from '../../style/evaluation_parameters';
1617

1718
import type {
1819
Bucket,
1920
BucketParameters,
21+
BucketFeature,
2022
IndexedFeature,
2123
PopulateParameters
2224
} from '../bucket';
@@ -26,6 +28,7 @@ import type IndexBuffer from '../../gl/index_buffer';
2628
import type VertexBuffer from '../../gl/vertex_buffer';
2729
import type Point from '@mapbox/point-geometry';
2830
import type {FeatureStates} from '../../source/source_state';
31+
import type {ImagePosition} from '../../render/image_atlas';
2932

3033
class FillBucket implements Bucket {
3134
index: number;
@@ -44,17 +47,20 @@ class FillBucket implements Bucket {
4447
indexArray2: LineIndexArray;
4548
indexBuffer2: IndexBuffer;
4649

50+
hasPattern: boolean;
4751
programConfigurations: ProgramConfigurationSet<FillStyleLayer>;
4852
segments: SegmentVector;
4953
segments2: SegmentVector;
5054
uploaded: boolean;
55+
features: Array<BucketFeature>;
5156

5257
constructor(options: BucketParameters<FillStyleLayer>) {
5358
this.zoom = options.zoom;
5459
this.overscaling = options.overscaling;
5560
this.layers = options.layers;
5661
this.layerIds = this.layers.map(layer => layer.id);
5762
this.index = options.index;
63+
this.hasPattern = false;
5864

5965
this.layoutVertexArray = new FillLayoutArray();
6066
this.indexArray = new TriangleIndexArray();
@@ -65,18 +71,47 @@ class FillBucket implements Bucket {
6571
}
6672

6773
populate(features: Array<IndexedFeature>, options: PopulateParameters) {
74+
this.features = [];
75+
this.hasPattern = hasPattern('fill', this.layers, options);
76+
6877
for (const {feature, index, sourceLayerIndex} of features) {
69-
if (this.layers[0]._featureFilter(new EvaluationParameters(this.zoom), feature)) {
70-
const geometry = loadGeometry(feature);
71-
this.addFeature(feature, geometry, index);
72-
options.featureIndex.insert(feature, geometry, index, sourceLayerIndex, this.index);
78+
if (!this.layers[0]._featureFilter(new EvaluationParameters(this.zoom), feature)) continue;
79+
80+
const geometry = loadGeometry(feature);
81+
82+
const patternFeature: BucketFeature = {
83+
sourceLayerIndex: sourceLayerIndex,
84+
index: index,
85+
geometry: geometry,
86+
properties: feature.properties,
87+
type: feature.type,
88+
patterns: {}
89+
};
90+
91+
if (typeof feature.id !== 'undefined') {
92+
patternFeature.id = feature.id;
7393
}
94+
95+
if (this.hasPattern) {
96+
this.features.push(addPatternDependencies('fill', this.layers, patternFeature, this.zoom, options));
97+
} else {
98+
this.addFeature(patternFeature, geometry, index, {});
99+
}
100+
101+
options.featureIndex.insert(feature, geometry, index, sourceLayerIndex, this.index);
74102
}
75103
}
76104

77-
update(states: FeatureStates, vtLayer: VectorTileLayer) {
105+
update(states: FeatureStates, vtLayer: VectorTileLayer, imagePositions: {[string]: ImagePosition}) {
78106
if (!this.stateDependentLayers.length) return;
79-
this.programConfigurations.updatePaintArrays(states, vtLayer, this.stateDependentLayers);
107+
this.programConfigurations.updatePaintArrays(states, vtLayer, this.stateDependentLayers, imagePositions);
108+
}
109+
110+
addFeatures(options: PopulateParameters, imagePositions: {[string]: ImagePosition}) {
111+
for (const feature of this.features) {
112+
const {geometry} = feature;
113+
this.addFeature(feature, geometry, feature.index, imagePositions);
114+
}
80115
}
81116

82117
isEmpty() {
@@ -106,7 +141,7 @@ class FillBucket implements Bucket {
106141
this.segments2.destroy();
107142
}
108143

109-
addFeature(feature: VectorTileFeature, geometry: Array<Array<Point>>, index: number) {
144+
addFeature(feature: BucketFeature, geometry: Array<Array<Point>>, index: number, imagePositions: {[string]: ImagePosition}) {
110145
for (const polygon of classifyRings(geometry, EARCUT_MAX_RINGS)) {
111146
let numVertices = 0;
112147
for (const ring of polygon) {
@@ -160,11 +195,10 @@ class FillBucket implements Bucket {
160195
triangleSegment.vertexLength += numVertices;
161196
triangleSegment.primitiveLength += indices.length / 3;
162197
}
163-
164-
this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index);
198+
this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index, imagePositions);
165199
}
166200
}
167201

168-
register('FillBucket', FillBucket, {omit: ['layers']});
202+
register('FillBucket', FillBucket, {omit: ['layers', 'features']});
169203

170204
export default FillBucket;

0 commit comments

Comments
 (0)