refactor functions
This commit is contained in:
42
bin/style.js
42
bin/style.js
@@ -6,7 +6,7 @@ module.exports = {
|
||||
"@land": "#eee",
|
||||
"@water": "#999",
|
||||
"@park": "#bda",
|
||||
"@road": "#fefefe",
|
||||
"@road": "#FF00FF",
|
||||
"@border": "#6d90ab",
|
||||
"@building": "#ddd",
|
||||
"@building_outline": "#ccc",
|
||||
@@ -53,28 +53,26 @@ module.exports = {
|
||||
"fill-color": "blue"
|
||||
}
|
||||
}, {
|
||||
"id": "roads",
|
||||
"layers": [{
|
||||
"id": "road",
|
||||
"source": "mapbox.mapbox-streets-v5",
|
||||
"source-layer": "road",
|
||||
"render": {
|
||||
"type": "line",
|
||||
"line-cap": "round",
|
||||
"line-join": "bevel"
|
||||
},
|
||||
"style": {
|
||||
"line-color": "@road",
|
||||
"line-blur": "@road_blur",
|
||||
"line-width": {
|
||||
"fn": "exponential",
|
||||
"z": 10,
|
||||
"val": -1,
|
||||
"slope": 0.2,
|
||||
"min": 1
|
||||
}
|
||||
"id": "road",
|
||||
"source": "mapbox.mapbox-streets-v5",
|
||||
"source-layer": "road",
|
||||
"render": {
|
||||
"type": "line",
|
||||
"line-cap": "round",
|
||||
"line-join": "bevel"
|
||||
},
|
||||
"style": {
|
||||
"line-color": "@road",
|
||||
"line-blur": "@road_blur",
|
||||
"line-width": {
|
||||
"fn": "exponential",
|
||||
"z": 10,
|
||||
"val": -1,
|
||||
"slope": 0.2,
|
||||
"min": 1,
|
||||
"max": 1000
|
||||
}
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"id": "building",
|
||||
"source": "mapbox.mapbox-streets-v5",
|
||||
|
||||
@@ -1,43 +1,72 @@
|
||||
#ifndef LLMR_STYLE_FUNCTION_PROPERTIES
|
||||
#define LLMR_STYLE_FUNCTION_PROPERTIES
|
||||
|
||||
#include <llmr/util/variant.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
namespace functions {
|
||||
template <typename T>
|
||||
struct ConstantFunction {
|
||||
inline ConstantFunction(const T &value) : value(value) {}
|
||||
inline T evaluate(float) const { return value; }
|
||||
|
||||
float null(float z, const std::vector<float>&);
|
||||
float constant(float z, const std::vector<float>& values);
|
||||
float min(float z, const std::vector<float>& values);
|
||||
float max(float z, const std::vector<float>& values);
|
||||
float stops(float z, const std::vector<float>& values);
|
||||
float linear(float z, const std::vector<float>& values);
|
||||
float exponential(float z, const std::vector<float>& values);
|
||||
private:
|
||||
const T value;
|
||||
};
|
||||
|
||||
}
|
||||
template <typename T>
|
||||
struct LinearFunction {
|
||||
inline LinearFunction(const T &value, float z_base, float slope, const T &min, const T &max)
|
||||
: value(value), min(min), max(max), z_base(z_base), slope(slope) {}
|
||||
T evaluate(float z) const;
|
||||
|
||||
struct FunctionProperty {
|
||||
typedef float (*fn)(float z, const std::vector<float>& values);
|
||||
private:
|
||||
const T value, min, max;
|
||||
const float z_base, slope;
|
||||
};
|
||||
|
||||
fn function;
|
||||
std::vector<float> values;
|
||||
template <typename T>
|
||||
struct ExponentialFunction {
|
||||
inline ExponentialFunction(const T &value, float z_base, float slope, const T &min,
|
||||
const T &max)
|
||||
: value(value), min(min), max(max), z_base(z_base), slope(slope) {}
|
||||
T evaluate(float z) const;
|
||||
|
||||
inline FunctionProperty() : function(&functions::null) {}
|
||||
private:
|
||||
const T value, min, max;
|
||||
const float z_base, slope;
|
||||
};
|
||||
|
||||
inline FunctionProperty(const FunctionProperty &property)
|
||||
: function(property.function), values(property.values) {}
|
||||
inline FunctionProperty(FunctionProperty &&property)
|
||||
: function(property.function), values(std::move(property.values)) {}
|
||||
template <typename T>
|
||||
struct StopsFunction {
|
||||
inline StopsFunction(const std::vector<std::pair<float, T>> &values) : values(values) {}
|
||||
T evaluate(float z) const;
|
||||
|
||||
private:
|
||||
const std::vector<std::pair<float, T>> values;
|
||||
};
|
||||
|
||||
inline void operator=(const FunctionProperty &rhs) {
|
||||
function = rhs.function;
|
||||
values = rhs.values;
|
||||
template <typename T>
|
||||
using Function = util::variant<
|
||||
ConstantFunction<T>,
|
||||
LinearFunction<T>,
|
||||
ExponentialFunction<T>,
|
||||
StopsFunction<T>
|
||||
>;
|
||||
|
||||
template <typename T>
|
||||
struct FunctionEvaluator {
|
||||
typedef T result_type;
|
||||
inline FunctionEvaluator(float z) : z(z) {}
|
||||
|
||||
template <template <typename> class Fn>
|
||||
inline result_type operator()(const Fn<T>& fn) {
|
||||
return fn.evaluate(z);
|
||||
}
|
||||
|
||||
inline FunctionProperty(float value) : function(&functions::constant), values(1, value) {}
|
||||
template <typename T> inline T evaluate(float z) const { return function(z, values); }
|
||||
private:
|
||||
float z;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -8,12 +8,15 @@
|
||||
namespace llmr {
|
||||
|
||||
typedef util::variant<
|
||||
FunctionProperty,
|
||||
TranslateAnchorType,
|
||||
RotateAnchorType,
|
||||
bool,
|
||||
float,
|
||||
Color,
|
||||
std::string,
|
||||
bool
|
||||
TranslateAnchorType,
|
||||
RotateAnchorType,
|
||||
Function<bool>,
|
||||
Function<float>,
|
||||
Function<Color>
|
||||
> PropertyValue;
|
||||
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ struct PrerenderProperties {
|
||||
|
||||
class RasterizeProperties {
|
||||
public:
|
||||
boost::optional<FunctionProperty> enabled;
|
||||
boost::optional<FunctionProperty> buffer;
|
||||
boost::optional<FunctionProperty> size;
|
||||
boost::optional<FunctionProperty> blur;
|
||||
boost::optional<Function<bool>> enabled;
|
||||
boost::optional<Function<float>> buffer;
|
||||
boost::optional<Function<float>> size;
|
||||
boost::optional<Function<float>> blur;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -39,6 +39,10 @@ public:
|
||||
const std::vector<std::string> &getAppliedClasses() const;
|
||||
void toggleClass(const std::string &name);
|
||||
|
||||
// Updates the styling information to reflect the current array
|
||||
// of applied classes.
|
||||
void updateClasses();
|
||||
|
||||
public:
|
||||
std::shared_ptr<Sprite> sprite;
|
||||
std::shared_ptr<StyleLayerGroup> layers;
|
||||
@@ -49,9 +53,6 @@ private:
|
||||
void updateSources();
|
||||
void updateSources(const std::shared_ptr<StyleLayerGroup> &group);
|
||||
|
||||
void updateProperties(const std::shared_ptr<StyleLayerGroup> &group, float z, timestamp t);
|
||||
|
||||
void updateClasses();
|
||||
|
||||
private:
|
||||
std::set<std::shared_ptr<Source>> activeSources;
|
||||
|
||||
@@ -24,8 +24,8 @@ public:
|
||||
std::unique_ptr<const RasterizeProperties> &&rasterize);
|
||||
|
||||
template <typename T> const T &getProperties() {
|
||||
if (style.is<T>()) {
|
||||
return style.get<T>();
|
||||
if (properties.is<T>()) {
|
||||
return properties.get<T>();
|
||||
} else {
|
||||
return defaultStyleProperties<T>();
|
||||
}
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
|
||||
// Updates the StyleProperties information in this layer by evaluating all
|
||||
// pending transitions and applied classes in order.
|
||||
void updateStyle(float z);
|
||||
void updateProperties(float z, timestamp t);
|
||||
|
||||
// Sets the list of classes and creates transitions to the currently applied values.
|
||||
void setClasses(const std::vector<std::string> &class_names, timestamp now,
|
||||
@@ -47,6 +47,11 @@ private:
|
||||
void applyClassProperties(ClassID class_id, std::set<PropertyKey> &already_applied,
|
||||
timestamp now, const PropertyTransition &defaultTransition);
|
||||
|
||||
// Sets the properties of this object by evaluating all pending transitions and
|
||||
// aplied classes in order.
|
||||
template <typename T> void applyStyleProperties(float z, timestamp t);
|
||||
template <typename T> void applyStyleProperty(PropertyKey key, T &, float z, timestamp t);
|
||||
|
||||
public:
|
||||
// The name of this layer.
|
||||
const std::string id;
|
||||
@@ -66,7 +71,7 @@ private:
|
||||
public:
|
||||
// Stores the evaluated, and cascaded styling information, specific to this
|
||||
// layer's type.
|
||||
StyleProperties style;
|
||||
StyleProperties properties;
|
||||
|
||||
// Rasterization properties are used for prerendering the tile to a bitmap,
|
||||
// which is then used as a raster image instead of rendering this layer
|
||||
|
||||
@@ -11,6 +11,7 @@ class StyleLayerGroup {
|
||||
public:
|
||||
void setClasses(const std::vector<std::string> &class_names, timestamp now,
|
||||
const PropertyTransition &defaultTransition);
|
||||
void updateProperties(float z, timestamp t);
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<StyleLayer>> layers;
|
||||
|
||||
@@ -59,6 +59,11 @@ private:
|
||||
template <typename T>
|
||||
bool parseStyleProperty(const char *property_name, const std::vector<PropertyKey> &keys, ClassProperties &klass, JSVal value);
|
||||
|
||||
template <typename T>
|
||||
bool parseFunction(PropertyKey key, ClassProperties &klass, JSVal value);
|
||||
template <typename T>
|
||||
T parseFunctionArgument(JSVal value);
|
||||
|
||||
|
||||
FilterExpression parseFilter(JSVal, FilterExpression::Operator op);
|
||||
FilterExpression parseFilter(JSVal);
|
||||
|
||||
@@ -1,86 +1,131 @@
|
||||
#include <llmr/style/function_properties.hpp>
|
||||
#include <llmr/style/types.hpp>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
float functions::null(float, const std::vector<float>&) {
|
||||
return 0;
|
||||
|
||||
template <>
|
||||
bool LinearFunction<bool>::evaluate(float z) const {
|
||||
return z < z_base ? slope >= 0 : z > z_base ? slope >= 0 : value;
|
||||
}
|
||||
|
||||
float functions::constant(float, const std::vector<float>& values) {
|
||||
return values.size() >= 1 ? values.front() : 0.0f;
|
||||
template <>
|
||||
float LinearFunction<float>::evaluate(float z) const {
|
||||
return std::fmin(std::fmax(min, value + (z - z_base) * slope), max);
|
||||
}
|
||||
|
||||
float functions::min(float z, const std::vector<float>& values) {
|
||||
return values.size() >= 1 && z >= values.front();
|
||||
template <>
|
||||
Color LinearFunction<Color>::evaluate(float z) const {
|
||||
return {{
|
||||
std::fmin(std::fmax(min[0], value[0] + (z - z_base) * slope), max[0]),
|
||||
std::fmin(std::fmax(min[1], value[1] + (z - z_base) * slope), max[1]),
|
||||
std::fmin(std::fmax(min[2], value[2] + (z - z_base) * slope), max[2]),
|
||||
std::fmin(std::fmax(min[3], value[3] + (z - z_base) * slope), max[3])
|
||||
}};
|
||||
}
|
||||
|
||||
float functions::max(float z, const std::vector<float>& values) {
|
||||
return values.size() >= 1 && z <= values.front();
|
||||
|
||||
template <>
|
||||
bool ExponentialFunction<bool>::evaluate(float z) const {
|
||||
return z < z_base ? slope >= 0 : z > z_base ? slope >= 0 : value;
|
||||
}
|
||||
|
||||
float functions::stops(float z, const std::vector<float>& stops) {
|
||||
// We need an even number of stop values.
|
||||
if (stops.size() % 2 != 0) return 0;
|
||||
template <>
|
||||
float ExponentialFunction<float>::evaluate(float z) const {
|
||||
return std::fmin(std::fmax(min, value + std::pow(1.75, (z - z_base)) * slope), max);
|
||||
}
|
||||
|
||||
template <>
|
||||
Color ExponentialFunction<Color>::evaluate(float z) const {
|
||||
return {{
|
||||
std::fmin(std::fmax(min[0], value[0] + float(std::pow(1.75, (z - z_base))) * slope), max[0]),
|
||||
std::fmin(std::fmax(min[1], value[1] + float(std::pow(1.75, (z - z_base))) * slope), max[1]),
|
||||
std::fmin(std::fmax(min[2], value[2] + float(std::pow(1.75, (z - z_base))) * slope), max[2]),
|
||||
std::fmin(std::fmax(min[3], value[3] + float(std::pow(1.75, (z - z_base))) * slope), max[3])
|
||||
}};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T exponentialInterpolate(T smaller, T larger, const float factor);
|
||||
|
||||
template <>
|
||||
inline float exponentialInterpolate(const float smaller, const float larger, const float factor) {
|
||||
// Linear interpolation if base is 0
|
||||
if (smaller == 0.0f) {
|
||||
return factor * larger;
|
||||
}
|
||||
// Exponential interpolation between the values
|
||||
return smaller * std::pow(larger / smaller, factor);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool exponentialInterpolate(const bool smaller, const bool larger, const float factor) {
|
||||
return exponentialInterpolate(float(smaller), float(larger), factor);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Color exponentialInterpolate(const Color smaller, const Color larger, const float factor) {
|
||||
return {{
|
||||
exponentialInterpolate(smaller[0], larger[0], factor),
|
||||
exponentialInterpolate(smaller[1], larger[1], factor),
|
||||
exponentialInterpolate(smaller[2], larger[2], factor),
|
||||
exponentialInterpolate(smaller[3], larger[3], factor)
|
||||
}};
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
T exponentialDefault();
|
||||
|
||||
template <> bool exponentialDefault() { return true; }
|
||||
template <> float exponentialDefault() { return 1.0f; }
|
||||
template <> Color exponentialDefault() { return {{ 0, 0, 0, 1 }}; }
|
||||
|
||||
|
||||
template <typename T>
|
||||
T StopsFunction<T>::evaluate(float z) const {
|
||||
bool smaller = false;
|
||||
float smaller_z = 0.0;
|
||||
float smaller_val = 0.0;
|
||||
float smaller_z = 0.0f;
|
||||
T smaller_val {};
|
||||
bool larger = false;
|
||||
float larger_z = 0.0;
|
||||
float larger_val = 0.0;
|
||||
float larger_z = 0.0f;
|
||||
T larger_val {};
|
||||
|
||||
for (uint32_t i = 0; i < stops.size(); i += 2) {
|
||||
float stop_z = stops[i];
|
||||
float stop_val = stops[i + 1];
|
||||
if (stop_z <= z && (!smaller || smaller_z < stop_z)) { smaller = true; smaller_z = stop_z; smaller_val = stop_val; }
|
||||
if (stop_z >= z && (!larger || larger_z > stop_z)) { larger = true; larger_z = stop_z; larger_val = stop_val; }
|
||||
for (uint32_t i = 0; i < values.size(); i++) {
|
||||
float stop_z = values[i].first;
|
||||
T stop_val = values[i].second;
|
||||
if (stop_z <= z && (!smaller || smaller_z < stop_z)) {
|
||||
smaller = true;
|
||||
smaller_z = stop_z;
|
||||
smaller_val = stop_val;
|
||||
}
|
||||
if (stop_z >= z && (!larger || larger_z > stop_z)) {
|
||||
larger = true;
|
||||
larger_z = stop_z;
|
||||
larger_val = stop_val;
|
||||
}
|
||||
}
|
||||
|
||||
if (smaller && larger) {
|
||||
if (larger_z == smaller_z || larger_val == smaller_val) return smaller_val;
|
||||
if (larger_z == smaller_z || larger_val == smaller_val) {
|
||||
return smaller_val;
|
||||
}
|
||||
float factor = (z - smaller_z) / (larger_z - smaller_z);
|
||||
// Linear interpolation if base is 0
|
||||
if (smaller_val == 0) return factor * larger_val;
|
||||
// Exponential interpolation between the values
|
||||
return smaller_val * std::pow(larger_val / smaller_val, factor);
|
||||
return exponentialInterpolate<T>(smaller_val, larger_val, factor);
|
||||
} else if (larger) {
|
||||
return larger_val;
|
||||
} else if (smaller) {
|
||||
return smaller_val;
|
||||
} else {
|
||||
// No stop defined.
|
||||
return 1;
|
||||
return exponentialDefault<T>();
|
||||
}
|
||||
}
|
||||
|
||||
float functions::linear(float z, const std::vector<float>& values) {
|
||||
if (values.size() != 5) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const float z_base = values[0];
|
||||
const float val = values[1];
|
||||
const float slope = values[2];
|
||||
const float min = values[3];
|
||||
const float max = values[4];
|
||||
|
||||
return std::fmin(std::fmax(min, val + (z - z_base) * slope), max);
|
||||
}
|
||||
|
||||
float functions::exponential(float z, const std::vector<float>& values) {
|
||||
if (values.size() != 5) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const float z_base = values[0];
|
||||
const float val = values[1];
|
||||
const float slope = values[2];
|
||||
const float min = values[3];
|
||||
const float max = values[4];
|
||||
|
||||
return std::fmin(std::fmax(min, val + std::pow(1.75, (z - z_base)) * slope), max);
|
||||
}
|
||||
template bool StopsFunction<bool>::evaluate(float z) const;
|
||||
template float StopsFunction<float>::evaluate(float z) const;
|
||||
template Color StopsFunction<Color>::evaluate(float z) const;
|
||||
|
||||
}
|
||||
|
||||
@@ -51,6 +51,6 @@ const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = {
|
||||
{ PropertyKey::BackgroundColor, Color({{ 0, 0, 0, 0 }}) },
|
||||
};
|
||||
|
||||
const PropertyValue PropertyFallbackValue::defaultProperty;
|
||||
const PropertyValue PropertyFallbackValue::defaultProperty = PropertyValue();
|
||||
|
||||
}
|
||||
|
||||
@@ -38,154 +38,14 @@ void Style::updateSources(const std::shared_ptr<StyleLayerGroup> &group) {
|
||||
}
|
||||
}
|
||||
|
||||
StyleProperties resetClassProperties(StyleLayer &layer) {
|
||||
if (layer.layers) {
|
||||
return CompositeProperties();
|
||||
} else if (layer.bucket) {
|
||||
switch (layer.bucket->type) {
|
||||
case BucketType::Fill: return FillProperties(); break;
|
||||
case BucketType::Line: return LineProperties(); break;
|
||||
case BucketType::Icon: return IconProperties(); break;
|
||||
case BucketType::Text: return TextProperties(); break;
|
||||
case BucketType::Raster: return RasterProperties(); break;
|
||||
default: return std::false_type(); break;
|
||||
}
|
||||
} else {
|
||||
return BackgroundProperties();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void applyProperty(const ClassProperties &properties, PropertyKey key, T &target) {
|
||||
auto it = properties.properties.find(key);
|
||||
if (it != properties.properties.end()) {
|
||||
const PropertyValue &value = it->second;
|
||||
if (value.is<T>()) {
|
||||
target = value.get<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void applyProperty(const ClassProperties &properties, PropertyKey key, T &target, float z) {
|
||||
auto it = properties.properties.find(key);
|
||||
if (it != properties.properties.end()) {
|
||||
const PropertyValue &value = it->second;
|
||||
if (value.is<FunctionProperty>()) {
|
||||
target = value.get<FunctionProperty>().evaluate<T>(z);
|
||||
} else if (value.is<bool>()) {
|
||||
target = value.get<bool>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void applyClassProperties(StyleProperties &style, const ClassProperties &properties, float z) {
|
||||
if (style.is<FillProperties>()) {
|
||||
FillProperties &fill = style.get<FillProperties>();
|
||||
applyProperty(properties, PropertyKey::FillEnabled, fill.enabled, z);
|
||||
applyProperty(properties, PropertyKey::FillAntialias, fill.antialias, z);
|
||||
applyProperty(properties, PropertyKey::FillOpacity, fill.opacity, z);
|
||||
applyProperty(properties, PropertyKey::FillColor, fill.fill_color);
|
||||
applyProperty(properties, PropertyKey::FillOutlineColor, fill.stroke_color);
|
||||
applyProperty(properties, PropertyKey::FillTranslateX, fill.translate[0], z);
|
||||
applyProperty(properties, PropertyKey::FillTranslateY, fill.translate[1], z);
|
||||
applyProperty(properties, PropertyKey::FillTranslateAnchor, fill.translateAnchor);
|
||||
applyProperty(properties, PropertyKey::FillImage, fill.image);
|
||||
} else if (style.is<LineProperties>()) {
|
||||
LineProperties &line = style.get<LineProperties>();
|
||||
applyProperty(properties, PropertyKey::LineEnabled, line.enabled, z);
|
||||
applyProperty(properties, PropertyKey::LineOpacity, line.opacity, z);
|
||||
applyProperty(properties, PropertyKey::LineColor, line.color);
|
||||
applyProperty(properties, PropertyKey::LineTranslateX, line.translate[0], z);
|
||||
applyProperty(properties, PropertyKey::LineTranslateY, line.translate[1], z);
|
||||
applyProperty(properties, PropertyKey::LineTranslateAnchor, line.translateAnchor);
|
||||
applyProperty(properties, PropertyKey::LineWidth, line.width, z);
|
||||
applyProperty(properties, PropertyKey::LineOffset, line.offset, z);
|
||||
applyProperty(properties, PropertyKey::LineBlur, line.blur, z);
|
||||
applyProperty(properties, PropertyKey::LineDashLand, line.dash_array[0], z);
|
||||
applyProperty(properties, PropertyKey::LineDashGap, line.dash_array[1], z);
|
||||
applyProperty(properties, PropertyKey::LineImage, line.image);
|
||||
} else if (style.is<IconProperties>()) {
|
||||
IconProperties &icon = style.get<IconProperties>();
|
||||
applyProperty(properties, PropertyKey::IconEnabled, icon.enabled, z);
|
||||
applyProperty(properties, PropertyKey::IconOpacity, icon.opacity, z);
|
||||
applyProperty(properties, PropertyKey::IconRotate, icon.rotate, z);
|
||||
applyProperty(properties, PropertyKey::IconRotateAnchor, icon.rotate_anchor);
|
||||
} else if (style.is<TextProperties>()) {
|
||||
TextProperties &text = style.get<TextProperties>();
|
||||
applyProperty(properties, PropertyKey::TextEnabled, text.enabled, z);
|
||||
applyProperty(properties, PropertyKey::TextOpacity, text.opacity, z);
|
||||
applyProperty(properties, PropertyKey::TextSize, text.size, z);
|
||||
applyProperty(properties, PropertyKey::TextColor, text.color);
|
||||
applyProperty(properties, PropertyKey::TextHaloColor, text.halo_color);
|
||||
applyProperty(properties, PropertyKey::TextHaloWidth, text.halo_width, z);
|
||||
applyProperty(properties, PropertyKey::TextHaloBlur, text.halo_blur, z);
|
||||
} else if (style.is<CompositeProperties>()) {
|
||||
CompositeProperties &composite = style.get<CompositeProperties>();
|
||||
applyProperty(properties, PropertyKey::CompositeEnabled, composite.enabled, z);
|
||||
applyProperty(properties, PropertyKey::CompositeOpacity, composite.opacity, z);
|
||||
} else if (style.is<RasterProperties>()) {
|
||||
RasterProperties &raster = style.get<RasterProperties>();
|
||||
applyProperty(properties, PropertyKey::RasterEnabled, raster.enabled, z);
|
||||
applyProperty(properties, PropertyKey::RasterOpacity, raster.opacity, z);
|
||||
applyProperty(properties, PropertyKey::RasterSpin, raster.spin, z);
|
||||
applyProperty(properties, PropertyKey::RasterBrightnessLow, raster.brightness[0], z);
|
||||
applyProperty(properties, PropertyKey::RasterBrightnessHigh, raster.brightness[1], z);
|
||||
applyProperty(properties, PropertyKey::RasterSaturation, raster.saturation, z);
|
||||
applyProperty(properties, PropertyKey::RasterContrast, raster.contrast, z);
|
||||
applyProperty(properties, PropertyKey::RasterFade, raster.fade, z);
|
||||
} else if (style.is<BackgroundProperties>()) {
|
||||
BackgroundProperties &background = style.get<BackgroundProperties>();
|
||||
applyProperty(properties, PropertyKey::BackgroundColor, background.color);
|
||||
}
|
||||
}
|
||||
|
||||
void Style::updateProperties(const std::shared_ptr<StyleLayerGroup> &group, float z, timestamp t) {
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const std::shared_ptr<StyleLayer> &layer : group->layers) {
|
||||
if (!layer) continue;
|
||||
|
||||
if (layer->layers) {
|
||||
updateProperties(layer->layers, z, t);
|
||||
}
|
||||
|
||||
// Accomodate for different tile size.
|
||||
if (layer->bucket) {
|
||||
const StyleBucket &bucket = *layer->bucket;
|
||||
if (bucket.source) {
|
||||
const Source &source = *bucket.source;
|
||||
z += std::log(source.tile_size / 256.0f) / M_LN2;
|
||||
}
|
||||
}
|
||||
|
||||
layer->style = resetClassProperties(*layer);
|
||||
|
||||
// Apply default class (if exists)
|
||||
auto default_it = layer->styles.find(ClassID::Default);
|
||||
if (default_it != layer->styles.end()) {
|
||||
applyClassProperties(layer->style, default_it->second, z);
|
||||
}
|
||||
|
||||
// Apply overriding classes in order.
|
||||
for (const std::string &class_name : appliedClasses) {
|
||||
const ClassID class_id = ClassDictionary::Lookup(class_name);
|
||||
auto class_it = layer->styles.find(class_id);
|
||||
if (class_it != layer->styles.end()) {
|
||||
applyClassProperties(layer->style, class_it->second, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Style::updateProperties(float z, timestamp t) {
|
||||
uv::writelock lock(mtx);
|
||||
|
||||
updateSources();
|
||||
|
||||
updateProperties(layers, z, t);
|
||||
if (layers) {
|
||||
layers->updateProperties(z, t);
|
||||
}
|
||||
|
||||
// Apply transitions after the first time.
|
||||
if (!initial_render_complete) {
|
||||
@@ -231,6 +91,8 @@ void Style::loadJSON(const uint8_t *const data) {
|
||||
parser.parse(const_cast<const rapidjson::Document &>(doc));
|
||||
|
||||
layers = parser.getLayers();
|
||||
|
||||
updateClasses();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include <llmr/style/style_layer.hpp>
|
||||
#include <llmr/style/style_bucket.hpp>
|
||||
#include <llmr/map/source.hpp>
|
||||
#include <llmr/style/style_layer_group.hpp>
|
||||
#include <llmr/style/property_fallback.hpp>
|
||||
|
||||
@@ -94,4 +96,177 @@ void StyleLayer::applyClassProperties(const ClassID class_id,
|
||||
}
|
||||
}
|
||||
|
||||
//void applyStyleProperties(StyleProperties &style, const ClassProperties &properties, float z) {
|
||||
// if (style.is<FillProperties>()) {
|
||||
// FillProperties &fill = style.get<FillProperties>();
|
||||
// applyStyleProperty(properties, PropertyKey::FillEnabled, fill.enabled, z);
|
||||
// applyStyleProperty(properties, PropertyKey::FillAntialias, fill.antialias, z);
|
||||
// applyStyleProperty(properties, PropertyKey::FillOpacity, fill.opacity, z);
|
||||
// applyStyleProperty(properties, PropertyKey::FillColor, fill.fill_color);
|
||||
// applyStyleProperty(properties, PropertyKey::FillOutlineColor, fill.stroke_color);
|
||||
// applyStyleProperty(properties, PropertyKey::FillTranslateX, fill.translate[0], z);
|
||||
// applyStyleProperty(properties, PropertyKey::FillTranslateY, fill.translate[1], z);
|
||||
// applyStyleProperty(properties, PropertyKey::FillTranslateAnchor, fill.translateAnchor);
|
||||
// applyStyleProperty(properties, PropertyKey::FillImage, fill.image);
|
||||
// } else if (style.is<LineProperties>()) {
|
||||
// LineProperties &line = style.get<LineProperties>();
|
||||
// applyStyleProperty(properties, PropertyKey::LineEnabled, line.enabled, z);
|
||||
// applyStyleProperty(properties, PropertyKey::LineOpacity, line.opacity, z);
|
||||
// applyStyleProperty(properties, PropertyKey::LineColor, line.color);
|
||||
// applyStyleProperty(properties, PropertyKey::LineTranslateX, line.translate[0], z);
|
||||
// applyStyleProperty(properties, PropertyKey::LineTranslateY, line.translate[1], z);
|
||||
// applyStyleProperty(properties, PropertyKey::LineTranslateAnchor, line.translateAnchor);
|
||||
// applyStyleProperty(properties, PropertyKey::LineWidth, line.width, z);
|
||||
// applyStyleProperty(properties, PropertyKey::LineOffset, line.offset, z);
|
||||
// applyStyleProperty(properties, PropertyKey::LineBlur, line.blur, z);
|
||||
// applyStyleProperty(properties, PropertyKey::LineDashLand, line.dash_array[0], z);
|
||||
// applyStyleProperty(properties, PropertyKey::LineDashGap, line.dash_array[1], z);
|
||||
// applyStyleProperty(properties, PropertyKey::LineImage, line.image);
|
||||
// } else if (style.is<IconProperties>()) {
|
||||
// IconProperties &icon = style.get<IconProperties>();
|
||||
// applyStyleProperty(properties, PropertyKey::IconEnabled, icon.enabled, z);
|
||||
// applyStyleProperty(properties, PropertyKey::IconOpacity, icon.opacity, z);
|
||||
// applyStyleProperty(properties, PropertyKey::IconRotate, icon.rotate, z);
|
||||
// applyStyleProperty(properties, PropertyKey::IconRotateAnchor, icon.rotate_anchor);
|
||||
// } else if (style.is<TextProperties>()) {
|
||||
// TextProperties &text = style.get<TextProperties>();
|
||||
// applyStyleProperty(properties, PropertyKey::TextEnabled, text.enabled, z);
|
||||
// applyStyleProperty(properties, PropertyKey::TextOpacity, text.opacity, z);
|
||||
// applyStyleProperty(properties, PropertyKey::TextSize, text.size, z);
|
||||
// applyStyleProperty(properties, PropertyKey::TextColor, text.color);
|
||||
// applyStyleProperty(properties, PropertyKey::TextHaloColor, text.halo_color);
|
||||
// applyStyleProperty(properties, PropertyKey::TextHaloWidth, text.halo_width, z);
|
||||
// applyStyleProperty(properties, PropertyKey::TextHaloBlur, text.halo_blur, z);
|
||||
// } else if (style.is<CompositeProperties>()) {
|
||||
// CompositeProperties &composite = style.get<CompositeProperties>();
|
||||
// applyStyleProperty(properties, PropertyKey::CompositeEnabled, composite.enabled, z);
|
||||
// applyStyleProperty(properties, PropertyKey::CompositeOpacity, composite.opacity, z);
|
||||
// } else if (style.is<RasterProperties>()) {
|
||||
// RasterProperties &raster = style.get<RasterProperties>();
|
||||
// applyStyleProperty(properties, PropertyKey::RasterEnabled, raster.enabled, z);
|
||||
// applyStyleProperty(properties, PropertyKey::RasterOpacity, raster.opacity, z);
|
||||
// applyStyleProperty(properties, PropertyKey::RasterSpin, raster.spin, z);
|
||||
// applyStyleProperty(properties, PropertyKey::RasterBrightnessLow, raster.brightness[0], z);
|
||||
// applyStyleProperty(properties, PropertyKey::RasterBrightnessHigh, raster.brightness[1], z);
|
||||
// applyStyleProperty(properties, PropertyKey::RasterSaturation, raster.saturation, z);
|
||||
// applyStyleProperty(properties, PropertyKey::RasterContrast, raster.contrast, z);
|
||||
// applyStyleProperty(properties, PropertyKey::RasterFade, raster.fade, z);
|
||||
// } else if (style.is<BackgroundProperties>()) {
|
||||
// BackgroundProperties &background = style.get<BackgroundProperties>();
|
||||
// applyStyleProperty(properties, PropertyKey::BackgroundColor, background.color);
|
||||
// }
|
||||
//}
|
||||
//
|
||||
|
||||
template <typename T>
|
||||
struct PropertyEvaluator {
|
||||
typedef T result_type;
|
||||
PropertyEvaluator(float z) : z(z) {}
|
||||
|
||||
template <typename P, typename std::enable_if<std::is_convertible<P, T>::value, int>::type = 0>
|
||||
T operator()(const P &value) const {
|
||||
return value;
|
||||
}
|
||||
|
||||
T operator()(const Function<T> &value) const {
|
||||
return util::apply_visitor(FunctionEvaluator<T>(z), value);
|
||||
}
|
||||
|
||||
template <typename P, typename std::enable_if<!std::is_convertible<P, T>::value, int>::type = 0>
|
||||
T operator()(const P &) const {
|
||||
return T();
|
||||
}
|
||||
|
||||
private:
|
||||
const float z;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void StyleLayer::applyStyleProperty(PropertyKey key, T &target, const float z, const timestamp t) {
|
||||
auto it = appliedStyle.find(key);
|
||||
if (it != appliedStyle.end()) {
|
||||
AppliedClassProperties &applied = it->second;
|
||||
// Iterate through all properties that we need to apply in order.
|
||||
const PropertyEvaluator<T> evaluator(z);
|
||||
for (AppliedClassProperty &property : applied.properties) {
|
||||
if (property.end >= t) {
|
||||
// We overwrite the current property with the new value.
|
||||
target = util::apply_visitor(evaluator, property.value);
|
||||
} else if (property.begin <= t) {
|
||||
// We overwrite the current property partially with the new value.
|
||||
float progress = float(t - property.begin) / float(property.end - property.begin);
|
||||
target = util::apply_visitor(evaluator, property.value);
|
||||
// target = (1.0f - progress) * target + (progress) *
|
||||
// evaluateProperty(property.value, target, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void StyleLayer::applyStyleProperties(float, timestamp) {
|
||||
properties.set<std::false_type>();
|
||||
}
|
||||
|
||||
template <>
|
||||
void StyleLayer::applyStyleProperties<FillProperties>(const float z, const timestamp t) {
|
||||
properties.set<FillProperties>();
|
||||
FillProperties &fill = properties.get<FillProperties>();
|
||||
applyStyleProperty(PropertyKey::FillEnabled, fill.enabled, z, t);
|
||||
applyStyleProperty(PropertyKey::FillAntialias, fill.antialias, z, t);
|
||||
applyStyleProperty(PropertyKey::FillOpacity, fill.opacity, z, t);
|
||||
applyStyleProperty(PropertyKey::FillColor, fill.fill_color, z, t);
|
||||
applyStyleProperty(PropertyKey::FillOutlineColor, fill.stroke_color, z, t);
|
||||
applyStyleProperty(PropertyKey::FillTranslateX, fill.translate[0], z, t);
|
||||
applyStyleProperty(PropertyKey::FillTranslateY, fill.translate[1], z, t);
|
||||
applyStyleProperty(PropertyKey::FillTranslateAnchor, fill.translateAnchor, z, t);
|
||||
applyStyleProperty(PropertyKey::FillImage, fill.image, z, t);
|
||||
}
|
||||
|
||||
template <>
|
||||
void StyleLayer::applyStyleProperties<LineProperties>(const float z, const timestamp t) {
|
||||
properties.set<LineProperties>();
|
||||
LineProperties &line = properties.get<LineProperties>();
|
||||
applyStyleProperty(PropertyKey::LineEnabled, line.enabled, z, t);
|
||||
applyStyleProperty(PropertyKey::LineOpacity, line.opacity, z, t);
|
||||
applyStyleProperty(PropertyKey::LineColor, line.color, z, t);
|
||||
applyStyleProperty(PropertyKey::LineTranslateX, line.translate[0], z, t);
|
||||
applyStyleProperty(PropertyKey::LineTranslateY, line.translate[1], z, t);
|
||||
applyStyleProperty(PropertyKey::LineTranslateAnchor, line.translateAnchor, z, t);
|
||||
applyStyleProperty(PropertyKey::LineWidth, line.width, z, t);
|
||||
applyStyleProperty(PropertyKey::LineOffset, line.offset, z, t);
|
||||
applyStyleProperty(PropertyKey::LineBlur, line.blur, z, t);
|
||||
applyStyleProperty(PropertyKey::LineDashLand, line.dash_array[0], z, t);
|
||||
applyStyleProperty(PropertyKey::LineDashGap, line.dash_array[1], z, t);
|
||||
applyStyleProperty(PropertyKey::LineImage, line.image, z, t);
|
||||
}
|
||||
|
||||
void StyleLayer::updateProperties(float z, const timestamp t) {
|
||||
if (layers) {
|
||||
layers->updateProperties(z, t);
|
||||
}
|
||||
|
||||
// Accomodate for different tile size.
|
||||
if (bucket && bucket->source) {
|
||||
z += std::log(bucket->source->tile_size / 256.0f) / M_LN2;
|
||||
}
|
||||
|
||||
if (layers) {
|
||||
applyStyleProperties<CompositeProperties>(z, t);
|
||||
} else if (bucket) {
|
||||
switch (bucket->type) {
|
||||
case BucketType::Fill: applyStyleProperties<FillProperties>(z, t); break;
|
||||
case BucketType::Line: applyStyleProperties<LineProperties>(z, t); break;
|
||||
case BucketType::Icon: applyStyleProperties<IconProperties>(z, t); break;
|
||||
case BucketType::Text: applyStyleProperties<TextProperties>(z, t); break;
|
||||
case BucketType::Raster: applyStyleProperties<RasterProperties>(z, t); break;
|
||||
default: properties.set<std::false_type>(); break;
|
||||
}
|
||||
} else {
|
||||
return applyStyleProperties<BackgroundProperties>(z, t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -11,4 +11,12 @@ void StyleLayerGroup::setClasses(const std::vector<std::string> &class_names, ti
|
||||
}
|
||||
}
|
||||
|
||||
void StyleLayerGroup::updateProperties(float z, timestamp t) {
|
||||
for (const std::shared_ptr<StyleLayer> &layer: layers) {
|
||||
if (layer) {
|
||||
layer->updateProperties(z, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -172,21 +172,150 @@ void StyleParser::parseSources(JSVal value) {
|
||||
|
||||
#pragma mark - Parse Style Properties
|
||||
|
||||
template<> bool StyleParser::parseStyleProperty<bool>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
|
||||
if (!value.HasMember(property_name)) {
|
||||
return false;
|
||||
Color parseColor(JSVal value) {
|
||||
if (!value.IsString()) {
|
||||
fprintf(stderr, "[WARNING] color value must be a string\n");
|
||||
return Color{{ 0, 0, 0, 0 }};
|
||||
}
|
||||
|
||||
JSVal rvalue = replaceConstant(value[property_name]);
|
||||
if (!rvalue.IsBool()) {
|
||||
fprintf(stderr, "[WARNING] value of '%s' must be a boolean\n", property_name);
|
||||
return false;
|
||||
}
|
||||
CSSColorParser::Color css_color = CSSColorParser::parse({ value.GetString(), value.GetStringLength() });
|
||||
|
||||
klass.set(key, bool(value.GetBool()));
|
||||
return true;
|
||||
// Premultiply the color.
|
||||
const float factor = css_color.a / 255;
|
||||
|
||||
return Color{{(float)css_color.r * factor,
|
||||
(float)css_color.g * factor,
|
||||
(float)css_color.b * factor,
|
||||
css_color.a}};
|
||||
}
|
||||
|
||||
template <>
|
||||
bool StyleParser::parseFunctionArgument(JSVal value) {
|
||||
JSVal rvalue = replaceConstant(value);
|
||||
if (rvalue.IsBool()) {
|
||||
return rvalue.GetBool();
|
||||
} else if (rvalue.IsNumber()) {
|
||||
return rvalue.GetDouble();
|
||||
} else {
|
||||
fprintf(stderr, "[WARNING] function argument must be a boolean or numeric value");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
float StyleParser::parseFunctionArgument(JSVal value) {
|
||||
JSVal rvalue = replaceConstant(value);
|
||||
if (rvalue.IsNumber()) {
|
||||
return rvalue.GetDouble();
|
||||
} else {
|
||||
fprintf(stderr, "[WARNING] function argument must be a numeric value");
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
Color StyleParser::parseFunctionArgument(JSVal value) {
|
||||
JSVal rvalue = replaceConstant(value);
|
||||
return parseColor(rvalue);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool StyleParser::parseFunction(PropertyKey key, ClassProperties &klass, JSVal value) {
|
||||
if (!value.HasMember("fn")) {
|
||||
fprintf(stderr, "[WARNING] function must specify a function name\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
JSVal value_fn = value["fn"];
|
||||
if (!value_fn.IsString()) {
|
||||
fprintf(stderr, "[WARNING] function must specify a function type\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string type { value_fn.GetString(), value_fn.GetStringLength() };
|
||||
|
||||
if (type == "linear") {
|
||||
if (!value.HasMember("z")) {
|
||||
fprintf(stderr, "[WARNING] linear function must specify a base zoom level\n");
|
||||
return false;
|
||||
}
|
||||
if (!value.HasMember("val")) {
|
||||
fprintf(stderr, "[WARNING] linear function must specify a base value\n");
|
||||
return false;
|
||||
}
|
||||
const float z_base = parseFunctionArgument<float>(value["z"]);
|
||||
const T val = parseFunctionArgument<T>(value["val"]);
|
||||
const float slope = value.HasMember("slope") ? parseFunctionArgument<float>(value["slope"]) : 1;
|
||||
const T min = value.HasMember("min") ? parseFunctionArgument<T>(value["min"]) : T();
|
||||
const T max = value.HasMember("max") ? parseFunctionArgument<T>(value["max"]) : T();
|
||||
klass.set(key, LinearFunction<T>(val, z_base, slope, min, max));
|
||||
return true;
|
||||
}
|
||||
else if (type == "exponential") {
|
||||
if (!value.HasMember("z")) {
|
||||
fprintf(stderr, "[WARNING] exponential function must specify a base zoom level\n");
|
||||
return false;
|
||||
}
|
||||
if (!value.HasMember("val")) {
|
||||
fprintf(stderr, "[WARNING] exponential function must specify a base value\n");
|
||||
return false;
|
||||
}
|
||||
const float z_base = parseFunctionArgument<float>(value["z"]);
|
||||
const T val = parseFunctionArgument<T>(value["val"]);
|
||||
const float slope = value.HasMember("slope") ? parseFunctionArgument<float>(value["slope"]) : 1;
|
||||
const T min = value.HasMember("min") ? parseFunctionArgument<T>(value["min"]) : T();
|
||||
const T max = value.HasMember("max") ? parseFunctionArgument<T>(value["max"]) : T();
|
||||
klass.set(key, ExponentialFunction<T>(val, z_base, slope, min, max));
|
||||
return true;
|
||||
|
||||
}
|
||||
else if (type == "stops") {
|
||||
if (!value.HasMember("stops")) {
|
||||
fprintf(stderr, "[WARNING] stops function must specify a stops array\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
JSVal value_stops = value["stops"];
|
||||
if (!value_stops.IsArray()) {
|
||||
fprintf(stderr, "[WARNING] stops function must specify a stops array\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::pair<float, T>> stops;
|
||||
for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
|
||||
JSVal stop = value[i];
|
||||
if (stop.IsObject()) {
|
||||
if (!stop.HasMember("z")) {
|
||||
fprintf(stderr, "[WARNING] stop must have zoom level specification\n");
|
||||
return false;
|
||||
}
|
||||
if (!stop.HasMember("val")) {
|
||||
fprintf(stderr, "[WARNING] stop must have value specification\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
JSVal z = stop["z"];
|
||||
if (!z.IsNumber()) {
|
||||
fprintf(stderr, "[WARNING] zoom level in stop must be a number\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
stops.emplace_back(z.GetDouble(), parseFunctionArgument<T>(stop["val"]));
|
||||
} else {
|
||||
fprintf(stderr, "[WARNING] function argument must be a numeric value\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
klass.set(key, StopsFunction<T>(stops));
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "[WARNING] function type '%s' is unknown\n", type.c_str());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<> bool StyleParser::parseStyleProperty<std::string>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
|
||||
if (!value.HasMember(property_name)) {
|
||||
@@ -218,7 +347,6 @@ template<> bool StyleParser::parseStyleProperty<TranslateAnchorType>(const char
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<> bool StyleParser::parseStyleProperty<RotateAnchorType>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
|
||||
if (!value.HasMember(property_name)) {
|
||||
return false;
|
||||
@@ -234,29 +362,6 @@ template<> bool StyleParser::parseStyleProperty<RotateAnchorType>(const char *pr
|
||||
return true;
|
||||
}
|
||||
|
||||
template<> bool StyleParser::parseStyleProperty<Color>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
|
||||
if (!value.HasMember(property_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSVal rvalue = replaceConstant(value[property_name]);
|
||||
if (!rvalue.IsString()) {
|
||||
fprintf(stderr, "[WARNING] value of '%s' must be a string\n", property_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
CSSColorParser::Color css_color = CSSColorParser::parse({ rvalue.GetString(), rvalue.GetStringLength() });
|
||||
|
||||
// Premultiply the color.
|
||||
const float factor = css_color.a / 255;
|
||||
|
||||
klass.set(key, Color{{(float)css_color.r * factor,
|
||||
(float)css_color.g * factor,
|
||||
(float)css_color.b * factor,
|
||||
css_color.a}});
|
||||
return true;
|
||||
}
|
||||
|
||||
template <> bool StyleParser::parseStyleProperty<PropertyTransition>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
|
||||
if (!value.HasMember(property_name)) {
|
||||
return false;
|
||||
@@ -281,82 +386,64 @@ template <> bool StyleParser::parseStyleProperty<PropertyTransition>(const char
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
FunctionProperty::fn parseFunctionType(JSVal type) {
|
||||
if (type.IsString()) {
|
||||
std::string t { type.GetString(), type.GetStringLength() };
|
||||
if (t == "constant") {
|
||||
return &functions::constant;
|
||||
} else if (t == "linear") {
|
||||
return &functions::linear;
|
||||
} else if (t == "stops") {
|
||||
return &functions::stops;
|
||||
} else if (t == "exponential") {
|
||||
return &functions::exponential;
|
||||
} else if (t == "min") {
|
||||
return &functions::min;
|
||||
} else if (t == "max") {
|
||||
return &functions::max;
|
||||
} else {
|
||||
throw Style::exception("unknown function type");
|
||||
}
|
||||
} else {
|
||||
throw Style::exception("function type must be a string");
|
||||
}
|
||||
}
|
||||
|
||||
template<> bool StyleParser::parseStyleProperty<FunctionProperty>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
|
||||
template<> bool StyleParser::parseStyleProperty<Function<bool>>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
|
||||
if (!value.HasMember(property_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSVal rvalue = replaceConstant(value[property_name]);
|
||||
FunctionProperty property;
|
||||
|
||||
if (rvalue.IsArray()) {
|
||||
if (rvalue.Size() < 1) {
|
||||
fprintf(stderr, "[WARNING] function of '%s' does not have arguments\n", property_name);
|
||||
} else {
|
||||
JSVal rvalue = replaceConstant(value[property_name]);
|
||||
if (rvalue.IsObject()) {
|
||||
return parseFunction<bool>(key, klass, rvalue);
|
||||
} else if (rvalue.IsNumber()) {
|
||||
klass.set(key, bool(rvalue.GetDouble()));
|
||||
return true;
|
||||
} else if (rvalue.IsBool()) {
|
||||
klass.set(key, bool(rvalue.GetBool()));
|
||||
return true;
|
||||
} else {
|
||||
fprintf(stderr, "[WARNING] value of '%s' must be a boolean, or a boolean function\n", property_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
property.function = parseFunctionType(rvalue[(rapidjson::SizeType)0]);
|
||||
for (rapidjson::SizeType i = 1; i < rvalue.Size(); ++i) {
|
||||
JSVal stop = rvalue[i];
|
||||
if (stop.IsObject()) {
|
||||
if (!stop.HasMember("z")) {
|
||||
throw Style::exception("stop must have zoom level specification");
|
||||
}
|
||||
JSVal z = stop["z"];
|
||||
if (!z.IsNumber()) {
|
||||
throw Style::exception("zoom level in stops must be a number");
|
||||
}
|
||||
property.values.push_back(z.GetDouble());
|
||||
|
||||
if (!stop.HasMember("val")) {
|
||||
throw Style::exception("stop must have value specification");
|
||||
}
|
||||
JSVal val = stop["val"];
|
||||
if (!val.IsNumber()) {
|
||||
throw Style::exception("value in stops must be a number");
|
||||
}
|
||||
property.values.push_back(val.GetDouble());
|
||||
} else if (stop.IsNumber()) {
|
||||
property.values.push_back(stop.GetDouble());
|
||||
} else if (stop.IsBool()) {
|
||||
property.values.push_back(stop.GetBool());
|
||||
} else {
|
||||
throw Style::exception("function argument must be a numeric value");
|
||||
}
|
||||
}
|
||||
} else if (rvalue.IsNumber()) {
|
||||
property.function = &functions::constant;
|
||||
property.values.push_back(rvalue.GetDouble());
|
||||
}
|
||||
|
||||
klass.set(key, std::move(property));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<> bool StyleParser::parseStyleProperty<Function<float>>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
|
||||
if (!value.HasMember(property_name)) {
|
||||
return false;
|
||||
} else {
|
||||
JSVal rvalue = replaceConstant(value[property_name]);
|
||||
if (rvalue.IsObject()) {
|
||||
return parseFunction<float>(key, klass, rvalue);
|
||||
} else if (rvalue.IsNumber()) {
|
||||
klass.set(key, float(rvalue.GetDouble()));
|
||||
return true;
|
||||
} else if (rvalue.IsBool()) {
|
||||
klass.set(key, float(rvalue.GetBool()));
|
||||
return true;
|
||||
} else {
|
||||
fprintf(stderr, "[WARNING] value of '%s' must be a number, or a number function\n", property_name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<> bool StyleParser::parseStyleProperty<Function<Color>>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
|
||||
if (!value.HasMember(property_name)) {
|
||||
return false;
|
||||
} else {
|
||||
JSVal rvalue = replaceConstant(value[property_name]);
|
||||
if (rvalue.IsObject()) {
|
||||
return parseFunction<Color>(key, klass, rvalue);
|
||||
} else if (rvalue.IsString()) {
|
||||
klass.set(key, parseColor(rvalue));
|
||||
return true;
|
||||
} else {
|
||||
fprintf(stderr, "[WARNING] value of '%s' must be a color, or a color function\n", property_name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
bool StyleParser::parseStyleProperty(const char *property_name, const std::vector<PropertyKey> &keys, ClassProperties &klass, JSVal value) {
|
||||
if (value.HasMember(property_name)) {
|
||||
@@ -490,84 +577,84 @@ void StyleParser::parseStyles(JSVal value, std::map<ClassID, ClassProperties> &s
|
||||
void StyleParser::parseStyle(JSVal value, ClassProperties &klass) {
|
||||
using Key = PropertyKey;
|
||||
|
||||
parseStyleProperty<FunctionProperty>("fill-enabled", Key::FillEnabled, klass, value);
|
||||
parseStyleProperty<Function<bool>>("fill-enabled", Key::FillEnabled, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-fill-enabled", Key::FillEnabled, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("fill-antialias", Key::FillAntialias, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("fill-opacity", Key::FillOpacity, klass, value);
|
||||
parseStyleProperty<Function<bool>>("fill-antialias", Key::FillAntialias, klass, value);
|
||||
parseStyleProperty<Function<float>>("fill-opacity", Key::FillOpacity, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-fill-opacity", Key::FillOpacity, klass, value);
|
||||
parseStyleProperty<Color>("fill-color", Key::FillColor, klass, value);
|
||||
parseStyleProperty<Function<Color>>("fill-color", Key::FillColor, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-fill-color", Key::FillColor, klass, value);
|
||||
parseStyleProperty<Color>("fill-outline-color", Key::FillOutlineColor, klass, value);
|
||||
parseStyleProperty<Function<Color>>("fill-outline-color", Key::FillOutlineColor, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-fill-outline-color", Key::FillOutlineColor, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("fill-translate", { Key::FillTranslateX, Key::FillTranslateY }, klass, value);
|
||||
parseStyleProperty<Function<float>>("fill-translate", { Key::FillTranslateX, Key::FillTranslateY }, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-fill-translate", Key::FillTranslate, klass, value);
|
||||
parseStyleProperty<TranslateAnchorType>("fill-translate-anchor", Key::FillTranslateAnchor, klass, value);
|
||||
parseStyleProperty<std::string>("fill-image", Key::FillImage, klass, value);
|
||||
|
||||
parseStyleProperty<FunctionProperty>("line-enabled", Key::LineEnabled, klass, value);
|
||||
parseStyleProperty<Function<bool>>("line-enabled", Key::LineEnabled, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-line-enabled", Key::LineEnabled, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("line-opacity", Key::LineOpacity, klass, value);
|
||||
parseStyleProperty<Function<float>>("line-opacity", Key::LineOpacity, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-line-opacity", Key::LineOpacity, klass, value);
|
||||
parseStyleProperty<Color>("line-color", Key::LineColor, klass, value);
|
||||
parseStyleProperty<Function<Color>>("line-color", Key::LineColor, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-line-color", Key::LineColor, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("line-translate", { Key::LineTranslateX, Key::LineTranslateY }, klass, value);
|
||||
parseStyleProperty<Function<float>>("line-translate", { Key::LineTranslateX, Key::LineTranslateY }, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-line-translate", Key::LineTranslate, klass, value);
|
||||
parseStyleProperty<TranslateAnchorType>("line-translate-anchor", Key::LineTranslateAnchor, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("line-width", Key::LineWidth, klass, value);
|
||||
parseStyleProperty<Function<float>>("line-width", Key::LineWidth, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-line-width", Key::LineWidth, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("line-offset", Key::LineOffset, klass, value);
|
||||
parseStyleProperty<Function<float>>("line-offset", Key::LineOffset, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-line-offset", Key::LineOffset, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("line-blur", Key::LineBlur, klass, value);
|
||||
parseStyleProperty<Function<float>>("line-blur", Key::LineBlur, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-line-blur", Key::LineBlur, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("line-dasharray", { Key::LineDashLand, Key::LineDashGap }, klass, value);
|
||||
parseStyleProperty<Function<float>>("line-dasharray", { Key::LineDashLand, Key::LineDashGap }, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-line-dasharray", Key::LineDashArray, klass, value);
|
||||
parseStyleProperty<std::string>("line-image", Key::LineImage, klass, value);
|
||||
|
||||
parseStyleProperty<FunctionProperty>("icon-enabled", Key::IconEnabled, klass, value);
|
||||
parseStyleProperty<Function<bool>>("icon-enabled", Key::IconEnabled, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-icon-enabled", Key::IconEnabled, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("icon-opacity", Key::IconOpacity, klass, value);
|
||||
parseStyleProperty<Function<float>>("icon-opacity", Key::IconOpacity, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-icon-opacity", Key::IconOpacity, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("icon-rotate", Key::IconRotate, klass, value);
|
||||
parseStyleProperty<Function<float>>("icon-rotate", Key::IconRotate, klass, value);
|
||||
parseStyleProperty<RotateAnchorType>("icon-rotate-anchor", Key::IconRotateAnchor, klass, value);
|
||||
|
||||
parseStyleProperty<FunctionProperty>("text-enabled", Key::TextEnabled, klass, value);
|
||||
parseStyleProperty<Function<bool>>("text-enabled", Key::TextEnabled, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-text-enabled", Key::TextEnabled, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("text-opacity", Key::TextOpacity, klass, value);
|
||||
parseStyleProperty<Function<float>>("text-opacity", Key::TextOpacity, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-text-opacity", Key::TextOpacity, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("text-size", Key::TextSize, klass, value);
|
||||
parseStyleProperty<Function<float>>("text-size", Key::TextSize, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-text-size", Key::TextSize, klass, value);
|
||||
parseStyleProperty<Color>("text-color", Key::TextColor, klass, value);
|
||||
parseStyleProperty<Function<Color>>("text-color", Key::TextColor, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-text-color", Key::TextColor, klass, value);
|
||||
parseStyleProperty<Color>("text-halo-color", Key::TextHaloColor, klass, value);
|
||||
parseStyleProperty<Function<Color>>("text-halo-color", Key::TextHaloColor, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-text-halo-color", Key::TextHaloColor, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("text-halo-width", Key::TextHaloWidth, klass, value);
|
||||
parseStyleProperty<Function<float>>("text-halo-width", Key::TextHaloWidth, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-text-halo-width", Key::TextHaloWidth, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("text-halo-blur", Key::TextHaloBlur, klass, value);
|
||||
parseStyleProperty<Function<float>>("text-halo-blur", Key::TextHaloBlur, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-text-halo-blur", Key::TextHaloBlur, klass, value);
|
||||
|
||||
parseStyleProperty<FunctionProperty>("composite-enabled", Key::CompositeEnabled, klass, value);
|
||||
parseStyleProperty<Function<bool>>("composite-enabled", Key::CompositeEnabled, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-composite-enabled", Key::CompositeEnabled, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("composite-opacity", Key::CompositeOpacity, klass, value);
|
||||
parseStyleProperty<Function<float>>("composite-opacity", Key::CompositeOpacity, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-composite-opacity", Key::CompositeOpacity, klass, value);
|
||||
|
||||
parseStyleProperty<FunctionProperty>("raster-enabled", Key::RasterEnabled, klass, value);
|
||||
parseStyleProperty<Function<bool>>("raster-enabled", Key::RasterEnabled, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-raster-enabled", Key::RasterEnabled, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("raster-opacity", Key::RasterOpacity, klass, value);
|
||||
parseStyleProperty<Function<float>>("raster-opacity", Key::RasterOpacity, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-raster-opacity", Key::RasterOpacity, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("raster-spin", Key::RasterSpin, klass, value);
|
||||
parseStyleProperty<Function<float>>("raster-spin", Key::RasterSpin, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-raster-spin", Key::RasterSpin, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("raster-brightness-low", Key::RasterBrightnessLow, klass, value);
|
||||
parseStyleProperty<Function<float>>("raster-brightness-low", Key::RasterBrightnessLow, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-raster-brightness-low", Key::RasterBrightnessLow, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("raster-brightness-high", Key::RasterBrightnessHigh, klass, value);
|
||||
parseStyleProperty<Function<float>>("raster-brightness-high", Key::RasterBrightnessHigh, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-raster-brightness-high", Key::RasterBrightnessHigh, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("raster-saturation", Key::RasterSaturation, klass, value);
|
||||
parseStyleProperty<Function<float>>("raster-saturation", Key::RasterSaturation, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-raster-saturation", Key::RasterSaturation, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("raster-contrast", Key::RasterContrast, klass, value);
|
||||
parseStyleProperty<Function<float>>("raster-contrast", Key::RasterContrast, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-raster-contrast", Key::RasterContrast, klass, value);
|
||||
parseStyleProperty<FunctionProperty>("raster-fade", Key::RasterFade, klass, value);
|
||||
parseStyleProperty<Function<float>>("raster-fade", Key::RasterFade, klass, value);
|
||||
parseStyleProperty<PropertyTransition>("transition-raster-fade", Key::RasterFade, klass, value);
|
||||
|
||||
parseStyleProperty<Color>("background-color", Key::BackgroundColor, klass, value);
|
||||
parseStyleProperty<Function<Color>>("background-color", Key::BackgroundColor, klass, value);
|
||||
}
|
||||
|
||||
std::unique_ptr<const RasterizeProperties> StyleParser::parseRasterize(JSVal value) {
|
||||
|
||||
Reference in New Issue
Block a user