summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnand Thakker <github@anandthakker.net>2017-08-11 21:51:21 -0400
committerAnand Thakker <github@anandthakker.net>2017-08-11 21:55:40 -0400
commitfe670e79474f5ef678631e47abd2e61c1d34e3ab (patch)
treead85aa818e2150a66379dadbbd67ff45103fe0cb
parent0a2bda6755f3db5cf198938f9f7cbb8a336ff65a (diff)
downloadqtlocation-mapboxgl-fe670e79474f5ef678631e47abd2e61c1d34e3ab.tar.gz
Implement CompositeFunction using expressions
-rw-r--r--include/mbgl/style/expression/coalesce.hpp8
-rw-r--r--include/mbgl/style/expression/curve.hpp8
-rw-r--r--include/mbgl/style/function/composite_function.hpp133
-rw-r--r--include/mbgl/style/function/convert.hpp115
-rw-r--r--include/mbgl/util/interpolate.hpp27
-rw-r--r--package.json1
-rw-r--r--src/mbgl/renderer/paint_property_binder.hpp11
-rw-r--r--src/mbgl/style/expression/value.cpp2
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;
}