#pragma once #include #include #include #include #include #include #include #include namespace mbgl { class GeometryTileFeature; namespace style { // A CompositeFunction consists of an outer zoom function whose stop range values are // "inner" source functions. It provides the GL Native implementation of // "zoom-and-property" functions from the style spec. template class CompositeFunction { public: using InnerStops = std::conditional_t< util::Interpolatable, variant< ExponentialStops, IntervalStops, CategoricalStops>, variant< IntervalStops, CategoricalStops>>; using Stops = std::conditional_t< util::Interpolatable, variant< CompositeExponentialStops, CompositeIntervalStops, CompositeCategoricalStops>, variant< CompositeIntervalStops, CompositeCategoricalStops>>; CompositeFunction(std::string property_, Stops stops_, optional defaultValue_ = {}) : property(std::move(property_)), stops(std::move(stops_)), defaultValue(std::move(defaultValue_)) { } struct CoveringRanges { float zoom; Range coveringZoomRange; Range 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 { minIt == s.stops.end() ? s.stops.rbegin()->first : minIt->first, maxIt == s.stops.end() ? s.stops.rbegin()->first : maxIt->first }, Range { 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 rangeOfCoveringRanges(Range zoomRange) { return Range { coveringRanges(zoomRange.min), coveringRanges(zoomRange.max) }; } // 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. template Range evaluate(const Range& ranges, const Feature& feature, T finalDefaultValue) { optional value = feature.getValue(property); if (!value) { return Range { defaultValue.value_or(finalDefaultValue), defaultValue.value_or(finalDefaultValue) }; } return Range { evaluateFinal(ranges.min, *value, finalDefaultValue), evaluateFinal(ranges.max, *value, finalDefaultValue) }; } // Fully evaluate the function for a zoom value and feature. This is used when evaluating data-driven // layout properties. template T evaluate(float zoom, const Feature& feature, T finalDefaultValue) const { optional value = feature.getValue(property); if (!value) { return defaultValue.value_or(finalDefaultValue); } return evaluateFinal(coveringRanges(zoom), *value, finalDefaultValue); } friend bool operator==(const CompositeFunction& lhs, const CompositeFunction& rhs) { return std::tie(lhs.property, lhs.stops, lhs.defaultValue) == std::tie(rhs.property, rhs.stops, rhs.defaultValue); } std::string property; Stops stops; optional defaultValue; 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)); } }; } // namespace style } // namespace mbgl