Files
MapLibreParticleLab/scripts/generate-style-code.js
Anand Thakker f901e776b3 [core] Implement data-driven styling for {text,icon}-{color,opacity,halo-color,halo-blur,halo-width} (#7939)
* Add symbol dds attributes and adapt style code generation

* Update to mapbox-gl-js/master

* Refactor SymbolFeature as a subclass of GeometryTileFeature

Prepares for enabling DDS on symbol paint properties by allowing the
SymbolFeatures, which we keep around after constructing SymbolLayout,
to be used in evaluating data-driven paint properties later in the
layout process.

* Draft approach for splitting icon/text paint properties

The `Program` types are set up to bind GL attributes to each of the
data-driven paint properties specified in the `PaintProperties` type
provided.  Since `SymbolPaintProperties` specifies both `Text*` and
`Icon*` properties, the symbolIcon, symbolIconSDF, and symbolGlyph
programs each attempt to bind roughly double the number of attributes
that they actually need.

This change addresses this by:
- Adding the more specific `IconPaintProperties` and `TextPaintProperties` types, which are subsets of the full `SymbolPaintProperties`.
- The symbol layer continues to use its `SymbolPaintProperties paint` member to track layer property state, but it provides helpers that construct objects of each the specific `{Icon,Text}PaintProperties::Evaluated` type, for use by the painter.
- The three symbol programs instantiate `Program<>` using the appropriate `{Icon,Text}PaintProperties` type.

* check in generated style code

* Populate paint buffers for symbol DDS properties

* Address first round of review comments

* Refactor VectorTile{Layer,Feature} to explicitly share data

* Update submodule
2017-02-28 19:54:24 -08:00

180 lines
5.8 KiB
JavaScript

'use strict';
const fs = require('fs');
const ejs = require('ejs');
const spec = require('../mapbox-gl-js/src/style-spec/reference/v8');
const colorParser = require('csscolorparser');
require('./style-code');
function parseCSSColor(str) {
const color = colorParser.parseCSSColor(str);
return [
color[0] / 255 * color[3], color[1] / 255 * color[3], color[2] / 255 * color[3], color[3]
];
}
global.isDataDriven = function (property) {
return property['property-function'] === true;
};
global.evaluatedType = function (property) {
if (/-translate-anchor$/.test(property.name)) {
return 'TranslateAnchorType';
}
if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) {
return 'AlignmentType';
}
switch (property.type) {
case 'boolean':
return 'bool';
case 'number':
return 'float';
case 'string':
return 'std::string';
case 'enum':
return `${camelize(property.name)}Type`;
case 'color':
return `Color`;
case 'array':
if (property.length) {
return `std::array<${evaluatedType({type: property.value})}, ${property.length}>`;
} else {
return `std::vector<${evaluatedType({type: property.value})}>`;
}
default: throw new Error(`unknown type for ${property.name}`)
}
};
function attributeType(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'
}
const name = attributeNameExceptions[property.name] ||
property.name.replace(type + '-', '').replace(/-/g, '_');
return `attributes::a_${name}${name === 'offset' ? '<1>' : ''}`;
}
global.layoutPropertyType = function (property) {
if (isDataDriven(property)) {
return `DataDrivenLayoutProperty<${evaluatedType(property)}>`;
} else {
return `LayoutProperty<${evaluatedType(property)}>`;
}
};
global.paintPropertyType = function (property, type) {
if (isDataDriven(property)) {
return `DataDrivenPaintProperty<${evaluatedType(property)}, ${attributeType(property, type)}>`;
} else if (/-pattern$/.test(property.name) || property.name === 'line-dasharray') {
return `CrossFadedPaintProperty<${evaluatedType(property)}>`;
} else {
return `PaintProperty<${evaluatedType(property)}>`;
}
};
global.propertyValueType = function (property) {
if (isDataDriven(property)) {
return `DataDrivenPropertyValue<${evaluatedType(property)}>`;
} else {
return `PropertyValue<${evaluatedType(property)}>`;
}
};
global.defaultValue = function (property) {
// https://github.com/mapbox/mapbox-gl-native/issues/5258
if (property.name === 'line-round-limit') {
return 1;
}
if (property.name === 'fill-outline-color') {
return '{}';
}
switch (property.type) {
case 'number':
return property.default;
case 'string':
return JSON.stringify(property.default || "");
case 'enum':
if (property.default === undefined) {
return `${evaluatedType(property)}::Undefined`;
} else {
return `${evaluatedType(property)}::${camelize(property.default)}`;
}
case 'color':
const color = parseCSSColor(property.default).join(', ');
switch (color) {
case '0, 0, 0, 0':
return '{}';
case '0, 0, 0, 1':
return 'Color::black()';
case '1, 1, 1, 1':
return 'Color::white()';
default:
return `{ ${color} }`;
}
case 'array':
const defaults = (property.default || []).map((e) => defaultValue({ type: property.value, default: e }));
if (property.length) {
return `{{ ${defaults.join(', ')} }}`;
} else {
return `{ ${defaults.join(', ')} }`;
}
default:
return property.default;
}
};
const layerHpp = ejs.compile(fs.readFileSync('include/mbgl/style/layers/layer.hpp.ejs', 'utf8'), {strict: true});
const layerCpp = ejs.compile(fs.readFileSync('src/mbgl/style/layers/layer.cpp.ejs', 'utf8'), {strict: true});
const propertiesHpp = ejs.compile(fs.readFileSync('src/mbgl/style/layers/layer_properties.hpp.ejs', 'utf8'), {strict: true});
const propertiesCpp = ejs.compile(fs.readFileSync('src/mbgl/style/layers/layer_properties.cpp.ejs', 'utf8'), {strict: true});
const layers = Object.keys(spec.layer.type.values).map((type) => {
const layoutProperties = Object.keys(spec[`layout_${type}`]).reduce((memo, name) => {
if (name !== 'visibility') {
spec[`layout_${type}`][name].name = name;
memo.push(spec[`layout_${type}`][name]);
}
return memo;
}, []);
const paintProperties = Object.keys(spec[`paint_${type}`]).reduce((memo, name) => {
spec[`paint_${type}`][name].name = name;
memo.push(spec[`paint_${type}`][name]);
return memo;
}, []);
return {
type: type,
layoutProperties: layoutProperties,
paintProperties: paintProperties,
doc: spec.layer.type.values[type].doc,
layoutPropertiesByName: spec[`layout_${type}`],
paintPropertiesByName: spec[`paint_${type}`],
};
});
for (const layer of layers) {
const layerFileName = layer.type.replace('-', '_');
writeIfModified(`include/mbgl/style/layers/${layerFileName}_layer.hpp`, layerHpp(layer));
writeIfModified(`src/mbgl/style/layers/${layerFileName}_layer.cpp`, layerCpp(layer));
writeIfModified(`src/mbgl/style/layers/${layerFileName}_layer_properties.hpp`, propertiesHpp(layer));
writeIfModified(`src/mbgl/style/layers/${layerFileName}_layer_properties.cpp`, propertiesCpp(layer));
}
const propertySettersHpp = ejs.compile(fs.readFileSync('include/mbgl/style/conversion/make_property_setters.hpp.ejs', 'utf8'), {strict: true});
writeIfModified('include/mbgl/style/conversion/make_property_setters.hpp', propertySettersHpp({layers: layers}));