package.src.data.program_configuration.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mapbox-gl Show documentation
Show all versions of mapbox-gl Show documentation
A WebGL interactive maps library
The newest version!
// @flow
import {packUint8ToFloat} from '../shaders/encode_attribute';
import Color from '../style-spec/util/color';
import {supportsPropertyExpression} from '../style-spec/util/properties';
import {register} from '../util/web_worker_transfer';
import {PossiblyEvaluatedPropertyValue} from '../style/properties';
import {StructArrayLayout1f4, StructArrayLayout2f8, StructArrayLayout4f16, PatternLayoutArray} from './array_types';
import {clamp} from '../util/util';
import patternAttributes from './bucket/pattern_attributes';
import EvaluationParameters from '../style/evaluation_parameters';
import FeaturePositionMap from './feature_position_map';
import {
Uniform,
Uniform1f,
UniformColor,
Uniform4f,
type UniformLocations
} from '../render/uniform_binding';
import type {CanonicalTileID} from '../source/tile_id';
import type Context from '../gl/context';
import type {TypedStyleLayer} from '../style/style_layer/typed_style_layer';
import type {CrossfadeParameters} from '../style/evaluation_parameters';
import type {StructArray, StructArrayMember} from '../util/struct_array';
import type VertexBuffer from '../gl/vertex_buffer';
import type {ImagePosition} from '../render/image_atlas';
import type {
Feature,
FeatureState,
GlobalProperties,
SourceExpression,
CompositeExpression
} from '../style-spec/expression';
import type {PossiblyEvaluated} from '../style/properties';
import type {FeatureStates} from '../source/source_state';
import type {FormattedSection} from '../style-spec/expression/types/formatted';
export type BinderUniform = {
name: string,
property: string,
binding: Uniform
};
function packColor(color: Color): [number, number] {
return [
packUint8ToFloat(255 * color.r, 255 * color.g),
packUint8ToFloat(255 * color.b, 255 * color.a)
];
}
/**
* `Binder` is the interface definition for the strategies for constructing,
* uploading, and binding paint property data as GLSL attributes. Most style-
* spec properties have a 1:1 relationship to shader attribute/uniforms, but
* some require multliple values per feature to be passed to the GPU, and in
* those cases we bind multiple attributes/uniforms.
*
* It has three implementations, one for each of the three strategies we use:
*
* * For _constant_ properties -- those whose value is a constant, or the constant
* result of evaluating a camera expression at a particular camera position -- we
* don't need a vertex attribute buffer, and instead use a uniform.
* * For data expressions, we use a vertex buffer with a single attribute value,
* the evaluated result of the source function for the given feature.
* * For composite expressions, we use a vertex buffer with two attributes: min and
* max values covering the range of zooms at which we expect the tile to be
* displayed. These values are calculated by evaluating the composite expression for
* the given feature at strategically chosen zoom levels. In addition to this
* attribute data, we also use a uniform value which the shader uses to interpolate
* between the min and max value at the final displayed zoom level. The use of a
* uniform allows us to cheaply update the value on every frame.
*
* Note that the shader source varies depending on whether we're using a uniform or
* attribute. We dynamically compile shaders at runtime to accomodate this.
*
* @private
*/
interface AttributeBinder {
populatePaintArray(length: number, feature: Feature, imagePositions: {[_: string]: ImagePosition}, canonical?: CanonicalTileID, formattedSection?: FormattedSection): void;
updatePaintArray(start: number, length: number, feature: Feature, featureState: FeatureState, imagePositions: {[_: string]: ImagePosition}): void;
upload(Context): void;
destroy(): void;
}
interface UniformBinder {
uniformNames: Array;
setUniform(uniform: Uniform<*>, globals: GlobalProperties, currentValue: PossiblyEvaluatedPropertyValue<*>, uniformName: string): void;
getBinding(context: Context, location: WebGLUniformLocation, name: string): $Shape>;
}
class ConstantBinder implements UniformBinder {
value: mixed;
type: string;
uniformNames: Array;
constructor(value: mixed, names: Array, type: string) {
this.value = value;
this.uniformNames = names.map(name => `u_${name}`);
this.type = type;
}
setUniform(uniform: Uniform<*>, globals: GlobalProperties, currentValue: PossiblyEvaluatedPropertyValue): void {
uniform.set(currentValue.constantOr(this.value));
}
getBinding(context: Context, location: WebGLUniformLocation, _: string): $Shape> {
return (this.type === 'color') ?
new UniformColor(context, location) :
new Uniform1f(context, location);
}
}
class CrossFadedConstantBinder implements UniformBinder {
uniformNames: Array;
patternFrom: ?Array;
patternTo: ?Array;
pixelRatioFrom: number;
pixelRatioTo: number;
constructor(value: mixed, names: Array) {
this.uniformNames = names.map(name => `u_${name}`);
this.patternFrom = null;
this.patternTo = null;
this.pixelRatioFrom = 1.0;
this.pixelRatioTo = 1.0;
}
setConstantPatternPositions(posTo: ImagePosition, posFrom: ImagePosition) {
this.pixelRatioFrom = posFrom.pixelRatio;
this.pixelRatioTo = posTo.pixelRatio;
this.patternFrom = posFrom.tlbr;
this.patternTo = posTo.tlbr;
}
setUniform(uniform: Uniform<*>, globals: GlobalProperties, currentValue: PossiblyEvaluatedPropertyValue, uniformName: string) {
const pos =
uniformName === 'u_pattern_to' ? this.patternTo :
uniformName === 'u_pattern_from' ? this.patternFrom :
uniformName === 'u_pixel_ratio_to' ? this.pixelRatioTo :
uniformName === 'u_pixel_ratio_from' ? this.pixelRatioFrom : null;
if (pos) uniform.set(pos);
}
getBinding(context: Context, location: WebGLUniformLocation, name: string): $Shape> {
return name.substr(0, 9) === 'u_pattern' ?
new Uniform4f(context, location) :
new Uniform1f(context, location);
}
}
class SourceExpressionBinder implements AttributeBinder {
expression: SourceExpression;
type: string;
maxValue: number;
paintVertexArray: StructArray;
paintVertexAttributes: Array;
paintVertexBuffer: ?VertexBuffer;
constructor(expression: SourceExpression, names: Array, type: string, PaintVertexArray: Class) {
this.expression = expression;
this.type = type;
this.maxValue = 0;
this.paintVertexAttributes = names.map((name) => ({
name: `a_${name}`,
type: 'Float32',
components: type === 'color' ? 2 : 1,
offset: 0
}));
this.paintVertexArray = new PaintVertexArray();
}
populatePaintArray(newLength: number, feature: Feature, imagePositions: {[_: string]: ImagePosition}, canonical?: CanonicalTileID, formattedSection?: FormattedSection) {
const start = this.paintVertexArray.length;
const value = this.expression.evaluate(new EvaluationParameters(0), feature, {}, canonical, [], formattedSection);
this.paintVertexArray.resize(newLength);
this._setPaintValue(start, newLength, value);
}
updatePaintArray(start: number, end: number, feature: Feature, featureState: FeatureState) {
const value = this.expression.evaluate({zoom: 0}, feature, featureState);
this._setPaintValue(start, end, value);
}
_setPaintValue(start, end, value) {
if (this.type === 'color') {
const color = packColor(value);
for (let i = start; i < end; i++) {
this.paintVertexArray.emplace(i, color[0], color[1]);
}
} else {
for (let i = start; i < end; i++) {
this.paintVertexArray.emplace(i, value);
}
this.maxValue = Math.max(this.maxValue, Math.abs(value));
}
}
upload(context: Context) {
if (this.paintVertexArray && this.paintVertexArray.arrayBuffer) {
if (this.paintVertexBuffer && this.paintVertexBuffer.buffer) {
this.paintVertexBuffer.updateData(this.paintVertexArray);
} else {
this.paintVertexBuffer = context.createVertexBuffer(this.paintVertexArray, this.paintVertexAttributes, this.expression.isStateDependent);
}
}
}
destroy() {
if (this.paintVertexBuffer) {
this.paintVertexBuffer.destroy();
}
}
}
class CompositeExpressionBinder implements AttributeBinder, UniformBinder {
expression: CompositeExpression;
uniformNames: Array;
type: string;
useIntegerZoom: boolean;
zoom: number;
maxValue: number;
paintVertexArray: StructArray;
paintVertexAttributes: Array;
paintVertexBuffer: ?VertexBuffer;
constructor(expression: CompositeExpression, names: Array, type: string, useIntegerZoom: boolean, zoom: number, PaintVertexArray: Class) {
this.expression = expression;
this.uniformNames = names.map(name => `u_${name}_t`);
this.type = type;
this.useIntegerZoom = useIntegerZoom;
this.zoom = zoom;
this.maxValue = 0;
this.paintVertexAttributes = names.map((name) => ({
name: `a_${name}`,
type: 'Float32',
components: type === 'color' ? 4 : 2,
offset: 0
}));
this.paintVertexArray = new PaintVertexArray();
}
populatePaintArray(newLength: number, feature: Feature, imagePositions: {[_: string]: ImagePosition}, canonical?: CanonicalTileID, formattedSection?: FormattedSection) {
const min = this.expression.evaluate(new EvaluationParameters(this.zoom), feature, {}, canonical, [], formattedSection);
const max = this.expression.evaluate(new EvaluationParameters(this.zoom + 1), feature, {}, canonical, [], formattedSection);
const start = this.paintVertexArray.length;
this.paintVertexArray.resize(newLength);
this._setPaintValue(start, newLength, min, max);
}
updatePaintArray(start: number, end: number, feature: Feature, featureState: FeatureState) {
const min = this.expression.evaluate({zoom: this.zoom}, feature, featureState);
const max = this.expression.evaluate({zoom: this.zoom + 1}, feature, featureState);
this._setPaintValue(start, end, min, max);
}
_setPaintValue(start, end, min, max) {
if (this.type === 'color') {
const minColor = packColor(min);
const maxColor = packColor(max);
for (let i = start; i < end; i++) {
this.paintVertexArray.emplace(i, minColor[0], minColor[1], maxColor[0], maxColor[1]);
}
} else {
for (let i = start; i < end; i++) {
this.paintVertexArray.emplace(i, min, max);
}
this.maxValue = Math.max(this.maxValue, Math.abs(min), Math.abs(max));
}
}
upload(context: Context) {
if (this.paintVertexArray && this.paintVertexArray.arrayBuffer) {
if (this.paintVertexBuffer && this.paintVertexBuffer.buffer) {
this.paintVertexBuffer.updateData(this.paintVertexArray);
} else {
this.paintVertexBuffer = context.createVertexBuffer(this.paintVertexArray, this.paintVertexAttributes, this.expression.isStateDependent);
}
}
}
destroy() {
if (this.paintVertexBuffer) {
this.paintVertexBuffer.destroy();
}
}
setUniform(uniform: Uniform<*>, globals: GlobalProperties): void {
const currentZoom = this.useIntegerZoom ? Math.floor(globals.zoom) : globals.zoom;
const factor = clamp(this.expression.interpolationFactor(currentZoom, this.zoom, this.zoom + 1), 0, 1);
uniform.set(factor);
}
getBinding(context: Context, location: WebGLUniformLocation, _: string): Uniform1f {
return new Uniform1f(context, location);
}
}
class CrossFadedCompositeBinder implements AttributeBinder {
expression: CompositeExpression;
type: string;
useIntegerZoom: boolean;
zoom: number;
layerId: string;
zoomInPaintVertexArray: StructArray;
zoomOutPaintVertexArray: StructArray;
zoomInPaintVertexBuffer: ?VertexBuffer;
zoomOutPaintVertexBuffer: ?VertexBuffer;
paintVertexAttributes: Array;
constructor(expression: CompositeExpression, type: string, useIntegerZoom: boolean, zoom: number, PaintVertexArray: Class, layerId: string) {
this.expression = expression;
this.type = type;
this.useIntegerZoom = useIntegerZoom;
this.zoom = zoom;
this.layerId = layerId;
this.zoomInPaintVertexArray = new PaintVertexArray();
this.zoomOutPaintVertexArray = new PaintVertexArray();
}
populatePaintArray(length: number, feature: Feature, imagePositions: {[_: string]: ImagePosition}) {
const start = this.zoomInPaintVertexArray.length;
this.zoomInPaintVertexArray.resize(length);
this.zoomOutPaintVertexArray.resize(length);
this._setPaintValues(start, length, feature.patterns && feature.patterns[this.layerId], imagePositions);
}
updatePaintArray(start: number, end: number, feature: Feature, featureState: FeatureState, imagePositions: {[_: string]: ImagePosition}) {
this._setPaintValues(start, end, feature.patterns && feature.patterns[this.layerId], imagePositions);
}
_setPaintValues(start, end, patterns, positions) {
if (!positions || !patterns) return;
const {min, mid, max} = patterns;
const imageMin = positions[min];
const imageMid = positions[mid];
const imageMax = positions[max];
if (!imageMin || !imageMid || !imageMax) return;
// We populate two paint arrays because, for cross-faded properties, we don't know which direction
// we're cross-fading to at layout time. In order to keep vertex attributes to a minimum and not pass
// unnecessary vertex data to the shaders, we determine which to upload at draw time.
for (let i = start; i < end; i++) {
this.zoomInPaintVertexArray.emplace(i,
imageMid.tl[0], imageMid.tl[1], imageMid.br[0], imageMid.br[1],
imageMin.tl[0], imageMin.tl[1], imageMin.br[0], imageMin.br[1],
imageMid.pixelRatio,
imageMin.pixelRatio,
);
this.zoomOutPaintVertexArray.emplace(i,
imageMid.tl[0], imageMid.tl[1], imageMid.br[0], imageMid.br[1],
imageMax.tl[0], imageMax.tl[1], imageMax.br[0], imageMax.br[1],
imageMid.pixelRatio,
imageMax.pixelRatio,
);
}
}
upload(context: Context) {
if (this.zoomInPaintVertexArray && this.zoomInPaintVertexArray.arrayBuffer && this.zoomOutPaintVertexArray && this.zoomOutPaintVertexArray.arrayBuffer) {
this.zoomInPaintVertexBuffer = context.createVertexBuffer(this.zoomInPaintVertexArray, patternAttributes.members, this.expression.isStateDependent);
this.zoomOutPaintVertexBuffer = context.createVertexBuffer(this.zoomOutPaintVertexArray, patternAttributes.members, this.expression.isStateDependent);
}
}
destroy() {
if (this.zoomOutPaintVertexBuffer) this.zoomOutPaintVertexBuffer.destroy();
if (this.zoomInPaintVertexBuffer) this.zoomInPaintVertexBuffer.destroy();
}
}
/**
* ProgramConfiguration contains the logic for binding style layer properties and tile
* layer feature data into GL program uniforms and vertex attributes.
*
* Non-data-driven property values are bound to shader uniforms. Data-driven property
* values are bound to vertex attributes. In order to support a uniform GLSL syntax over
* both, [Mapbox GL Shaders](https://github.com/mapbox/mapbox-gl-shaders) defines a `#pragma`
* abstraction, which ProgramConfiguration is responsible for implementing. At runtime,
* it examines the attributes of a particular layer, combines this with fixed knowledge
* about how layers of the particular type are implemented, and determines which uniforms
* and vertex attributes will be required. It can then substitute the appropriate text
* into the shader source code, create and link a program, and bind the uniforms and
* vertex attributes in preparation for drawing.
*
* When a vector tile is parsed, this same configuration information is used to
* populate the attribute buffers needed for data-driven styling using the zoom
* level and feature property data.
*
* @private
*/
export default class ProgramConfiguration {
binders: {[_: string]: (AttributeBinder | UniformBinder) };
cacheKey: string;
_buffers: Array;
constructor(layer: TypedStyleLayer, zoom: number, filterProperties: (_: string) => boolean) {
this.binders = {};
this._buffers = [];
const keys = [];
for (const property in layer.paint._values) {
if (!filterProperties(property)) continue;
const value = layer.paint.get(property);
if (!(value instanceof PossiblyEvaluatedPropertyValue) || !supportsPropertyExpression(value.property.specification)) {
continue;
}
const names = paintAttributeNames(property, layer.type);
const expression = value.value;
const type = value.property.specification.type;
const useIntegerZoom = value.property.useIntegerZoom;
const propType = value.property.specification['property-type'];
const isCrossFaded = propType === 'cross-faded' || propType === 'cross-faded-data-driven';
if (expression.kind === 'constant') {
this.binders[property] = isCrossFaded ?
new CrossFadedConstantBinder(expression.value, names) :
new ConstantBinder(expression.value, names, type);
keys.push(`/u_${property}`);
} else if (expression.kind === 'source' || isCrossFaded) {
const StructArrayLayout = layoutType(property, type, 'source');
this.binders[property] = isCrossFaded ?
new CrossFadedCompositeBinder(expression, type, useIntegerZoom, zoom, StructArrayLayout, layer.id) :
new SourceExpressionBinder(expression, names, type, StructArrayLayout);
keys.push(`/a_${property}`);
} else {
const StructArrayLayout = layoutType(property, type, 'composite');
this.binders[property] = new CompositeExpressionBinder(expression, names, type, useIntegerZoom, zoom, StructArrayLayout);
keys.push(`/z_${property}`);
}
}
this.cacheKey = keys.sort().join('');
}
getMaxValue(property: string): number {
const binder = this.binders[property];
return binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder ? binder.maxValue : 0;
}
populatePaintArrays(newLength: number, feature: Feature, imagePositions: {[_: string]: ImagePosition}, canonical?: CanonicalTileID, formattedSection?: FormattedSection) {
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedCompositeBinder)
(binder: AttributeBinder).populatePaintArray(newLength, feature, imagePositions, canonical, formattedSection);
}
}
setConstantPatternPositions(posTo: ImagePosition, posFrom: ImagePosition) {
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof CrossFadedConstantBinder)
binder.setConstantPatternPositions(posTo, posFrom);
}
}
updatePaintArrays(featureStates: FeatureStates, featureMap: FeaturePositionMap, vtLayer: VectorTileLayer, layer: TypedStyleLayer, imagePositions: {[_: string]: ImagePosition}): boolean {
let dirty: boolean = false;
for (const id in featureStates) {
const positions = featureMap.getPositions(id);
for (const pos of positions) {
const feature = vtLayer.feature(pos.index);
for (const property in this.binders) {
const binder = this.binders[property];
if ((binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder ||
binder instanceof CrossFadedCompositeBinder) && (binder: any).expression.isStateDependent === true) {
//AHM: Remove after https://github.com/mapbox/mapbox-gl-js/issues/6255
const value = layer.paint.get(property);
(binder: any).expression = value.value;
(binder: AttributeBinder).updatePaintArray(pos.start, pos.end, feature, featureStates[id], imagePositions);
dirty = true;
}
}
}
}
return dirty;
}
defines(): Array {
const result = [];
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof ConstantBinder || binder instanceof CrossFadedConstantBinder) {
result.push(...binder.uniformNames.map(name => `#define HAS_UNIFORM_${name}`));
}
}
return result;
}
getBinderAttributes(): Array {
const result = [];
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder) {
for (let i = 0; i < binder.paintVertexAttributes.length; i++) {
result.push(binder.paintVertexAttributes[i].name);
}
} else if (binder instanceof CrossFadedCompositeBinder) {
for (let i = 0; i < patternAttributes.members.length; i++) {
result.push(patternAttributes.members[i].name);
}
}
}
return result;
}
getBinderUniforms(): Array {
const uniforms = [];
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof ConstantBinder || binder instanceof CrossFadedConstantBinder || binder instanceof CompositeExpressionBinder) {
for (const uniformName of binder.uniformNames) {
uniforms.push(uniformName);
}
}
}
return uniforms;
}
getPaintVertexBuffers(): Array {
return this._buffers;
}
getUniforms(context: Context, locations: UniformLocations): Array {
const uniforms = [];
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof ConstantBinder || binder instanceof CrossFadedConstantBinder || binder instanceof CompositeExpressionBinder) {
for (const name of binder.uniformNames) {
if (locations[name]) {
const binding = binder.getBinding(context, locations[name], name);
uniforms.push({name, property, binding});
}
}
}
}
return uniforms;
}
setUniforms(context: Context, binderUniforms: Array, properties: PossiblyEvaluated, globals: GlobalProperties) {
// Uniform state bindings are owned by the Program, but we set them
// from within the ProgramConfiguraton's binder members.
for (const {name, property, binding} of binderUniforms) {
(this.binders[property]: any).setUniform(binding, globals, properties.get(property), name);
}
}
updatePaintBuffers(crossfade?: CrossfadeParameters) {
this._buffers = [];
for (const property in this.binders) {
const binder = this.binders[property];
if (crossfade && binder instanceof CrossFadedCompositeBinder) {
const patternVertexBuffer = crossfade.fromScale === 2 ? binder.zoomInPaintVertexBuffer : binder.zoomOutPaintVertexBuffer;
if (patternVertexBuffer) this._buffers.push(patternVertexBuffer);
} else if ((binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder) && binder.paintVertexBuffer) {
this._buffers.push(binder.paintVertexBuffer);
}
}
}
upload(context: Context) {
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedCompositeBinder)
binder.upload(context);
}
this.updatePaintBuffers();
}
destroy() {
for (const property in this.binders) {
const binder = this.binders[property];
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedCompositeBinder)
binder.destroy();
}
}
}
export class ProgramConfigurationSet {
programConfigurations: {[_: string]: ProgramConfiguration};
needsUpload: boolean;
_featureMap: FeaturePositionMap;
_bufferOffset: number;
constructor(layers: $ReadOnlyArray, zoom: number, filterProperties: (_: string) => boolean = () => true) {
this.programConfigurations = {};
for (const layer of layers) {
this.programConfigurations[layer.id] = new ProgramConfiguration(layer, zoom, filterProperties);
}
this.needsUpload = false;
this._featureMap = new FeaturePositionMap();
this._bufferOffset = 0;
}
populatePaintArrays(length: number, feature: Feature, index: number, imagePositions: {[_: string]: ImagePosition}, canonical: CanonicalTileID, formattedSection?: FormattedSection) {
for (const key in this.programConfigurations) {
this.programConfigurations[key].populatePaintArrays(length, feature, imagePositions, canonical, formattedSection);
}
if (feature.id !== undefined) {
this._featureMap.add(feature.id, index, this._bufferOffset, length);
}
this._bufferOffset = length;
this.needsUpload = true;
}
updatePaintArrays(featureStates: FeatureStates, vtLayer: VectorTileLayer, layers: $ReadOnlyArray, imagePositions: {[_: string]: ImagePosition}) {
for (const layer of layers) {
this.needsUpload = this.programConfigurations[layer.id].updatePaintArrays(featureStates, this._featureMap, vtLayer, layer, imagePositions) || this.needsUpload;
}
}
get(layerId: string) {
return this.programConfigurations[layerId];
}
upload(context: Context) {
if (!this.needsUpload) return;
for (const layerId in this.programConfigurations) {
this.programConfigurations[layerId].upload(context);
}
this.needsUpload = false;
}
destroy() {
for (const layerId in this.programConfigurations) {
this.programConfigurations[layerId].destroy();
}
}
}
function paintAttributeNames(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'],
'line-pattern': ['pattern_to', 'pattern_from', 'pixel_ratio_to', 'pixel_ratio_from'],
'fill-pattern': ['pattern_to', 'pattern_from', 'pixel_ratio_to', 'pixel_ratio_from'],
'fill-extrusion-pattern': ['pattern_to', 'pattern_from', 'pixel_ratio_to', 'pixel_ratio_from'],
};
return attributeNameExceptions[property] || [property.replace(`${type}-`, '').replace(/-/g, '_')];
}
function getLayoutException(property) {
const propertyExceptions = {
'line-pattern':{
'source': PatternLayoutArray,
'composite': PatternLayoutArray
},
'fill-pattern': {
'source': PatternLayoutArray,
'composite': PatternLayoutArray
},
'fill-extrusion-pattern':{
'source': PatternLayoutArray,
'composite': PatternLayoutArray
}
};
return propertyExceptions[property];
}
function layoutType(property, type, binderType) {
const defaultLayouts = {
'color': {
'source': StructArrayLayout2f8,
'composite': StructArrayLayout4f16
},
'number': {
'source': StructArrayLayout1f4,
'composite': StructArrayLayout2f8
}
};
const layoutException = getLayoutException(property);
return layoutException && layoutException[binderType] || defaultLayouts[type][binderType];
}
register('ConstantBinder', ConstantBinder);
register('CrossFadedConstantBinder', CrossFadedConstantBinder);
register('SourceExpressionBinder', SourceExpressionBinder);
register('CrossFadedCompositeBinder', CrossFadedCompositeBinder);
register('CompositeExpressionBinder', CompositeExpressionBinder);
register('ProgramConfiguration', ProgramConfiguration, {omit: ['_buffers']});
register('ProgramConfigurationSet', ProgramConfigurationSet);