diff options
-rw-r--r-- | include/mbgl/style/expression/coalesce.hpp | 8 | ||||
-rw-r--r-- | include/mbgl/style/expression/curve.hpp | 8 | ||||
-rw-r--r-- | include/mbgl/style/function/composite_function.hpp | 133 | ||||
-rw-r--r-- | include/mbgl/style/function/convert.hpp | 115 | ||||
-rw-r--r-- | include/mbgl/util/interpolate.hpp | 27 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/mbgl/renderer/paint_property_binder.hpp | 11 | ||||
-rw-r--r-- | src/mbgl/style/expression/value.cpp | 2 |
8 files changed, 176 insertions, 129 deletions
diff --git a/include/mbgl/style/expression/coalesce.hpp b/include/mbgl/style/expression/coalesce.hpp index ab64f2e8bc..57948459e5 100644 --- a/include/mbgl/style/expression/coalesce.hpp +++ b/include/mbgl/style/expression/coalesce.hpp @@ -44,6 +44,14 @@ public: return true; } + std::size_t getLength() const { + return args.size(); + } + + Expression* getChild(std::size_t i) const { + return args.at(i).get(); + } + template <typename V> static ParseResult parse(const V& value, ParsingContext ctx) { using namespace mbgl::style::conversion; diff --git a/include/mbgl/style/expression/curve.hpp b/include/mbgl/style/expression/curve.hpp index 2a4a3574dd..5a9998e902 100644 --- a/include/mbgl/style/expression/curve.hpp +++ b/include/mbgl/style/expression/curve.hpp @@ -139,6 +139,10 @@ public: return false; } + Interpolator getInterpolator() const { + return interpolator; + } + private: Interpolator interpolator; std::unique_ptr<Expression> input; @@ -274,9 +278,9 @@ struct ParseCurve { }, [&](const type::Array& arrayType) -> ParseResult { if (arrayType.itemType == type::Number && arrayType.N) { - return ParseResult(std::make_unique<Curve<ExponentialInterpolator<std::vector<float>>>>( + return ParseResult(std::make_unique<Curve<ExponentialInterpolator<std::vector<Value>>>>( *outputType, - ExponentialInterpolator<std::vector<float>>(base), + ExponentialInterpolator<std::vector<Value>>(base), std::move(*input), std::move(stops) )); diff --git a/include/mbgl/style/function/composite_function.hpp b/include/mbgl/style/function/composite_function.hpp index 7b524b6021..5a88cc0b4d 100644 --- a/include/mbgl/style/function/composite_function.hpp +++ b/include/mbgl/style/function/composite_function.hpp @@ -1,5 +1,8 @@ #pragma once +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/expression/coalesce.hpp> +#include <mbgl/style/expression/curve.hpp> #include <mbgl/style/function/composite_exponential_stops.hpp> #include <mbgl/style/function/composite_interval_stops.hpp> #include <mbgl/style/function/composite_categorical_stops.hpp> @@ -43,87 +46,43 @@ public: CompositeIntervalStops<T>, CompositeCategoricalStops<T>>>; - CompositeFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {}) - : property(std::move(property_)), - stops(std::move(stops_)), - defaultValue(std::move(defaultValue_)) { - } - - struct CoveringRanges { - float zoom; - Range<float> coveringZoomRange; - Range<InnerStops> coveringStopsRange; - }; - - // Return the relevant stop zoom values and inner stops that bracket a given zoom level. This - // is the first step toward evaluating the function, and is used for in the course of both partial - // evaluation of data-driven paint properties, and full evaluation of data-driven layout properties. - CoveringRanges coveringRanges(float zoom) const { - return stops.match( - [&] (const auto& s) { - assert(!s.stops.empty()); - auto minIt = s.stops.lower_bound(zoom); - auto maxIt = s.stops.upper_bound(zoom); - - // lower_bound yields first element >= zoom, but we want the *last* - // element <= zoom, so if we found a stop > zoom, back up by one. - if (minIt != s.stops.begin() && minIt != s.stops.end() && minIt->first > zoom) { - minIt--; - } - - return CoveringRanges { - zoom, - Range<float> { - minIt == s.stops.end() ? s.stops.rbegin()->first : minIt->first, - maxIt == s.stops.end() ? s.stops.rbegin()->first : maxIt->first - }, - Range<InnerStops> { - s.innerStops(minIt == s.stops.end() ? s.stops.rbegin()->second : minIt->second), - s.innerStops(maxIt == s.stops.end() ? s.stops.rbegin()->second : maxIt->second) - } - }; - } - ); - } - - // Given a range of zoom values (typically two adjacent integer zoom levels, e.g. 5.0 and 6.0), - // return the covering ranges for both. This is used in the course of partial evaluation for - // data-driven paint properties. - Range<CoveringRanges> rangeOfCoveringRanges(Range<float> zoomRange) { - return Range<CoveringRanges> { - coveringRanges(zoomRange.min), - coveringRanges(zoomRange.max) - }; - } + using Interpolator = expression::ExponentialInterpolator<T>; + using Curve = expression::Curve<Interpolator>; - // Given the covering ranges for range of zoom values (typically two adjacent integer zoom levels, - // e.g. 5.0 and 6.0), and a feature, return the results of fully evaluating the function for that - // feature at each of the two zoom levels. These two results are what go into the paint vertex buffers - // for vertices associated with this feature. The shader will interpolate between them at render time. + CompositeFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {}) + : property(std::move(property_)), + stops(std::move(stops_)), + defaultValue(std::move(defaultValue_)), + expression(stops.match([&] (const auto& s) { + return expression::Convert::toExpression(property, s, defaultValue); + })), + interpolator([&]() -> Interpolator { + optional<Curve*> zoomCurve = findZoomCurve(expression.get()); + assert(zoomCurve); + return (*zoomCurve)->getInterpolator(); + }()) + {} + + // Return the range obtained by evaluating the function at each of the zoom levels in zoomRange template <class Feature> - Range<T> evaluate(const Range<CoveringRanges>& ranges, const Feature& feature, T finalDefaultValue) { - optional<Value> value = feature.getValue(property); - if (!value) { - return Range<T> { - defaultValue.value_or(finalDefaultValue), - defaultValue.value_or(finalDefaultValue) - }; - } + Range<T> evaluate(const Range<float>& zoomRange, const Feature& feature, T finalDefaultValue) { return Range<T> { - evaluateFinal(ranges.min, *value, finalDefaultValue), - evaluateFinal(ranges.max, *value, finalDefaultValue) + evaluate(zoomRange.min, feature, finalDefaultValue), + evaluate(zoomRange.max, feature, finalDefaultValue) }; } - // Fully evaluate the function for a zoom value and feature. This is used when evaluating data-driven - // layout properties. template <class Feature> T evaluate(float zoom, const Feature& feature, T finalDefaultValue) const { - optional<Value> value = feature.getValue(property); - if (!value) { - return defaultValue.value_or(finalDefaultValue); + auto result = expression->evaluate<T>(expression::EvaluationParameters { {zoom}, &feature }); + if (!result) { + return finalDefaultValue; } - return evaluateFinal(coveringRanges(zoom), *value, finalDefaultValue); + return *result; + } + + Interpolator getInterpolator() const { + return interpolator; } friend bool operator==(const CompositeFunction& lhs, @@ -138,15 +97,29 @@ public: bool useIntegerZoom = false; private: - T evaluateFinal(const CoveringRanges& ranges, const Value& value, T finalDefaultValue) const { - auto eval = [&] (const auto& s) { - return s.evaluate(value).value_or(defaultValue.value_or(finalDefaultValue)); - }; - return util::interpolate( - ranges.coveringStopsRange.min.match(eval), - ranges.coveringStopsRange.max.match(eval), - util::interpolationFactor(1.0f, ranges.coveringZoomRange, ranges.zoom)); + static optional<Curve*> findZoomCurve(expression::Expression* e) { + if (auto curve = dynamic_cast<Curve*>(e)) { + assert(curve->isZoomCurve()); + return {curve}; +// } else if (auto let = dynamic_cast<expression::Let*>(e)) { +// return let->getUnsafeResultExpressionPointer(); + } else if (auto coalesce = dynamic_cast<expression::Coalesce*>(e)) { + std::size_t length = coalesce->getLength(); + for (std::size_t i = 0; i < length; i++) { + optional<Curve*> childCurve = findZoomCurve(coalesce->getChild(i)); + if (!childCurve) { + continue; + } else { + return childCurve; + } + } + } + + return optional<Curve*>(); } + + std::shared_ptr<expression::Expression> expression; + const Interpolator interpolator; }; } // namespace style diff --git a/include/mbgl/style/function/convert.hpp b/include/mbgl/style/function/convert.hpp index 4e57c5eb29..611bf66e09 100644 --- a/include/mbgl/style/function/convert.hpp +++ b/include/mbgl/style/function/convert.hpp @@ -14,6 +14,9 @@ #include <mbgl/style/function/exponential_stops.hpp> #include <mbgl/style/function/interval_stops.hpp> #include <mbgl/style/function/categorical_stops.hpp> +#include <mbgl/style/function/composite_exponential_stops.hpp> +#include <mbgl/style/function/composite_interval_stops.hpp> +#include <mbgl/style/function/composite_categorical_stops.hpp> #include <mbgl/style/function/identity_stops.hpp> #include <string> @@ -101,40 +104,12 @@ struct Convert { float base, optional<T> defaultValue) { - ParseResult curve = valueTypeToExpressionType<T>().match( - [&](const type::NumberType& t) -> ParseResult { - return ParseResult(std::make_unique<Curve<ExponentialInterpolator<float>>>( - t, - ExponentialInterpolator<float>(base), - std::move(input), - std::move(convertedStops) - )); - }, - [&](const type::ColorType& t) -> ParseResult { - return ParseResult(std::make_unique<Curve<ExponentialInterpolator<mbgl::Color>>>( - t, - ExponentialInterpolator<mbgl::Color>(base), - std::move(input), - std::move(convertedStops) - )); - }, - [&](const type::Array& arrayType) -> ParseResult { - if (arrayType.itemType == type::Number && arrayType.N) { - return ParseResult(std::make_unique<Curve<ExponentialInterpolator<std::vector<Value>>>>( - arrayType, - ExponentialInterpolator<std::vector<Value>>(base), - std::move(input), - std::move(convertedStops) - )); - } else { - return ParseResult(); - } - }, - [&](const auto&) -> ParseResult { - return ParseResult(); - } - ); - + ParseResult curve = ParseResult(std::make_unique<Curve<ExponentialInterpolator<T>>>( + valueTypeToExpressionType<T>(), + ExponentialInterpolator<T>(base), + std::move(input), + std::move(convertedStops) + )); assert(curve); return makeCoalesceToDefault(std::move(*curve), defaultValue); } @@ -270,6 +245,78 @@ struct Convert { assert(e); return std::move(*e); } + + template <typename T> + static std::unique_ptr<Expression> toExpression(const std::string& property, + const CompositeExponentialStops<T>& stops, + optional<T> defaultValue) + { + std::vector<ParsingError> errors; + std::map<float, std::unique_ptr<Expression>> outerStops; + for (const std::pair<float, std::map<float, T>>& stop : stops.stops) { + std::unique_ptr<Expression> get = makeGet("number", property, ParsingContext(errors)); + ParseResult innerCurve = makeExponentialCurve(std::move(get), + convertStops(stop.second), + stops.base, + defaultValue); + assert(innerCurve); + outerStops.emplace(stop.first, std::move(*innerCurve)); + } + ParseResult outerCurve = makeExponentialCurve(makeZoom(ParsingContext(errors)), + std::move(outerStops), + 1.0f, + defaultValue); + assert(outerCurve); + ParseResult e = makeCoalesceToDefault(std::move(*outerCurve), defaultValue); + assert(e); + return std::move(*e); + } + + template <typename T> + static std::unique_ptr<Expression> toExpression(const std::string& property, + const CompositeIntervalStops<T>& stops, + optional<T> defaultValue) + { + std::vector<ParsingError> errors; + std::map<float, std::unique_ptr<Expression>> outerStops; + for (const std::pair<float, std::map<float, T>>& stop : stops.stops) { + std::unique_ptr<Expression> get = makeGet("number", property, ParsingContext(errors)); + ParseResult innerCurve = makeStepCurve(std::move(get), stop.second, defaultValue); + assert(innerCurve); + outerStops.emplace(stop.first, std::move(*innerCurve)); + } + ParseResult outerCurve = makeExponentialCurve(makeZoom(ParsingContext(errors)), + std::move(outerStops), + 1.0f, + defaultValue); + assert(outerCurve); + ParseResult e = makeCoalesceToDefault(std::move(*outerCurve), defaultValue); + assert(e); + return std::move(*e); + } + + template <typename T> + static std::unique_ptr<Expression> toExpression(const std::string& property, + const CompositeCategoricalStops<T>& stops, + optional<T> defaultValue) + { + std::vector<ParsingError> errors; + std::map<float, std::unique_ptr<Expression>> outerStops; + for (const std::pair<float, std::map<CategoricalValue, T>>& stop : stops.stops) { + ParseResult innerCurve = convertCategoricalStops(stop.second, property); + assert(innerCurve); + outerStops.emplace(stop.first, std::move(*innerCurve)); + } + ParseResult outerCurve = makeExponentialCurve(makeZoom(ParsingContext(errors)), + std::move(outerStops), + 1.0f, + defaultValue); + assert(outerCurve); + ParseResult e = makeCoalesceToDefault(std::move(*outerCurve), defaultValue); + assert(e); + return std::move(*e); + } + template <typename T> static std::unique_ptr<Expression> toExpression(const std::string& property, diff --git a/include/mbgl/util/interpolate.hpp b/include/mbgl/util/interpolate.hpp index 4853385ad5..c5e04c459f 100644 --- a/include/mbgl/util/interpolate.hpp +++ b/include/mbgl/util/interpolate.hpp @@ -3,6 +3,7 @@ #include <mbgl/util/color.hpp> #include <mbgl/util/range.hpp> #include <mbgl/style/position.hpp> +#include <mbgl/style/expression/value.hpp> #include <array> #include <vector> @@ -47,17 +48,31 @@ public: } }; -// Only safe if vectors are guaranteed at runtime to be the same length. + +// In order to accept Array<Number, N> as an output value for Curve +// expressions, we need to have an interpolatable std::vector type. +// However, style properties like line-dasharray are represented using +// std::vector<float>, and should NOT be considered interpolatable. +// So, we use std::vector<Value> to represent expression array values, +// asserting that (a) the vectors are the same size, and (b) they contain +// only numeric values. (These invariants should be relatively safe, +// being enforced by the expression type system.) template<> -struct Interpolator<std::vector<float>> { - std::vector<float> operator()(const std::vector<float>& a, - const std::vector<float>& b, +struct Interpolator<std::vector<style::expression::Value>> { + std::vector<style::expression::Value> operator()(const std::vector<style::expression::Value>& a, + const std::vector<style::expression::Value>& b, const double t) const { assert(a.size() == b.size()); if (a.size() == 0) return {}; - std::vector<float> result; + std::vector<style::expression::Value> result; for (std::size_t i = 0; i < a.size(); i++) { - result.push_back(interpolate(a[i], b[i], t)); + assert(a[i].template is<float>()); + assert(b[i].template is<float>()); + style::expression::Value item = interpolate( + a[i].template get<float>(), + b[i].template get<float>(), + t); + result.push_back(item); } return result; } diff --git a/package.json b/package.json index 7a9e5591b0..864275a651 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "ejs": "^2.4.1", "express": "^4.11.1", "flow-remove-types": "^1.2.1", + "json-stringify-pretty-compact": "^1.0.4", "lodash": "^4.16.4", "mapbox-gl-styles": "2.0.2", "pixelmatch": "^4.0.2", diff --git a/src/mbgl/renderer/paint_property_binder.hpp b/src/mbgl/renderer/paint_property_binder.hpp index 652948c8df..1ee74083ce 100644 --- a/src/mbgl/renderer/paint_property_binder.hpp +++ b/src/mbgl/renderer/paint_property_binder.hpp @@ -190,11 +190,11 @@ public: CompositeFunctionPaintPropertyBinder(style::CompositeFunction<T> function_, float zoom, T defaultValue_) : function(std::move(function_)), defaultValue(std::move(defaultValue_)), - rangeOfCoveringRanges(function.rangeOfCoveringRanges({zoom, zoom + 1})) { + zoomRange({zoom, zoom + 1}) { } void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) override { - Range<T> range = function.evaluate(rangeOfCoveringRanges, feature, defaultValue); + Range<T> range = function.evaluate(zoomRange, feature, defaultValue); this->statistics.add(range.min); this->statistics.add(range.max); AttributeValue value = zoomInterpolatedAttributeValue( @@ -219,9 +219,9 @@ public: float interpolationFactor(float currentZoom) const override { if (function.useIntegerZoom) { - return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, std::floor(currentZoom)); + return function.getInterpolator().interpolationFactor(zoomRange, std::floor(currentZoom)); } else { - return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, currentZoom); + return function.getInterpolator().interpolationFactor(zoomRange, currentZoom); } } @@ -237,8 +237,7 @@ public: private: style::CompositeFunction<T> function; T defaultValue; - using CoveringRanges = typename style::CompositeFunction<T>::CoveringRanges; - Range<CoveringRanges> rangeOfCoveringRanges; + Range<float> zoomRange; gl::VertexVector<Vertex> vertexVector; optional<gl::VertexBuffer<Vertex>> vertexBuffer; }; diff --git a/src/mbgl/style/expression/value.cpp b/src/mbgl/style/expression/value.cpp index f1cf685e91..60b67fd9e8 100644 --- a/src/mbgl/style/expression/value.cpp +++ b/src/mbgl/style/expression/value.cpp @@ -111,7 +111,7 @@ struct Converter<mbgl::Value> { template <typename T, std::size_t N> struct Converter<std::array<T, N>> { static Value toExpressionValue(const std::array<T, N>& value) { - std::vector<Value> result; + std::vector<Value> result(N); std::copy_n(value.begin(), N, result.begin()); return result; } |