Skip to content
This repository was archived by the owner on Aug 8, 2023. It is now read-only.

Commit b2f5305

Browse files
committed
[core] Fix composite function approximation for non-integer stops
1 parent cdc5f8f commit b2f5305

File tree

4 files changed

+84
-28
lines changed

4 files changed

+84
-28
lines changed

include/mbgl/style/function/composite_function.hpp

+53-22
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,16 @@ class CompositeFunction {
4949
defaultValue(std::move(defaultValue_)) {
5050
}
5151

52-
std::tuple<Range<float>, Range<InnerStops>>
53-
coveringRanges(float zoom) const {
52+
struct CoveringRanges {
53+
float zoom;
54+
Range<float> coveringZoomRange;
55+
Range<InnerStops> coveringStopsRange;
56+
};
57+
58+
// Return the relevant stop zoom values and inner stops that bracket a given zoom level. This
59+
// is the first step toward evaluating the function, and is used for in the course of both partial
60+
// evaluation of data-driven paint properties, and full evaluation of data-driven layout properties.
61+
CoveringRanges coveringRanges(float zoom) const {
5462
return stops.match(
5563
[&] (const auto& s) {
5664
assert(!s.stops.empty());
@@ -63,7 +71,8 @@ class CompositeFunction {
6371
minIt--;
6472
}
6573

66-
return std::make_tuple(
74+
return CoveringRanges {
75+
zoom,
6776
Range<float> {
6877
minIt == s.stops.end() ? s.stops.rbegin()->first : minIt->first,
6978
maxIt == s.stops.end() ? s.stops.rbegin()->first : maxIt->first
@@ -72,38 +81,49 @@ class CompositeFunction {
7281
s.innerStops(minIt == s.stops.end() ? s.stops.rbegin()->second : minIt->second),
7382
s.innerStops(maxIt == s.stops.end() ? s.stops.rbegin()->second : maxIt->second)
7483
}
75-
);
84+
};
7685
}
7786
);
7887
}
7988

89+
// Given a range of zoom values (typically two adjacent integer zoom levels, e.g. 5.0 and 6.0),
90+
// return the covering ranges for both. This is used in the course of partial evaluation for
91+
// data-driven paint properties.
92+
Range<CoveringRanges> rangeOfCoveringRanges(Range<float> zoomRange) {
93+
return Range<CoveringRanges> {
94+
coveringRanges(zoomRange.min),
95+
coveringRanges(zoomRange.max)
96+
};
97+
}
98+
99+
// Given the covering ranges for range of zoom values (typically two adjacent integer zoom levels,
100+
// e.g. 5.0 and 6.0), and a feature, return the results of fully evaluating the function for that
101+
// feature at each of the two zoom levels. These two results are what go into the paint vertex buffers
102+
// for vertices associated with this feature. The shader will interpolate between them at render time.
80103
template <class Feature>
81-
Range<T> evaluate(Range<InnerStops> coveringStops,
82-
const Feature& feature,
83-
T finalDefaultValue) const {
84-
optional<Value> v = feature.getValue(property);
85-
if (!v) {
86-
return {
104+
Range<T> evaluate(const Range<CoveringRanges>& ranges, const Feature& feature, T finalDefaultValue) {
105+
optional<Value> value = feature.getValue(property);
106+
if (!value) {
107+
return Range<T> {
87108
defaultValue.value_or(finalDefaultValue),
88109
defaultValue.value_or(finalDefaultValue)
89110
};
90111
}
91-
auto eval = [&] (const auto& s) {
92-
return s.evaluate(*v).value_or(defaultValue.value_or(finalDefaultValue));
93-
};
94112
return Range<T> {
95-
coveringStops.min.match(eval),
96-
coveringStops.max.match(eval)
113+
evaluateFinal(ranges.min, *value, finalDefaultValue),
114+
evaluateFinal(ranges.max, *value, finalDefaultValue)
97115
};
98116
}
99117

100-
T evaluate(float zoom, const GeometryTileFeature& feature, T finalDefaultValue) const {
101-
std::tuple<Range<float>, Range<InnerStops>> ranges = coveringRanges(zoom);
102-
Range<T> resultRange = evaluate(std::get<1>(ranges), feature, finalDefaultValue);
103-
return util::interpolate(
104-
resultRange.min,
105-
resultRange.max,
106-
util::interpolationFactor(1.0f, std::get<0>(ranges), zoom));
118+
// Fully evaluate the function for a zoom value and feature. This is used when evaluating data-driven
119+
// layout properties.
120+
template <class Feature>
121+
T evaluate(float zoom, const Feature& feature, T finalDefaultValue) const {
122+
optional<Value> value = feature.getValue(property);
123+
if (!value) {
124+
return defaultValue.value_or(finalDefaultValue);
125+
}
126+
return evaluateFinal(coveringRanges(zoom), *value, finalDefaultValue);
107127
}
108128

109129
friend bool operator==(const CompositeFunction& lhs,
@@ -115,6 +135,17 @@ class CompositeFunction {
115135
std::string property;
116136
Stops stops;
117137
optional<T> defaultValue;
138+
139+
private:
140+
T evaluateFinal(const CoveringRanges& ranges, const Value& value, T finalDefaultValue) const {
141+
auto eval = [&] (const auto& s) {
142+
return s.evaluate(value).value_or(defaultValue.value_or(finalDefaultValue));
143+
};
144+
return util::interpolate(
145+
ranges.coveringStopsRange.min.match(eval),
146+
ranges.coveringStopsRange.max.match(eval),
147+
util::interpolationFactor(1.0f, ranges.coveringZoomRange, ranges.zoom));
148+
}
118149
};
119150

120151
} // namespace style

src/mbgl/renderer/paint_property_binder.hpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,11 @@ class CompositeFunctionPaintPropertyBinder : public PaintPropertyBinder<T, A> {
189189
CompositeFunctionPaintPropertyBinder(style::CompositeFunction<T> function_, float zoom, T defaultValue_)
190190
: function(std::move(function_)),
191191
defaultValue(std::move(defaultValue_)),
192-
coveringRanges(function.coveringRanges(zoom)) {
192+
rangeOfCoveringRanges(function.rangeOfCoveringRanges({zoom, zoom + 1})) {
193193
}
194194

195195
void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) override {
196-
Range<T> range = function.evaluate(std::get<1>(coveringRanges), feature, defaultValue);
196+
Range<T> range = function.evaluate(rangeOfCoveringRanges, feature, defaultValue);
197197
this->statistics.add(range.min);
198198
this->statistics.add(range.max);
199199
AttributeValue value = zoomInterpolatedAttributeValue(
@@ -217,7 +217,7 @@ class CompositeFunctionPaintPropertyBinder : public PaintPropertyBinder<T, A> {
217217
}
218218

219219
float interpolationFactor(float currentZoom) const override {
220-
return util::interpolationFactor(1.0f, std::get<0>(coveringRanges), currentZoom);
220+
return util::interpolationFactor(1.0f, { rangeOfCoveringRanges.min.zoom, rangeOfCoveringRanges.max.zoom }, currentZoom);
221221
}
222222

223223
T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
@@ -230,10 +230,10 @@ class CompositeFunctionPaintPropertyBinder : public PaintPropertyBinder<T, A> {
230230
}
231231

232232
private:
233-
using InnerStops = typename style::CompositeFunction<T>::InnerStops;
234233
style::CompositeFunction<T> function;
235234
T defaultValue;
236-
std::tuple<Range<float>, Range<InnerStops>> coveringRanges;
235+
using CoveringRanges = typename style::CompositeFunction<T>::CoveringRanges;
236+
Range<CoveringRanges> rangeOfCoveringRanges;
237237
gl::VertexVector<Vertex> vertexVector;
238238
optional<gl::VertexBuffer<Vertex>> vertexBuffer;
239239
};

test/style/function/composite_function.test.cpp

+25
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,28 @@ TEST(CompositeFunction, ZoomInterpolation) {
4444
}), 0.0f)
4545
.evaluate(0.0f, oneInteger, -1.0f)) << "Should interpolate TO the first stop";
4646
}
47+
48+
TEST(CompositeFunction, Issue8460) {
49+
CompositeFunction<float> fn1("property", CompositeExponentialStops<float>({
50+
{15.0f, {{uint64_t(1), 0.0f}}},
51+
{15.2f, {{uint64_t(1), 600.0f}}},
52+
}), 0.0f);
53+
54+
EXPECT_NEAR( 0.0f, fn1.evaluate(15.0f, oneInteger, -1.0f), 0.00);
55+
EXPECT_NEAR(300.0f, fn1.evaluate(15.1f, oneInteger, -1.0f), 0.01);
56+
EXPECT_NEAR(600.0f, fn1.evaluate(15.2f, oneInteger, -1.0f), 0.00);
57+
EXPECT_NEAR(600.0f, fn1.evaluate(16.0f, oneInteger, -1.0f), 0.00);
58+
59+
CompositeFunction<float> fn2("property", CompositeExponentialStops<float>({
60+
{15.0f, {{uint64_t(1), 0.0f}}},
61+
{15.2f, {{uint64_t(1), 300.0f}}},
62+
{18.0f, {{uint64_t(1), 600.0f}}},
63+
}), 0.0f);
64+
65+
EXPECT_NEAR( 0.0f, fn2.evaluate(15.0f, oneInteger, -1.0f), 0.00);
66+
EXPECT_NEAR(150.0f, fn2.evaluate(15.1f, oneInteger, -1.0f), 0.01);
67+
EXPECT_NEAR(300.0f, fn2.evaluate(15.2f, oneInteger, -1.0f), 0.00);
68+
EXPECT_NEAR(385.71f, fn2.evaluate(16.0f, oneInteger, -1.0f), 0.01);
69+
EXPECT_NEAR(600.0f, fn2.evaluate(18.0f, oneInteger, -1.0f), 0.00);
70+
EXPECT_NEAR(600.0f, fn2.evaluate(19.0f, oneInteger, -1.0f), 0.00);
71+
}

0 commit comments

Comments
 (0)