Skip to content

Commit

Permalink
add feature expression support for line-pattern, fill-pattern, and fi…
Browse files Browse the repository at this point in the history
…ll-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 #6665

remove unpack function for pattern attrs

backport #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
  • Loading branch information
Molly Lloyd authored and mollymerp committed Aug 27, 2018
1 parent a02b6c0 commit 53e622b
Show file tree
Hide file tree
Showing 81 changed files with 2,173 additions and 467 deletions.
4 changes: 3 additions & 1 deletion build/generate-struct-arrays.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,16 @@ const circleAttributes = require('../src/data/bucket/circle_attributes').default
const fillAttributes = require('../src/data/bucket/fill_attributes').default;
const fillExtrusionAttributes = require('../src/data/bucket/fill_extrusion_attributes').default;
const lineAttributes = require('../src/data/bucket/line_attributes').default;
const patternAttributes = require('../src/data/bucket/pattern_attributes').default;

// layout vertex arrays
const layoutAttributes = {
circle: circleAttributes,
fill: fillAttributes,
'fill-extrusion': fillExtrusionAttributes,
heatmap: circleAttributes,
line: lineAttributes
line: lineAttributes,
pattern: patternAttributes
};
for (const name in layoutAttributes) {
createStructArrayType(`${name.replace(/-/g, '_')}_layout`, layoutAttributes[name]);
Expand Down
4 changes: 4 additions & 0 deletions build/generate-style-code.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ global.propertyType = function (property) {
return `DataDrivenProperty<${flowType(property)}>`;
case 'cross-faded':
return `CrossFadedProperty<${flowType(property)}>`;
case 'cross-faded-data-driven':
return `CrossFadedDataDrivenProperty<${flowType(property)}>`;
case 'color-ramp':
return `ColorRampProperty`;
case 'data-constant':
Expand Down Expand Up @@ -100,6 +102,8 @@ global.propertyValue = function (property, type) {
return `new DataDrivenProperty(styleSpec["${type}_${property.layerType}"]["${property.name}"])`;
case 'cross-faded':
return `new CrossFadedProperty(styleSpec["${type}_${property.layerType}"]["${property.name}"])`;
case 'cross-faded-data-driven':
return `new CrossFadedDataDrivenProperty(styleSpec["${type}_${property.layerType}"]["${property.name}"])`;
case 'color-ramp':
return `new ColorRampProperty(styleSpec["${type}_${property.layerType}"]["${property.name}"])`;
case 'data-constant':
Expand Down
50 changes: 50 additions & 0 deletions src/data/array_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,54 @@ StructArrayLayout4i4ub12.prototype.bytesPerElement = 12;
register('StructArrayLayout4i4ub12', StructArrayLayout4i4ub12);


/**
* Implementation of the StructArray layout:
* [0]: Uint16[8]
*
* @private
*/
class StructArrayLayout8ui16 extends StructArray {
uint8: Uint8Array;
uint16: Uint16Array;

_refreshViews() {
this.uint8 = new Uint8Array(this.arrayBuffer);
this.uint16 = new Uint16Array(this.arrayBuffer);
}

emplaceBack(v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number) {
const i = this.length;
this.resize(i + 1);
const o2 = i * 8;
this.uint16[o2 + 0] = v0;
this.uint16[o2 + 1] = v1;
this.uint16[o2 + 2] = v2;
this.uint16[o2 + 3] = v3;
this.uint16[o2 + 4] = v4;
this.uint16[o2 + 5] = v5;
this.uint16[o2 + 6] = v6;
this.uint16[o2 + 7] = v7;
return i;
}

emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number) {
const o2 = i * 8;
this.uint16[o2 + 0] = v0;
this.uint16[o2 + 1] = v1;
this.uint16[o2 + 2] = v2;
this.uint16[o2 + 3] = v3;
this.uint16[o2 + 4] = v4;
this.uint16[o2 + 5] = v5;
this.uint16[o2 + 6] = v6;
this.uint16[o2 + 7] = v7;
return i;
}
}

StructArrayLayout8ui16.prototype.bytesPerElement = 16;
register('StructArrayLayout8ui16', StructArrayLayout8ui16);


/**
* Implementation of the StructArray layout:
* [0]: Int16[4]
Expand Down Expand Up @@ -1187,6 +1235,7 @@ export {
StructArrayLayout4i8,
StructArrayLayout2i4i12,
StructArrayLayout4i4ub12,
StructArrayLayout8ui16,
StructArrayLayout4i4ui16,
StructArrayLayout3f12,
StructArrayLayout1ul4,
Expand All @@ -1210,6 +1259,7 @@ export {
StructArrayLayout2i4i12 as FillExtrusionLayoutArray,
StructArrayLayout2i4 as HeatmapLayoutArray,
StructArrayLayout4i4ub12 as LineLayoutArray,
StructArrayLayout8ui16 as PatternLayoutArray,
StructArrayLayout4i4ui16 as SymbolLayoutArray,
StructArrayLayout3f12 as SymbolDynamicLayoutArray,
StructArrayLayout1ul4 as SymbolOpacityArray,
Expand Down
15 changes: 14 additions & 1 deletion src/data/bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {TypedStyleLayer} from '../style/style_layer/typed_style_layer';
import type FeatureIndex from './feature_index';
import type Context from '../gl/context';
import type {FeatureStates} from '../source/source_state';
import type {ImagePosition} from '../render/image_atlas';

export type BucketParameters<Layer: TypedStyleLayer> = {
index: number,
Expand All @@ -21,6 +22,7 @@ export type BucketParameters<Layer: TypedStyleLayer> = {
export type PopulateParameters = {
featureIndex: FeatureIndex,
iconDependencies: {},
patternDependencies: {},
glyphDependencies: {}
}

Expand All @@ -30,6 +32,16 @@ export type IndexedFeature = {
sourceLayerIndex: number,
}

export type BucketFeature = {|
index: number,
sourceLayerIndex: number,
geometry: Array<Array<Point>>,
properties: Object,
type: 1 | 2 | 3,
id?: any,
+patterns: {[string]: {"min": string, "mid": string, "max": string}}
|};

/**
* The `Bucket` interface is the single point of knowledge about turning vector
* tiles into WebGL buffers.
Expand All @@ -55,11 +67,12 @@ export type IndexedFeature = {
*/
export interface Bucket {
layerIds: Array<string>;
hasPattern: boolean;
+layers: Array<any>;
+stateDependentLayers: Array<any>;

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

upload(context: Context): void;
Expand Down
10 changes: 7 additions & 3 deletions src/data/bucket/circle_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import type IndexBuffer from '../../gl/index_buffer';
import type VertexBuffer from '../../gl/vertex_buffer';
import type Point from '@mapbox/point-geometry';
import type {FeatureStates} from '../../source/source_state';
import type {ImagePosition} from '../../render/image_atlas';


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

hasPattern: boolean;
programConfigurations: ProgramConfigurationSet<Layer>;
segments: SegmentVector;
uploaded: boolean;
Expand All @@ -63,6 +66,7 @@ class CircleBucket<Layer: CircleStyleLayer | HeatmapStyleLayer> implements Bucke
this.layers = options.layers;
this.layerIds = this.layers.map(layer => layer.id);
this.index = options.index;
this.hasPattern = false;

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

update(states: FeatureStates, vtLayer: VectorTileLayer) {
update(states: FeatureStates, vtLayer: VectorTileLayer, imagePositions: {[string]: ImagePosition}) {
if (!this.stateDependentLayers.length) return;
this.programConfigurations.updatePaintArrays(states, vtLayer, this.stateDependentLayers);
this.programConfigurations.updatePaintArrays(states, vtLayer, this.stateDependentLayers, imagePositions);
}

isEmpty() {
Expand Down Expand Up @@ -144,7 +148,7 @@ class CircleBucket<Layer: CircleStyleLayer | HeatmapStyleLayer> implements Bucke
}
}

this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index);
this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index, {});
}
}

Expand Down
56 changes: 45 additions & 11 deletions src/data/bucket/fill_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ import { members as layoutAttributes } from './fill_attributes';
import SegmentVector from '../segment';
import { ProgramConfigurationSet } from '../program_configuration';
import { LineIndexArray, TriangleIndexArray } from '../index_array_type';
import loadGeometry from '../load_geometry';
import earcut from 'earcut';
import classifyRings from '../../util/classify_rings';
import assert from 'assert';
const EARCUT_MAX_RINGS = 500;
import { register } from '../../util/web_worker_transfer';
import {hasPattern, addPatternDependencies} from './pattern_bucket_features';
import loadGeometry from '../load_geometry';
import EvaluationParameters from '../../style/evaluation_parameters';

import type {
Bucket,
BucketParameters,
BucketFeature,
IndexedFeature,
PopulateParameters
} from '../bucket';
Expand All @@ -26,6 +28,7 @@ import type IndexBuffer from '../../gl/index_buffer';
import type VertexBuffer from '../../gl/vertex_buffer';
import type Point from '@mapbox/point-geometry';
import type {FeatureStates} from '../../source/source_state';
import type {ImagePosition} from '../../render/image_atlas';

class FillBucket implements Bucket {
index: number;
Expand All @@ -44,17 +47,20 @@ class FillBucket implements Bucket {
indexArray2: LineIndexArray;
indexBuffer2: IndexBuffer;

hasPattern: boolean;
programConfigurations: ProgramConfigurationSet<FillStyleLayer>;
segments: SegmentVector;
segments2: SegmentVector;
uploaded: boolean;
features: Array<BucketFeature>;

constructor(options: BucketParameters<FillStyleLayer>) {
this.zoom = options.zoom;
this.overscaling = options.overscaling;
this.layers = options.layers;
this.layerIds = this.layers.map(layer => layer.id);
this.index = options.index;
this.hasPattern = false;

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

populate(features: Array<IndexedFeature>, options: PopulateParameters) {
this.features = [];
this.hasPattern = hasPattern('fill', this.layers, options);

for (const {feature, index, sourceLayerIndex} of features) {
if (this.layers[0]._featureFilter(new EvaluationParameters(this.zoom), feature)) {
const geometry = loadGeometry(feature);
this.addFeature(feature, geometry, index);
options.featureIndex.insert(feature, geometry, index, sourceLayerIndex, this.index);
if (!this.layers[0]._featureFilter(new EvaluationParameters(this.zoom), feature)) continue;

const geometry = loadGeometry(feature);

const patternFeature: BucketFeature = {
sourceLayerIndex: sourceLayerIndex,
index: index,
geometry: geometry,
properties: feature.properties,
type: feature.type,
patterns: {}
};

if (typeof feature.id !== 'undefined') {
patternFeature.id = feature.id;
}

if (this.hasPattern) {
this.features.push(addPatternDependencies('fill', this.layers, patternFeature, this.zoom, options));
} else {
this.addFeature(patternFeature, geometry, index, {});
}

options.featureIndex.insert(feature, geometry, index, sourceLayerIndex, this.index);
}
}

update(states: FeatureStates, vtLayer: VectorTileLayer) {
update(states: FeatureStates, vtLayer: VectorTileLayer, imagePositions: {[string]: ImagePosition}) {
if (!this.stateDependentLayers.length) return;
this.programConfigurations.updatePaintArrays(states, vtLayer, this.stateDependentLayers);
this.programConfigurations.updatePaintArrays(states, vtLayer, this.stateDependentLayers, imagePositions);
}

addFeatures(options: PopulateParameters, imagePositions: {[string]: ImagePosition}) {
for (const feature of this.features) {
const {geometry} = feature;
this.addFeature(feature, geometry, feature.index, imagePositions);
}
}

isEmpty() {
Expand Down Expand Up @@ -106,7 +141,7 @@ class FillBucket implements Bucket {
this.segments2.destroy();
}

addFeature(feature: VectorTileFeature, geometry: Array<Array<Point>>, index: number) {
addFeature(feature: BucketFeature, geometry: Array<Array<Point>>, index: number, imagePositions: {[string]: ImagePosition}) {
for (const polygon of classifyRings(geometry, EARCUT_MAX_RINGS)) {
let numVertices = 0;
for (const ring of polygon) {
Expand Down Expand Up @@ -160,11 +195,10 @@ class FillBucket implements Bucket {
triangleSegment.vertexLength += numVertices;
triangleSegment.primitiveLength += indices.length / 3;
}

this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index);
this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index, imagePositions);
}
}

register('FillBucket', FillBucket, {omit: ['layers']});
register('FillBucket', FillBucket, {omit: ['layers', 'features']});

export default FillBucket;
Loading

0 comments on commit 53e622b

Please sign in to comment.