diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/style/function_properties.cpp | 153 | ||||
-rw-r--r-- | src/style/property_fallback.cpp | 2 | ||||
-rw-r--r-- | src/style/style.cpp | 148 | ||||
-rw-r--r-- | src/style/style_layer.cpp | 175 | ||||
-rw-r--r-- | src/style/style_layer_group.cpp | 8 | ||||
-rw-r--r-- | src/style/style_parser.cpp | 341 |
6 files changed, 502 insertions, 325 deletions
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) { |