summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2014-06-30 16:52:39 +0200
committerKonstantin Käfer <mail@kkaefer.com>2014-06-30 16:52:39 +0200
commit18d898fae70681fba8cc31d73b581c360557746c (patch)
tree813ad2d0696184bef3fafefdc30318a09fa76fbc
parent93cd4c5c7b0939038eab38a6f6eb71eae4014acb (diff)
downloadqtlocation-mapboxgl-18d898fae70681fba8cc31d73b581c360557746c.tar.gz
refactor functions
-rw-r--r--bin/style.js42
-rw-r--r--include/llmr/style/function_properties.hpp77
-rw-r--r--include/llmr/style/property_value.hpp11
-rw-r--r--include/llmr/style/rasterize_properties.hpp8
-rw-r--r--include/llmr/style/style.hpp7
-rw-r--r--include/llmr/style/style_layer.hpp13
-rw-r--r--include/llmr/style/style_layer_group.hpp1
-rw-r--r--include/llmr/style/style_parser.hpp5
-rw-r--r--src/style/function_properties.cpp153
-rw-r--r--src/style/property_fallback.cpp2
-rw-r--r--src/style/style.cpp148
-rw-r--r--src/style/style_layer.cpp175
-rw-r--r--src/style/style_layer_group.cpp8
-rw-r--r--src/style/style_parser.cpp341
14 files changed, 605 insertions, 386 deletions
diff --git a/bin/style.js b/bin/style.js
index 7ca40b6123..691c913510 100644
--- a/bin/style.js
+++ b/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",
diff --git a/include/llmr/style/function_properties.hpp b/include/llmr/style/function_properties.hpp
index 970d617d02..d1e1a5b1c6 100644
--- a/include/llmr/style/function_properties.hpp
+++ b/include/llmr/style/function_properties.hpp
@@ -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>
+>;
- inline FunctionProperty(float value) : function(&functions::constant), values(1, value) {}
- template <typename T> inline T evaluate(float z) const { return function(z, values); }
+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);
+ }
+private:
+ float z;
};
}
diff --git a/include/llmr/style/property_value.hpp b/include/llmr/style/property_value.hpp
index 9772258da8..f648e9bef6 100644
--- a/include/llmr/style/property_value.hpp
+++ b/include/llmr/style/property_value.hpp
@@ -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;
}
diff --git a/include/llmr/style/rasterize_properties.hpp b/include/llmr/style/rasterize_properties.hpp
index f085a7bdd9..2e1fc32895 100644
--- a/include/llmr/style/rasterize_properties.hpp
+++ b/include/llmr/style/rasterize_properties.hpp
@@ -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;
};
}
diff --git a/include/llmr/style/style.hpp b/include/llmr/style/style.hpp
index 62864b75ff..6b9b4549cc 100644
--- a/include/llmr/style/style.hpp
+++ b/include/llmr/style/style.hpp
@@ -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;
diff --git a/include/llmr/style/style_layer.hpp b/include/llmr/style/style_layer.hpp
index 89d4e14764..b04970f7c1 100644
--- a/include/llmr/style/style_layer.hpp
+++ b/include/llmr/style/style_layer.hpp
@@ -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
diff --git a/include/llmr/style/style_layer_group.hpp b/include/llmr/style/style_layer_group.hpp
index 73214830fe..2122c16286 100644
--- a/include/llmr/style/style_layer_group.hpp
+++ b/include/llmr/style/style_layer_group.hpp
@@ -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;
diff --git a/include/llmr/style/style_parser.hpp b/include/llmr/style/style_parser.hpp
index 3c2a12a2a3..abf047fc17 100644
--- a/include/llmr/style/style_parser.hpp
+++ b/include/llmr/style/style_parser.hpp
@@ -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);
diff --git a/src/style/function_properties.cpp b/src/style/function_properties.cpp
index eaaa2ad6fe..98f6503be3 100644
--- a/src/style/function_properties.cpp
+++ b/src/style/function_properties.cpp
@@ -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;
+}
+
+template <>
+float LinearFunction<float>::evaluate(float z) const {
+ return std::fmin(std::fmax(min, value + (z - z_base) * slope), max);
}
-float functions::constant(float, const std::vector<float>& values) {
- return values.size() >= 1 ? values.front() : 0.0f;
+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])
+ }};
+}
+
+
+template <>
+bool ExponentialFunction<bool>::evaluate(float z) const {
+ return z < z_base ? slope >= 0 : z > z_base ? slope >= 0 : value;
+}
+
+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);
}
-float functions::min(float z, const std::vector<float>& values) {
- return values.size() >= 1 && z >= values.front();
+template <>
+inline bool exponentialInterpolate(const bool smaller, const bool larger, const float factor) {
+ return exponentialInterpolate(float(smaller), float(larger), factor);
}
-float functions::max(float z, const std::vector<float>& values) {
- return values.size() >= 1 && z <= values.front();
+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)
+ }};
}
-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 <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;
-
- 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; }
+ float larger_z = 0.0f;
+ T larger_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;
}
diff --git a/src/style/property_fallback.cpp b/src/style/property_fallback.cpp
index d94f7e9e54..b57a6a216b 100644
--- a/src/style/property_fallback.cpp
+++ b/src/style/property_fallback.cpp
@@ -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();
}
diff --git a/src/style/style.cpp b/src/style/style.cpp
index c2794fed88..a08e77ac01 100644
--- a/src/style/style.cpp
+++ b/src/style/style.cpp
@@ -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();
}
}
diff --git a/src/style/style_layer.cpp b/src/style/style_layer.cpp
index 3bfd235796..19497aa9e9 100644
--- a/src/style/style_layer.cpp
+++ b/src/style/style_layer.cpp
@@ -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);
+ }
+}
+
+
}
diff --git a/src/style/style_layer_group.cpp b/src/style/style_layer_group.cpp
index 2f0fdf39e0..bb19df45be 100644
--- a/src/style/style_layer_group.cpp
+++ b/src/style/style_layer_group.cpp
@@ -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);
+ }
+ }
+}
+
}
diff --git a/src/style/style_parser.cpp b/src/style/style_parser.cpp
index 7fa23cc914..c2f1ab5ec3 100644
--- a/src/style/style_parser.cpp
+++ b/src/style/style_parser.cpp
@@ -172,38 +172,152 @@ 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);
+ CSSColorParser::Color css_color = CSSColorParser::parse({ value.GetString(), value.GetStringLength() });
+
+ // 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;
}
+}
- klass.set(key, bool(value.GetBool()));
- return true;
+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<> bool StyleParser::parseStyleProperty<std::string>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
- if (!value.HasMember(property_name)) {
+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 rvalue = replaceConstant(value[property_name]);
- if (!rvalue.IsString()) {
- fprintf(stderr, "[WARNING] value of '%s' must be a string\n", property_name);
+ JSVal value_fn = value["fn"];
+ if (!value_fn.IsString()) {
+ fprintf(stderr, "[WARNING] function must specify a function type\n");
return false;
}
- klass.set(key, std::string { rvalue.GetString(), rvalue.GetStringLength() });
- return true;
+ 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<TranslateAnchorType>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
+template<> bool StyleParser::parseStyleProperty<std::string>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
if (!value.HasMember(property_name)) {
return false;
}
@@ -214,12 +328,11 @@ template<> bool StyleParser::parseStyleProperty<TranslateAnchorType>(const char
return false;
}
- klass.set(key, parseTranslateAnchorType({ rvalue.GetString(), rvalue.GetStringLength() }));
+ klass.set(key, std::string { rvalue.GetString(), rvalue.GetStringLength() });
return true;
}
-
-template<> bool StyleParser::parseStyleProperty<RotateAnchorType>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
+template<> bool StyleParser::parseStyleProperty<TranslateAnchorType>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
if (!value.HasMember(property_name)) {
return false;
}
@@ -230,11 +343,11 @@ template<> bool StyleParser::parseStyleProperty<RotateAnchorType>(const char *pr
return false;
}
- klass.set(key, parseRotateAnchorType({ rvalue.GetString(), rvalue.GetStringLength() }));
+ klass.set(key, parseTranslateAnchorType({ rvalue.GetString(), rvalue.GetStringLength() }));
return true;
}
-template<> bool StyleParser::parseStyleProperty<Color>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
+template<> bool StyleParser::parseStyleProperty<RotateAnchorType>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
if (!value.HasMember(property_name)) {
return false;
}
@@ -245,15 +358,7 @@ template<> bool StyleParser::parseStyleProperty<Color>(const char *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}});
+ klass.set(key, parseRotateAnchorType({ rvalue.GetString(), rvalue.GetStringLength() }));
return true;
}
@@ -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;
+template<> bool StyleParser::parseStyleProperty<Function<bool>>(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<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 {
- throw Style::exception("unknown function type");
+ fprintf(stderr, "[WARNING] value of '%s' must be a boolean, or a boolean function\n", property_name);
+ return false;
}
- } 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<float>>(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<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;
}
+ }
+}
- 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");
- }
+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;
}
- } else if (rvalue.IsNumber()) {
- property.function = &functions::constant;
- property.values.push_back(rvalue.GetDouble());
}
-
- klass.set(key, std::move(property));
- return true;
}
+
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) {