287 lines
9.4 KiB
JavaScript
Executable File
287 lines
9.4 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const ejs = require('ejs');
|
|
const spec = require('./style-spec');
|
|
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.isLightProperty = function (property) {
|
|
return property['light-property'] === true;
|
|
};
|
|
|
|
global.isOverridable = function (property) {
|
|
return ['text-color'].includes(property.name);
|
|
};
|
|
|
|
global.expressionType = function (property) {
|
|
switch (property.type) {
|
|
case 'boolean':
|
|
return 'BooleanType';
|
|
case 'number':
|
|
case 'enum':
|
|
return 'NumberType';
|
|
case 'image':
|
|
return 'ImageType';
|
|
case 'string':
|
|
return 'StringType';
|
|
case 'color':
|
|
return `ColorType`;
|
|
case 'formatted':
|
|
return `FormattedType`;
|
|
case 'array':
|
|
return `Array<${expressionType({type: property.value})}>`;
|
|
default: throw new Error(`unknown type for ${property.name}`)
|
|
}
|
|
};
|
|
|
|
global.evaluatedType = function (property) {
|
|
if (/-translate-anchor$/.test(property.name)) {
|
|
return 'TranslateAnchorType';
|
|
}
|
|
if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) {
|
|
return 'AlignmentType';
|
|
}
|
|
if (/^(text|icon)-anchor$/.test(property.name)) {
|
|
return 'SymbolAnchorType';
|
|
}
|
|
if (/position/.test(property.name)) {
|
|
return 'Position';
|
|
}
|
|
switch (property.type) {
|
|
case 'boolean':
|
|
return 'bool';
|
|
case 'number':
|
|
// TODO: Check if 'Rotation' should be used for other properties,
|
|
// such as icon-rotate
|
|
if (/bearing$/.test(property.name) &&
|
|
property.period == 360 &&
|
|
property.units =='degrees') {
|
|
return 'Rotation';
|
|
}
|
|
return /location$/.test(property.name) ? 'double' : 'float';
|
|
case 'resolvedImage':
|
|
return 'expression::Image';
|
|
case 'formatted':
|
|
return 'expression::Formatted';
|
|
case 'string':
|
|
return 'std::string';
|
|
case 'enum':
|
|
return (isLightProperty(property) ? 'Light' : '') + `${camelize(property.name)}Type`;
|
|
case 'color':
|
|
return `Color`;
|
|
case 'array':
|
|
if (property.length) {
|
|
return `std::array<${evaluatedType({type: property.value, name: property.name})}, ${property.length}>`;
|
|
} else {
|
|
return `std::vector<${evaluatedType({type: property.value, name: property.name})}>`;
|
|
}
|
|
default: throw new Error(`unknown type for ${property.name}`)
|
|
}
|
|
};
|
|
|
|
function attributeUniformType(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'],
|
|
'line-floor-width': ['floorwidth'],
|
|
'fill-pattern': ['pattern_to', 'pattern_from'],
|
|
'fill-extrusion-pattern': ['pattern_to', 'pattern_from']
|
|
}
|
|
const names = attributeNameExceptions[property.name] ||
|
|
[ property.name.replace(type + '-', '').replace(/-/g, '_') ];
|
|
|
|
return names.map(name => {
|
|
return `attributes::${name}, uniforms::${name}`
|
|
}).join(', ');
|
|
}
|
|
|
|
global.layoutPropertyType = function (property) {
|
|
switch (property['property-type']) {
|
|
case 'data-driven':
|
|
case 'cross-faded-data-driven':
|
|
return `DataDrivenLayoutProperty<${evaluatedType(property)}>`;
|
|
default:
|
|
return `LayoutProperty<${evaluatedType(property)}>`;
|
|
}
|
|
};
|
|
|
|
global.paintPropertyType = function (property, type) {
|
|
switch (property['property-type']) {
|
|
case 'data-driven':
|
|
if (isOverridable(property))
|
|
return `DataDrivenPaintProperty<${evaluatedType(property)}, ${attributeUniformType(property, type)}, true>`;
|
|
return `DataDrivenPaintProperty<${evaluatedType(property)}, ${attributeUniformType(property, type)}>`;
|
|
case 'cross-faded-data-driven':
|
|
return `CrossFadedDataDrivenPaintProperty<${evaluatedType(property)}, ${attributeUniformType(property, type)}>`;
|
|
case 'cross-faded':
|
|
return `CrossFadedPaintProperty<${evaluatedType(property)}>`;
|
|
default:
|
|
return `PaintProperty<${evaluatedType(property)}>`;
|
|
}
|
|
};
|
|
|
|
global.propertyValueType = function (property) {
|
|
switch (property['property-type']) {
|
|
case 'color-ramp':
|
|
return `ColorRampPropertyValue`;
|
|
default:
|
|
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 '{}';
|
|
}
|
|
|
|
if (property['property-type'] === 'color-ramp') {
|
|
return '{}';
|
|
}
|
|
|
|
switch (property.type) {
|
|
case 'number':
|
|
if (property.default === undefined) {
|
|
return 0;
|
|
} else {
|
|
return property.default;
|
|
}
|
|
case 'formatted':
|
|
case 'string':
|
|
case 'resolvedImage':
|
|
return property.default ? `{${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 collator = new Intl.Collator("en-US");
|
|
|
|
// Add this mock property that our SDF line shader needs so that it gets added to the list of
|
|
// "data driven" properties.
|
|
spec.paint_line['line-floor-width'] = {
|
|
"type": "number",
|
|
"default": 1,
|
|
"property-type": "data-driven"
|
|
};
|
|
|
|
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;
|
|
}, []);
|
|
|
|
// JSON doesn't have a defined order. We're going to sort them alphabetically
|
|
// to get a deterministic order.
|
|
layoutProperties.sort((a, b) => collator.compare(a.name, b.name));
|
|
|
|
const paintProperties = Object.keys(spec[`paint_${type}`]).reduce((memo, name) => {
|
|
spec[`paint_${type}`][name].name = name;
|
|
memo.push(spec[`paint_${type}`][name]);
|
|
return memo;
|
|
}, []);
|
|
|
|
// JSON doesn't have a defined order. We're going to sort them alphabetically
|
|
// to get a deterministic order.
|
|
paintProperties.sort((a, b) => collator.compare(a.name, b.name));
|
|
|
|
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(`src/mbgl/style/layers/${layerFileName}_layer_properties.hpp`, propertiesHpp(layer));
|
|
writeIfModified(`src/mbgl/style/layers/${layerFileName}_layer_properties.cpp`, propertiesCpp(layer));
|
|
|
|
// Remove our fake property for the external interace.
|
|
if (layer.type === 'line') {
|
|
layer.paintProperties = layer.paintProperties.filter(property => property.name !== 'line-floor-width');
|
|
}
|
|
|
|
writeIfModified(`include/mbgl/style/layers/${layerFileName}_layer.hpp`, layerHpp(layer));
|
|
writeIfModified(`src/mbgl/style/layers/${layerFileName}_layer.cpp`, layerCpp(layer));
|
|
}
|
|
|
|
// Light
|
|
const lightProperties = Object.keys(spec[`light`]).reduce((memo, name) => {
|
|
var property = spec[`light`][name];
|
|
property.name = name;
|
|
property['light-property'] = true;
|
|
memo.push(property);
|
|
return memo;
|
|
}, []);
|
|
|
|
// JSON doesn't have a defined order. We're going to sort them alphabetically
|
|
// to get a deterministic order.
|
|
lightProperties.sort((a, b) => collator.compare(a.name, b.name));
|
|
|
|
const lightHpp = ejs.compile(fs.readFileSync('include/mbgl/style/light.hpp.ejs', 'utf8'), {strict: true});
|
|
const lightCpp = ejs.compile(fs.readFileSync('src/mbgl/style/light.cpp.ejs', 'utf8'), {strict: true});
|
|
writeIfModified(`include/mbgl/style/light.hpp`, lightHpp({properties: lightProperties}));
|
|
writeIfModified(`src/mbgl/style/light.cpp`, lightCpp({properties: lightProperties}));
|