Skip to content

Commit

Permalink
add source function support for line-pattern to line bucket populatio…
Browse files Browse the repository at this point in the history
…n and draw code
  • Loading branch information
Molly Lloyd committed Mar 7, 2018
1 parent 8c51cfb commit 2cfe261
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 31 deletions.
107 changes: 104 additions & 3 deletions src/data/bucket/line_bucket.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow

const {LineLayoutArray} = require('../array_types');
const {lineLayoutAttributes, dynamicLineAttributes} = require('./line_attributes');
const {lineLayoutAttributes} = require('./line_attributes');
const layoutAttributes = lineLayoutAttributes.members;
const {SegmentVector} = require('../segment');
const {ProgramConfigurationSet} = require('../program_configuration');
Expand All @@ -10,6 +10,8 @@ const loadGeometry = require('../load_geometry');
const EXTENT = require('../extent');
const vectorTileFeatureTypes = require('@mapbox/vector-tile').VectorTileFeature.types;
const {register} = require('../../util/web_worker_transfer');
const packUint8ToFloat = require('../../shaders/encode_attribute').packUint8ToFloat;


import type {
Bucket,
Expand All @@ -23,6 +25,18 @@ import type {Segment} from '../segment';
import type Context from '../../gl/context';
import type IndexBuffer from '../../gl/index_buffer';
import type VertexBuffer from '../../gl/vertex_buffer';
import type {CrossFaded} from '../../style/cross_faded';


export type LineFeature = {|
image: ?CrossFaded<string>,
index: number,
sourceLayerIndex: number,
geometry: Array<Array<Point>>,
properties: Object,
type: number,
id?: any
|};

// NOTE ON EXTRUDE SCALE:
// scale the extrusion vector so that the normal length is this value.
Expand Down Expand Up @@ -92,6 +106,8 @@ class LineBucket implements Bucket {
overscaling: number;
layers: Array<LineStyleLayer>;
layerIds: Array<string>;
features: Array<LineFeature>;
dataDrivenPattern: boolean;

layoutVertexArray: LineLayoutArray;
layoutVertexBuffer: VertexBuffer;
Expand All @@ -109,23 +125,75 @@ class LineBucket implements Bucket {
this.layers = options.layers;
this.layerIds = this.layers.map(layer => layer.id);
this.index = options.index;
this.features = [];

this.layoutVertexArray = new LineLayoutArray();
this.indexArray = new TriangleIndexArray();
this.programConfigurations = new ProgramConfigurationSet(layoutAttributes, options.layers, options.zoom);
this.segments = new SegmentVector();

this.dataDrivenPattern = false;

for (const key in this.programConfigurations) {
const programConfiguration = this.programConfigurations[key];
for (const layer in programConfiguration) {
const binders = programConfiguration[layer].binders;
if (binders && binders['line-pattern'] && binders['line-pattern'].paintVertexArray) {
this.dataDrivenPattern = true;
break;
}
}
}
}

populate(features: Array<IndexedFeature>, options: PopulateParameters) {
const icons = options.iconDependencies;
this.features = [];
for (const {feature, index, sourceLayerIndex} of features) {
if (this.layers[0]._featureFilter({zoom: this.zoom}, feature)) {
if (!this.layers[0]._featureFilter({zoom: this.zoom}, feature)) continue;
if (this.dataDrivenPattern) {
const layer = this.layers[0];
const linePattern = layer.paint.get('line-pattern');
const image = linePattern.evaluate(feature);
if (image) {
icons[image.from] = true;
icons[image.to] = true;
}
const geometry = loadGeometry(feature)
const lineFeature: LineFeature = {
image: image,
sourceLayerIndex: sourceLayerIndex,
index: index,
geometry: geometry,
properties: feature.properties,
type: feature.type
};

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

this.features.push(lineFeature);
options.featureIndex.insert(feature, geometry, index, sourceLayerIndex, this.index);
} else {
const geometry = loadGeometry(feature);
this.addFeature(feature, geometry);
options.featureIndex.insert(feature, geometry, index, sourceLayerIndex, this.index);
}
}
}

// used if line-pattern is data-driven
addPatternFeatures(options, imageMap, imagePositions) {
this.imageMap = imageMap;
this.imagePositions = imagePositions;
for (const feature of this.features) {
const {geometry, index, sourceLayerIndex} = feature;
this.addFeature(feature, geometry);
// options.featureIndex.insert(feature, geometry, index, sourceLayerIndex, this.index);
}
}

isEmpty() {
return this.layoutVertexArray.length === 0;
}
Expand Down Expand Up @@ -420,8 +488,41 @@ class LineBucket implements Bucket {
}

this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature);
if (this.dataDrivenPattern) this.populatePatternPaintArray(this.layoutVertexArray.length, feature);
}

populatePatternPaintArray(length, feature) {
for (const key in this.programConfigurations) {
const programConfig = this.programConfigurations[key];
for (const layer in programConfig) {
if (programConfig[layer].binders && programConfig[layer].binders['line-pattern'] && programConfig[layer].binders['line-pattern'].paintVertexArray) {
const paintArray = programConfig[layer].binders['line-pattern'].paintVertexArray;
const start = paintArray.length;

paintArray.reserve(length);
const image = feature.image;
const imagePosA = this.imagePositions[image.from];
const imagePosB = this.imagePositions[image.to];

if (!imagePosA || !imagePosB) return;

const aTL = packUint8ToFloat(imagePosA.tl[0], imagePosA.tl[1]);
const aBR = packUint8ToFloat(imagePosA.br[0], imagePosA.br[1]);
const bTL = packUint8ToFloat(imagePosB.tl[0], imagePosB.tl[1]);
const bBR = packUint8ToFloat(imagePosB.br[0], imagePosB.br[1]);

for (let i = start; i < length; i++) {
paintArray.emplaceBack(
// u_pattern_tl_a, u_pattern_br_a
aTL, aBR,
// u_pattern_tl_b, u_pattern_br_b
bTL, bBR
);
}
}
}
}
}
/**
* Add two vertices to the buffers.
*
Expand Down Expand Up @@ -509,6 +610,6 @@ class LineBucket implements Bucket {
}
}

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

module.exports = LineBucket;
61 changes: 39 additions & 22 deletions src/data/program_configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ const {PossiblyEvaluatedPropertyValue} = require('../style/properties');
const {
StructArrayLayout1f4,
StructArrayLayout2f8,
StructArrayLayout4f16
StructArrayLayout4f16,
LinePatternSourceExpressionLayoutArray,
LinePatternCompositeExpressionLayoutArray
} = require('./array_types');

import type Context from '../gl/context';
Expand Down Expand Up @@ -97,7 +99,7 @@ class ConstantBinder<T> implements Binder<T> {
const value: any = currentValue.constantOr(this.value);
const gl = context.gl;
for (let i = 0; i < this.names.length; i++) {
const name = this.names[i]
const name = this.names[i];
if (this.type === 'color') {
gl.uniform4f(program.uniforms[`u_${name}`], value.r, value.g, value.b, value.a);
} else {
Expand All @@ -117,18 +119,18 @@ class SourceExpressionBinder<T> implements Binder<T> {
paintVertexAttributes: Array<StructArrayMember>;
paintVertexBuffer: ?VertexBuffer;

constructor(expression: SourceExpression, names: Array<string>, type: string, layout: () => StructArray) {
constructor(expression: SourceExpression, names: Array<string>, type: string, layout: Class<StructArray>) {
this.expression = expression;
this.names = names;
this.type = type;
this.statistics = { max: -Infinity };
const PaintVertexArray = layout;
this.paintVertexAttributes = names.map( name =>
this.paintVertexAttributes = names.map((name, i) =>
({
name: `a_${name}`,
type: 'Float32',
components: type === 'color' ? 2 : 1,
offset: 0
components: type === 'color' || name.match(/pattern/) ? 2 : 1,
offset: name.match(/pattern/) ? i * 8 : 0
})
);
this.paintVertexArray = new PaintVertexArray();
Expand All @@ -145,6 +147,7 @@ class SourceExpressionBinder<T> implements Binder<T> {
paintArray.reserve(length);

const value = this.expression.evaluate({zoom: 0}, feature);

// figure out how to design this for atypical paint properties with multiple attributes
if (this.type === 'color') {
const color = packColor(value);
Expand Down Expand Up @@ -189,7 +192,7 @@ class CompositeExpressionBinder<T> implements Binder<T> {
paintVertexAttributes: Array<StructArrayMember>;
paintVertexBuffer: ?VertexBuffer;

constructor(expression: CompositeExpression, names: Array<string>, type: string, useIntegerZoom: boolean, zoom: number, layout: () => StructArray) {
constructor(expression: CompositeExpression, names: Array<string>, type: string, useIntegerZoom: boolean, zoom: number, layout: Class<StructArray>) {
this.expression = expression;
this.names = names;
this.type = type;
Expand Down Expand Up @@ -330,6 +333,9 @@ class ProgramConfiguration {

populatePaintArrays(length: number, feature: Feature) {
for (const property in this.binders) {
// binders for properties with layout exceptions populate their paint arrays in the
// bucket because they have multiple attributes and property-specific population code
if (getLayoutException(property)) continue;
this.binders[property].populatePaintArray(length, feature);
}
}
Expand Down Expand Up @@ -420,25 +426,35 @@ class ProgramConfigurationSet<Layer: TypedStyleLayer> {
// paint property arrays
function paintAttributeName(property, type) {
const attributeNameExceptions = {
'text-opacity': 'opacity',
'icon-opacity': 'opacity',
'text-color': 'fill_color',
'icon-color': 'fill_color',
'text-halo-color': 'halo_color',
'icon-halo-color': 'halo_color',
'text-halo-blur': 'halo_blur',
'icon-halo-blur': 'halo_blur',
'text-halo-width': 'halo_width',
'icon-halo-width': 'halo_width',
'line-gap-width': 'gapwidth',
'text-opacity': ['opacity'],
'icon-opacity': ['opacity'],
'text-color': ['fill_color'],
'icon-color': ['fill_color'],
'text-halo-color': ['halo_color'],
'icon-halo-color': ['halo_color'],
'text-halo-blur': ['halo_blur'],
'icon-halo-blur': ['halo_blur'],
'text-halo-width': ['halo_width'],
'icon-halo-width': ['halo_width'],
'line-gap-width': ['gapwidth'],
'line-pattern': ['pattern_a', 'pattern_b', 'pattern_size']
};
return [attributeNameExceptions[property] ||
property.replace(`${type}-`, '').replace(/-/g, '_')];
return attributeNameExceptions[property] ||
[property.replace(`${type}-`, '').replace(/-/g, '_')];
}

function getLayoutException(property) {
const propertyExceptions = {
'line-pattern':{
'source': LinePatternSourceExpressionLayoutArray,
'composite': LinePatternCompositeExpressionLayoutArray
}
};

return propertyExceptions[property];
}

function layoutType(property, type, binderType) {
const propertyExceptions = {};
const defaultLayouts = {
'color': {
'source': StructArrayLayout2f8,
Expand All @@ -450,7 +466,8 @@ function layoutType(property, type, binderType) {
}
};

return propertyExceptions[property] && propertyExceptions[property][binderType] ||
const layoutException = getLayoutException(property, binderType);
return layoutException && layoutException[binderType] ||
defaultLayouts[type][binderType];
}

Expand Down
30 changes: 24 additions & 6 deletions src/render/draw_line.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ function drawLineTile(program, painter, tile, bucket, layer, coord, programConfi
const gl = context.gl;
const dasharray = layer.paint.get('line-dasharray');
const linePattern = layer.paint.get('line-pattern');
const image = linePattern && linePattern.evaluate();
const image = linePattern && (linePattern.value.kind === "constant" || linePattern.value.kind === "camera" ) ? linePattern.value.value : undefined;

let posA, posB, imagePosA, imagePosB;

const tileRatio = 1 / pixelsToTileUnits(tile, 1, painter.transform.tileZoom);
if (programChanged || tileRatioChanged) {
const tileRatio = 1 / pixelsToTileUnits(tile, 1, painter.transform.tileZoom);

if (dasharray) {
posA = painter.lineAtlas.getDash(dasharray.from, layer.layout.get('line-cap') === 'round');
Expand All @@ -71,22 +71,27 @@ function drawLineTile(program, painter, tile, bucket, layer, coord, programConfi
gl.uniform2f(program.uniforms.u_patternscale_b, tileRatio / widthB, -posB.height / 2);
gl.uniform1f(program.uniforms.u_sdfgamma, painter.lineAtlas.width / (Math.min(widthA, widthB) * 256 * browser.devicePixelRatio) / 2);

} else if (bucket.dataDrivenPattern) {
const size = tile.iconAtlasTexture.size;
gl.uniform2fv(program.uniforms.u_texsize, size);
} else if (image) {

imagePosA = painter.imageManager.getPattern(image.from);
imagePosB = painter.imageManager.getPattern(image.to);
if (!imagePosA || !imagePosB) return;
gl.uniform4f(program.uniforms.u_pattern_size, imagePosA.displaySize[0] * image.fromScale / tileRatio, imagePosA.displaySize[1], imagePosB.displaySize[0] * image.toScale / tileRatio, imagePosB.displaySize[1]);
gl.uniform4f(program.uniforms.u_scale, imagePosA.pixelRatio, tileRatio, image.fromScale, image.toScale);

const {width, height} = painter.imageManager.getPixelSize();
gl.uniform2fv(program.uniforms.u_texsize, [width, height]);
} else if (bucket.dataDrivenPattern) {
const size = tile.iconAtlasTexture.size;
gl.uniform2fv(program.uniforms.u_texsize, size);
}

gl.uniform2f(program.uniforms.u_gl_units_to_pixels, 1 / painter.transform.pixelsToGLUnits[0], 1 / painter.transform.pixelsToGLUnits[1]);
}

if (programChanged) {

if (dasharray) {
gl.uniform1i(program.uniforms.u_image, 0);
context.activeTexture.set(gl.TEXTURE0);
Expand All @@ -95,14 +100,27 @@ function drawLineTile(program, painter, tile, bucket, layer, coord, programConfi
gl.uniform1f(program.uniforms.u_tex_y_a, (posA: any).y);
gl.uniform1f(program.uniforms.u_tex_y_b, (posB: any).y);
gl.uniform1f(program.uniforms.u_mix, dasharray.t);
} else if (bucket.dataDrivenPattern) {
gl.uniform1i(program.uniforms.u_image, 0);
context.activeTexture.set(gl.TEXTURE0);
tile.iconAtlasTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE);

const evaluated = linePattern.evaluate();
gl.uniform1f(program.uniforms.u_fade, evaluated.t);
gl.uniform4f(program.uniforms.u_scale, 1, tileRatio, evaluated.fromScale, evaluated.toScale);
} else if (image) {
gl.uniform1i(program.uniforms.u_image, 0);
context.activeTexture.set(gl.TEXTURE0);
painter.imageManager.bind(context);
gl.uniform4fv(program.uniforms.u_pattern_a, (imagePosA: any).tl.concat((imagePosA: any).br));
gl.uniform4fv(program.uniforms.u_pattern_b, (imagePosB: any).tl.concat((imagePosB: any).br));
gl.uniform1f(program.uniforms.u_fade, image.t);
} else if (bucket.dataDrivenPattern) {
gl.uniform1i(program.uniforms.u_image, 0);
context.activeTexture.set(gl.TEXTURE0);
tile.iconAtlasTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE);
gl.uniform1f(program.uniforms.u_fade, 1);

}
}

Expand All @@ -112,13 +130,13 @@ function drawLineTile(program, painter, tile, bucket, layer, coord, programConfi
gl.uniformMatrix4fv(program.uniforms.u_matrix, false, posMatrix);

gl.uniform1f(program.uniforms.u_ratio, 1 / pixelsToTileUnits(tile, 1, painter.transform.zoom));

program.draw(
context,
gl.TRIANGLES,
layer.id,
bucket.layoutVertexBuffer,
bucket.indexBuffer,
bucket.segments,
programConfiguration);
programConfiguration,
bucket.dynamicLineAttributeBuffer);
}
Loading

0 comments on commit 2cfe261

Please sign in to comment.