diff options
author | Langston Smith <langston.smith@mapbox.com> | 2018-01-04 11:15:50 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-04 11:15:50 -0800 |
commit | 2ea955d2751ba6459f99a0695e53505c0a11702b (patch) | |
tree | f54450918b634a2eea1bd2c4ebc671bf1bb06106 /include | |
parent | f2ec6ae326bad79fea2b06a21151a2835522572a (diff) | |
parent | c62b0af24fc76b4bb2eb34100611dd3ee9ee5536 (diff) | |
download | qtlocation-mapboxgl-2ea955d2751ba6459f99a0695e53505c0a11702b.tar.gz |
Merge branch 'master' into ls-android-readme-tweaksupstream/ls-android-readme-tweaks
Diffstat (limited to 'include')
100 files changed, 3897 insertions, 1688 deletions
diff --git a/include/mbgl/actor/actor.hpp b/include/mbgl/actor/actor.hpp index 810114c513..a0df19208e 100644 --- a/include/mbgl/actor/actor.hpp +++ b/include/mbgl/actor/actor.hpp @@ -6,6 +6,8 @@ #include <mbgl/util/noncopyable.hpp> #include <memory> +#include <future> +#include <type_traits> namespace mbgl { @@ -46,10 +48,18 @@ namespace mbgl { template <class Object> class Actor : public util::noncopyable { public: - template <class... Args> + + // Enabled for Objects with a constructor taking ActorRef<Object> as the first parameter + template <typename U = Object, class... Args, typename std::enable_if<std::is_constructible<U, ActorRef<U>, Args...>::value>::type * = nullptr> Actor(Scheduler& scheduler, Args&&... args_) - : mailbox(std::make_shared<Mailbox>(scheduler)), - object(self(), std::forward<Args>(args_)...) { + : mailbox(std::make_shared<Mailbox>(scheduler)), + object(self(), std::forward<Args>(args_)...) { + } + + // Enabled for plain Objects + template<typename U = Object, class... Args, typename std::enable_if<!std::is_constructible<U, ActorRef<U>, Args...>::value>::type * = nullptr> + Actor(Scheduler& scheduler, Args&& ... args_) + : mailbox(std::make_shared<Mailbox>(scheduler)), object(std::forward<Args>(args_)...) { } ~Actor() { @@ -61,6 +71,17 @@ public: mailbox->push(actor::makeMessage(object, fn, std::forward<Args>(args)...)); } + template <typename Fn, class... Args> + auto ask(Fn fn, Args&&... args) { + // Result type is deduced from the function's return type + using ResultType = typename std::result_of<decltype(fn)(Object, Args...)>::type; + + std::promise<ResultType> promise; + auto future = promise.get_future(); + mailbox->push(actor::makeMessage(std::move(promise), object, fn, std::forward<Args>(args)...)); + return future; + } + ActorRef<std::decay_t<Object>> self() { return ActorRef<std::decay_t<Object>>(object, mailbox); } diff --git a/include/mbgl/actor/actor_ref.hpp b/include/mbgl/actor/actor_ref.hpp index aeb5bb4507..958ee3777c 100644 --- a/include/mbgl/actor/actor_ref.hpp +++ b/include/mbgl/actor/actor_ref.hpp @@ -35,6 +35,27 @@ public: } } + template <typename Fn, class... Args> + auto ask(Fn fn, Args&&... args) { + // Result type is deduced from the function's return type + using ResultType = typename std::result_of<decltype(fn)(Object, Args...)>::type; + + std::promise<ResultType> promise; + auto future = promise.get_future(); + + if (auto mailbox = weakMailbox.lock()) { + mailbox->push( + actor::makeMessage( + std::move(promise), *object, fn, std::forward<Args>(args)... + ) + ); + } else { + promise.set_exception(std::make_exception_ptr(std::runtime_error("Actor has gone away"))); + } + + return future; + } + private: Object* object; std::weak_ptr<Mailbox> weakMailbox; diff --git a/include/mbgl/actor/message.hpp b/include/mbgl/actor/message.hpp index cf071d4933..0a20993352 100644 --- a/include/mbgl/actor/message.hpp +++ b/include/mbgl/actor/message.hpp @@ -1,5 +1,8 @@ #pragma once +#include <mbgl/util/optional.hpp> + +#include <future> #include <utility> namespace mbgl { @@ -36,6 +39,57 @@ public: ArgsTuple argsTuple; }; +template <class ResultType, class Object, class MemberFn, class ArgsTuple> +class AskMessageImpl : public Message { +public: + AskMessageImpl(std::promise<ResultType> promise_, Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_) + : object(object_), + memberFn(memberFn_), + argsTuple(std::move(argsTuple_)), + promise(std::move(promise_)) { + } + + void operator()() override { + promise.set_value(ask(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>())); + } + + template <std::size_t... I> + ResultType ask(std::index_sequence<I...>) { + return (object.*memberFn)(std::move(std::get<I>(argsTuple))...); + } + + Object& object; + MemberFn memberFn; + ArgsTuple argsTuple; + std::promise<ResultType> promise; +}; + +template <class Object, class MemberFn, class ArgsTuple> +class AskMessageImpl<void, Object, MemberFn, ArgsTuple> : public Message { +public: + AskMessageImpl(std::promise<void> promise_, Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_) + : object(object_), + memberFn(memberFn_), + argsTuple(std::move(argsTuple_)), + promise(std::move(promise_)) { + } + + void operator()() override { + ask(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>()); + promise.set_value(); + } + + template <std::size_t... I> + void ask(std::index_sequence<I...>) { + (object.*memberFn)(std::move(std::get<I>(argsTuple))...); + } + + Object& object; + MemberFn memberFn; + ArgsTuple argsTuple; + std::promise<void> promise; +}; + namespace actor { template <class Object, class MemberFn, class... Args> @@ -44,5 +98,11 @@ std::unique_ptr<Message> makeMessage(Object& object, MemberFn memberFn, Args&&.. return std::make_unique<MessageImpl<Object, MemberFn, decltype(tuple)>>(object, memberFn, std::move(tuple)); } +template <class ResultType, class Object, class MemberFn, class... Args> +std::unique_ptr<Message> makeMessage(std::promise<ResultType>&& promise, Object& object, MemberFn memberFn, Args&&... args) { + auto tuple = std::make_tuple(std::forward<Args>(args)...); + return std::make_unique<AskMessageImpl<ResultType, Object, MemberFn, decltype(tuple)>>(std::move(promise), object, memberFn, std::move(tuple)); +} + } // namespace actor } // namespace mbgl diff --git a/include/mbgl/actor/scheduler.hpp b/include/mbgl/actor/scheduler.hpp index 83689c3348..d8a26ebeab 100644 --- a/include/mbgl/actor/scheduler.hpp +++ b/include/mbgl/actor/scheduler.hpp @@ -21,18 +21,21 @@ class Mailbox; Subject to these constraints, processing can happen on whatever thread in the pool is available. - * `RunLoop` is a `Scheduler` that is typically used to create a mailbox and - `ActorRef` for an object that lives on the main thread and is not itself wrapped - as an `Actor`: - - auto mailbox = std::make_shared<Mailbox>(*util::RunLoop::Get()); + * `Scheduler::GetCurrent()` is typically used to create a mailbox and `ActorRef` + for an object that lives on the main thread and is not itself wrapped an + `Actor`. The underlying implementation of this Scheduler should usually be + a `RunLoop` + auto mailbox = std::make_shared<Mailbox>(*Scheduler::Get()); Actor<Worker> worker(threadPool, ActorRef<Foo>(*this, mailbox)); */ - class Scheduler { public: virtual ~Scheduler() = default; virtual void schedule(std::weak_ptr<Mailbox>) = 0; + + // Set/Get the current Scheduler for this thread + static Scheduler* GetCurrent(); + static void SetCurrent(Scheduler*); }; } // namespace mbgl diff --git a/include/mbgl/annotation/annotation.hpp b/include/mbgl/annotation/annotation.hpp index 96e06ca222..bbe479b5ba 100644 --- a/include/mbgl/annotation/annotation.hpp +++ b/include/mbgl/annotation/annotation.hpp @@ -17,6 +17,10 @@ using AnnotationIDs = std::vector<AnnotationID>; class SymbolAnnotation { public: + SymbolAnnotation(Point<double> geometry_, std::string icon_ = {}) + : geometry(std::move(geometry_)), + icon(std::move(icon_)) {} + Point<double> geometry; std::string icon; }; @@ -29,18 +33,36 @@ using ShapeAnnotationGeometry = variant< class LineAnnotation { public: + LineAnnotation(ShapeAnnotationGeometry geometry_, + style::DataDrivenPropertyValue<float> opacity_ = 1.0f, + style::DataDrivenPropertyValue<float> width_ = 1.0f, + style::DataDrivenPropertyValue<Color> color_ = Color::black()) + : geometry(std::move(geometry_)), + opacity(std::move(opacity_)), + width(std::move(width_)), + color(std::move(color_)) {} + ShapeAnnotationGeometry geometry; - style::DataDrivenPropertyValue<float> opacity { 1.0f }; - style::DataDrivenPropertyValue<float> width { 1.0f }; - style::DataDrivenPropertyValue<Color> color { Color::black() }; + style::DataDrivenPropertyValue<float> opacity; + style::DataDrivenPropertyValue<float> width; + style::DataDrivenPropertyValue<Color> color; }; class FillAnnotation { public: + FillAnnotation(ShapeAnnotationGeometry geometry_, + style::DataDrivenPropertyValue<float> opacity_ = 1.0f, + style::DataDrivenPropertyValue<Color> color_ = Color::black(), + style::DataDrivenPropertyValue<Color> outlineColor_ = {}) + : geometry(std::move(geometry_)), + opacity(std::move(opacity_)), + color(std::move(color_)), + outlineColor(std::move(outlineColor_)) {} + ShapeAnnotationGeometry geometry; - style::DataDrivenPropertyValue<float> opacity { 1.0f }; - style::DataDrivenPropertyValue<Color> color { Color::black() }; - style::DataDrivenPropertyValue<Color> outlineColor {}; + style::DataDrivenPropertyValue<float> opacity; + style::DataDrivenPropertyValue<Color> color; + style::DataDrivenPropertyValue<Color> outlineColor; }; using Annotation = variant< diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index dd29d444bd..5ba23a76dd 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -4,13 +4,11 @@ #include <mbgl/util/chrono.hpp> #include <mbgl/map/map_observer.hpp> #include <mbgl/map/mode.hpp> -#include <mbgl/util/geo.hpp> -#include <mbgl/util/feature.hpp> #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/size.hpp> #include <mbgl/annotation/annotation.hpp> #include <mbgl/map/camera.hpp> -#include <mbgl/map/query.hpp> +#include <mbgl/util/geometry.hpp> #include <cstdint> #include <string> @@ -20,10 +18,9 @@ namespace mbgl { -class Backend; -class View; class FileSource; class Scheduler; +class RendererFrontend; namespace style { class Image; @@ -32,29 +29,26 @@ class Style; class Map : private util::noncopyable { public: - explicit Map(Backend&, + explicit Map(RendererFrontend&, + MapObserver&, Size size, float pixelRatio, FileSource&, Scheduler&, MapMode mapMode = MapMode::Continuous, - GLContextMode contextMode = GLContextMode::Unique, ConstrainMode constrainMode = ConstrainMode::HeightOnly, - ViewportMode viewportMode = ViewportMode::Default, - const optional<std::string>& programCacheDir = {}); + ViewportMode viewportMode = ViewportMode::Default); ~Map(); // Register a callback that will get called (on the render thread) when all resources have // been loaded and a complete render occurs. using StillImageCallback = std::function<void (std::exception_ptr)>; - void renderStill(View&, StillImageCallback callback); + void renderStill(StillImageCallback); + void renderStill(const CameraOptions&, MapDebugOptions, StillImageCallback); // Triggers a repaint. void triggerRepaint(); - // Main render function. - void render(View&); - style::Style& getStyle(); const style::Style& getStyle() const; @@ -73,6 +67,10 @@ public: void jumpTo(const CameraOptions&); void easeTo(const CameraOptions&, const AnimationOptions&); void flyTo(const CameraOptions&, const AnimationOptions&); + CameraOptions cameraForLatLngBounds(const LatLngBounds&, const EdgeInsets&, optional<double> bearing = {}) const; + CameraOptions cameraForLatLngs(const std::vector<LatLng>&, const EdgeInsets&, optional<double> bearing = {}) const; + CameraOptions cameraForGeometry(const Geometry<double>&, const EdgeInsets&, optional<double> bearing = {}) const; + LatLngBounds latLngBoundsForCamera(const CameraOptions&) const; // Position void moveBy(const ScreenCoordinate&, const AnimationOptions& = {}); @@ -89,9 +87,6 @@ public: double getZoom() const; void setLatLngZoom(const LatLng&, double zoom, const AnimationOptions& = {}); void setLatLngZoom(const LatLng&, double zoom, const EdgeInsets&, const AnimationOptions& = {}); - CameraOptions cameraForLatLngBounds(const LatLngBounds&, const EdgeInsets&) const; - CameraOptions cameraForLatLngs(const std::vector<LatLng>&, const EdgeInsets&) const; - LatLngBounds latLngBoundsForCamera(const CameraOptions&) const; void resetZoom(); // Bounds @@ -132,6 +127,14 @@ public: void setViewportMode(ViewportMode); ViewportMode getViewportMode() const; + // Projection mode + void setAxonometric(bool); + bool getAxonometric() const; + void setXSkew(double ySkew); + double getXSkew() const; + void setYSkew(double ySkew); + double getYSkew() const; + // Size void setSize(Size); Size getSize() const; @@ -149,16 +152,14 @@ public: void updateAnnotation(AnnotationID, const Annotation&); void removeAnnotation(AnnotationID); - // Feature queries - std::vector<Feature> queryRenderedFeatures(const ScreenCoordinate&, const RenderedQueryOptions& options = {}); - std::vector<Feature> queryRenderedFeatures(const ScreenBox&, const RenderedQueryOptions& options = {}); - std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options = {}); - - AnnotationIDs queryPointAnnotations(const ScreenBox&); - - // Memory - void setSourceTileCacheSize(size_t); - void onLowMemory(); + // Tile prefetching + // + // When loading a map, if `PrefetchZoomDelta` is set to any number greater than 0, the map will + // first request a tile for `zoom = getZoom() - delta` in a attempt to display a full map at + // lower resolution as quick as possible. It will get clamped at the tile source minimum zoom. + // The default `delta` is 4. + void setPrefetchZoomDelta(uint8_t delta); + uint8_t getPrefetchZoomDelta() const; // Debug void setDebug(MapDebugOptions); diff --git a/include/mbgl/map/mode.hpp b/include/mbgl/map/mode.hpp index afec5c0a08..4ee289d855 100644 --- a/include/mbgl/map/mode.hpp +++ b/include/mbgl/map/mode.hpp @@ -1,5 +1,6 @@ #pragma once +#include <mbgl/util/util.hpp> #include <mbgl/util/traits.hpp> #include <cstdint> @@ -10,16 +11,8 @@ using EnumType = uint32_t; enum class MapMode : EnumType { Continuous, // continually updating map - Still, // a once-off still image -}; - -// We can avoid redundant GL calls when it is known that the GL context is not -// being shared. In a shared GL context case, we need to make sure that the -// correct GL configurations are in use - they might have changed between render -// calls. -enum class GLContextMode : EnumType { - Unique, - Shared, + Static, // a once-off still image of an arbitrary viewport + Tile // a once-off still image of a single tile }; // We can choose to constrain the map both horizontally or vertically, or only @@ -51,23 +44,23 @@ enum class MapDebugOptions : EnumType { #endif // MBGL_USE_GLES2 }; -constexpr MapDebugOptions operator|(MapDebugOptions lhs, MapDebugOptions rhs) { +MBGL_CONSTEXPR MapDebugOptions operator|(MapDebugOptions lhs, MapDebugOptions rhs) { return MapDebugOptions(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs)); } -constexpr MapDebugOptions& operator|=(MapDebugOptions& lhs, MapDebugOptions rhs) { +MBGL_CONSTEXPR MapDebugOptions& operator|=(MapDebugOptions& lhs, MapDebugOptions rhs) { return (lhs = MapDebugOptions(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs))); } -constexpr bool operator&(MapDebugOptions lhs, MapDebugOptions rhs) { +MBGL_CONSTEXPR bool operator&(MapDebugOptions lhs, MapDebugOptions rhs) { return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs); } -constexpr MapDebugOptions& operator&=(MapDebugOptions& lhs, MapDebugOptions rhs) { +MBGL_CONSTEXPR MapDebugOptions& operator&=(MapDebugOptions& lhs, MapDebugOptions rhs) { return (lhs = MapDebugOptions(mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs))); } -constexpr MapDebugOptions operator~(MapDebugOptions value) { +MBGL_CONSTEXPR MapDebugOptions operator~(MapDebugOptions value) { return MapDebugOptions(~mbgl::underlying_type(value)); } diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp deleted file mode 100644 index 295779fe51..0000000000 --- a/include/mbgl/map/view.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -namespace mbgl { - -class Map; - -class View { -public: - virtual ~View() = default; - - // Called when this View is used for rendering. Implementations should ensure that a renderable - // object is bound and glClear/glDraw* calls can be done. They should also make sure that - // calling .bind() repeatedly is a no-op and that the appropriate gl::Context values are - // set to the current state. - virtual void bind() = 0; -}; - -} // namespace mbgl diff --git a/include/mbgl/math/log2.hpp b/include/mbgl/math/log2.hpp index 8a3bc7f1c0..53d5e45545 100644 --- a/include/mbgl/math/log2.hpp +++ b/include/mbgl/math/log2.hpp @@ -17,9 +17,9 @@ typename std::enable_if_t<std::is_floating_point<T>::value, T> log2(T x) // log2() is producing wrong results on ARMv5 binaries // running on ARMv7+ CPUs. #if defined(__ANDROID__) - return std::log(x) / M_LN2; + return ::log(x) / M_LN2; #else - return std::log2(x); + return ::log2(x); #endif } diff --git a/include/mbgl/map/backend_scope.hpp b/include/mbgl/renderer/backend_scope.hpp index 4985cd197f..73bafc84c7 100644 --- a/include/mbgl/map/backend_scope.hpp +++ b/include/mbgl/renderer/backend_scope.hpp @@ -2,7 +2,7 @@ namespace mbgl { -class Backend; +class RendererBackend; class BackendScope { public: @@ -15,17 +15,21 @@ public: Explicit, }; - BackendScope(Backend&, ScopeType = ScopeType::Explicit); + BackendScope(RendererBackend&, ScopeType = ScopeType::Explicit); ~BackendScope(); // Returns true when there is currently a BackendScope active in this thread. static bool exists(); private: + void activate(); + void deactivate(); + BackendScope* priorScope; BackendScope* nextScope; - Backend& backend; + RendererBackend& backend; const ScopeType scopeType; + bool activated = false; }; } // namespace mbgl diff --git a/include/mbgl/renderer/mode.hpp b/include/mbgl/renderer/mode.hpp new file mode 100644 index 0000000000..6ff42d8058 --- /dev/null +++ b/include/mbgl/renderer/mode.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include <cstdint> + +namespace mbgl { + +using EnumType = uint32_t; + +// We can avoid redundant GL calls when it is known that the GL context is not +// being shared. In a shared GL context case, we need to make sure that the +// correct GL configurations are in use - they might have changed between render +// calls. +enum class GLContextMode : EnumType { + Unique, + Shared, +}; + +} // namespace mbgl diff --git a/include/mbgl/map/query.hpp b/include/mbgl/renderer/query.hpp index e114fa2ebd..4cadf4f017 100644 --- a/include/mbgl/map/query.hpp +++ b/include/mbgl/renderer/query.hpp @@ -13,6 +13,11 @@ namespace mbgl { */ class RenderedQueryOptions { public: + RenderedQueryOptions(optional<std::vector<std::string>> layerIDs_ = {}, + optional<style::Filter> filter_ = {}) + : layerIDs(std::move(layerIDs_)), + filter(std::move(filter_)) {} + /** layerIDs to include in the query */ optional<std::vector<std::string>> layerIDs; @@ -24,6 +29,11 @@ public: */ class SourceQueryOptions { public: + SourceQueryOptions(optional<std::vector<std::string>> sourceLayers_ = {}, + optional<style::Filter> filter_ = {}) + : sourceLayers(std::move(sourceLayers_)), + filter(std::move(filter_)) {} + // Required for VectorSource, ignored for GeoJSONSource optional<std::vector<std::string>> sourceLayers; diff --git a/include/mbgl/renderer/renderer.hpp b/include/mbgl/renderer/renderer.hpp new file mode 100644 index 0000000000..db28ee92fc --- /dev/null +++ b/include/mbgl/renderer/renderer.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include <mbgl/renderer/query.hpp> +#include <mbgl/renderer/mode.hpp> +#include <mbgl/annotation/annotation.hpp> +#include <mbgl/util/geo.hpp> +#include <mbgl/util/geo.hpp> + +#include <functional> +#include <memory> +#include <string> +#include <vector> + +namespace mbgl { + +class FileSource; +class RendererBackend; +class RendererObserver; +class RenderedQueryOptions; +class Scheduler; +class SourceQueryOptions; +class UpdateParameters; + +class Renderer { +public: + Renderer(RendererBackend&, float pixelRatio_, FileSource&, Scheduler&, + GLContextMode = GLContextMode::Unique, + const optional<std::string> programCacheDir = {}, + const optional<std::string> localFontFamily = {}); + ~Renderer(); + + void markContextLost(); + + void setObserver(RendererObserver*); + + void render(const UpdateParameters&); + + // Feature queries + std::vector<Feature> queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions& options = {}) const; + std::vector<Feature> queryRenderedFeatures(const ScreenCoordinate& point, const RenderedQueryOptions& options = {}) const; + std::vector<Feature> queryRenderedFeatures(const ScreenBox& box, const RenderedQueryOptions& options = {}) const; + std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options = {}) const; + AnnotationIDs queryPointAnnotations(const ScreenBox& box) const; + AnnotationIDs queryShapeAnnotations(const ScreenBox& box) const; + AnnotationIDs getAnnotationIDs(const std::vector<Feature>&) const; + + // Debug + void dumpDebugLogs(); + + // Memory + void onLowMemory(); + +private: + class Impl; + std::unique_ptr<Impl> impl; +}; + +} // namespace mbgl diff --git a/include/mbgl/map/backend.hpp b/include/mbgl/renderer/renderer_backend.hpp index 2e73ad994c..b83c128169 100644 --- a/include/mbgl/map/backend.hpp +++ b/include/mbgl/renderer/renderer_backend.hpp @@ -1,8 +1,9 @@ #pragma once -#include <mbgl/map/map_observer.hpp> +#include <mbgl/renderer/backend_scope.hpp> #include <mbgl/util/image.hpp> #include <mbgl/util/size.hpp> +#include <mbgl/util/util.hpp> #include <memory> #include <mutex> @@ -15,12 +16,12 @@ using ProcAddress = void (*)(); using FramebufferID = uint32_t; } // namespace gl -class BackendScope; - -class Backend : public MapObserver { +// The RendererBackend is used by the Renderer to facilitate +// the actual rendering. +class RendererBackend { public: - Backend(); - virtual ~Backend(); + RendererBackend(); + virtual ~RendererBackend(); // Returns the backend's context which manages OpenGL state. gl::Context& getContext(); @@ -28,26 +29,28 @@ public: // Called prior to rendering to update the internally assumed OpenGL state. virtual void updateAssumedState() = 0; - // Called when the map needs to be rendered; the backend should call Map::render() at some point - // in the near future. (Not called for Map::renderStill() mode.) - virtual void invalidate() = 0; + // Called when this backend is used for rendering. Implementations should ensure that a renderable + // object is bound and glClear/glDraw* calls can be done. They should also make sure that + // calling .bind() repeatedly is a no-op and that the appropriate gl::Context values are + // set to the current state. + virtual void bind() = 0; + + virtual Size getFramebufferSize() const = 0; protected: - // Called with the name of an OpenGL extension that should be loaded. Backend implementations + // Called with the name of an OpenGL extension that should be loaded. RendererBackend implementations // must call the API-specific version that obtains the function pointer for this function, // or a null pointer if unsupported/unavailable. - virtual gl::ProcAddress initializeExtension(const char*) = 0; + virtual gl::ProcAddress getExtensionFunctionPointer(const char*) = 0; // Called when the backend's GL context needs to be made active or inactive. These are called, - // as a matched pair, in four situations: + // as a matched pair, exclusively through BackendScope, in two situations: // - // 1. When releasing GL resources during Map destruction - // 2. When calling a CustomLayerInitializeFunction, during Map::addLayer - // 3. When calling a CustomLayerDeinitializeFunction, during Map::removeLayer - // 4. When rendering for Map::renderStill - // - // They are *not* called for Map::render; it is assumed that the correct context is already - // activated prior to calling Map::render. + // 1. When releasing GL resources during Renderer destruction + // (Including calling CustomLayerDeinitializeFunction during RenderCustomLayer destruction) + // 2. When renderering through Renderer::render() + // (Including calling CustomLayerDeinitializeFunction for newly added custom layers and + // CustomLayerDeinitializeFunction on layer removal) virtual void activate() = 0; virtual void deactivate() = 0; @@ -82,4 +85,8 @@ private: friend class BackendScope; }; +MBGL_CONSTEXPR bool operator==(const RendererBackend& a, const RendererBackend& b) { + return &a == &b; +} + } // namespace mbgl diff --git a/include/mbgl/renderer/renderer_frontend.hpp b/include/mbgl/renderer/renderer_frontend.hpp new file mode 100644 index 0000000000..f72b0ccdde --- /dev/null +++ b/include/mbgl/renderer/renderer_frontend.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include <memory> + +namespace mbgl { + +class RendererObserver; +class UpdateParameters; + +// The RenderFrontend is the bridge between the Map and +// platform used to update and observer the Renderer +// +// It hides any threading specifics and always replies on +// the original thread. +class RendererFrontend { +public: + + virtual ~RendererFrontend() = default; + + // Must synchronously clean up the Renderer if set + virtual void reset() = 0; + + // Implementer must bind the renderer observer to the renderer in a + // appropriate manner so that the callbacks occur on the main thread + virtual void setObserver(RendererObserver&) = 0; + + // Coalescing updates is up to the implementer + virtual void update(std::shared_ptr<UpdateParameters>) = 0; +}; + +} // namespace mbgl diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp index 9911e0ce67..b9c8de5052 100644 --- a/include/mbgl/storage/default_file_source.hpp +++ b/include/mbgl/storage/default_file_source.hpp @@ -34,7 +34,7 @@ public: uint64_t maximumCacheSize = util::DEFAULT_MAX_CACHE_SIZE); ~DefaultFileSource() override; - bool supportsOptionalRequests() const override { + bool supportsCacheOnlyRequests() const override { return true; } @@ -140,6 +140,7 @@ public: void resume(); // For testing only. + void setOnlineStatus(bool); void put(const Resource&, const Response&); class Impl; diff --git a/include/mbgl/storage/file_source.hpp b/include/mbgl/storage/file_source.hpp index 404c683fdb..0709a1c245 100644 --- a/include/mbgl/storage/file_source.hpp +++ b/include/mbgl/storage/file_source.hpp @@ -24,11 +24,11 @@ public: // not be executed. virtual std::unique_ptr<AsyncRequest> request(const Resource&, Callback) = 0; - // When a file source supports optional requests, it must return true. - // Optional requests are requests that aren't as urgent, but could be useful, e.g. + // When a file source supports consulting a local cache only, it must return true. + // Cache-only requests are requests that aren't as urgent, but could be useful, e.g. // to cover part of the map while loading. The FileSource should only do cheap actions to // retrieve the data, e.g. load it from a cache, but not from the internet. - virtual bool supportsOptionalRequests() const { + virtual bool supportsCacheOnlyRequests() const { return false; } }; diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp index 818cfe2ba5..ef4a499e83 100644 --- a/include/mbgl/storage/offline.hpp +++ b/include/mbgl/storage/offline.hpp @@ -30,13 +30,15 @@ public: OfflineTilePyramidRegionDefinition(std::string, LatLngBounds, double, double, float); /* Private */ - std::vector<CanonicalTileID> tileCover(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; - + std::vector<CanonicalTileID> tileCover(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; + uint64_t tileCount(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; const std::string styleURL; const LatLngBounds bounds; const double minZoom; const double maxZoom; const float pixelRatio; +private: + Range<uint8_t> coveringZoomRange(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; }; /* diff --git a/include/mbgl/storage/online_file_source.hpp b/include/mbgl/storage/online_file_source.hpp index ffd75662e6..28d70ce544 100644 --- a/include/mbgl/storage/online_file_source.hpp +++ b/include/mbgl/storage/online_file_source.hpp @@ -24,6 +24,9 @@ public: std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override; + // For testing only. + void setOnlineStatus(bool); + private: friend class OnlineFileRequest; diff --git a/include/mbgl/storage/resource.hpp b/include/mbgl/storage/resource.hpp index 7e9ced8049..318fa389f4 100644 --- a/include/mbgl/storage/resource.hpp +++ b/include/mbgl/storage/resource.hpp @@ -4,6 +4,8 @@ #include <mbgl/util/optional.hpp> #include <mbgl/util/font_stack.hpp> #include <mbgl/util/tileset.hpp> +#include <mbgl/util/util.hpp> +#include <mbgl/util/traits.hpp> #include <string> @@ -30,18 +32,28 @@ public: int8_t z; }; - enum Necessity : bool { - Optional = false, - Required = true, + enum class LoadingMethod : uint8_t { + None = 0b00, + Cache = 0b01, + Network = 0b10, + + CacheOnly = Cache, + NetworkOnly = Network, + All = Cache | Network, }; - Resource(Kind kind_, std::string url_, optional<TileData> tileData_ = {}, Necessity necessity_ = Required) + Resource(Kind kind_, + std::string url_, + optional<TileData> tileData_ = {}, + LoadingMethod loadingMethod_ = LoadingMethod::All) : kind(kind_), - necessity(necessity_), + loadingMethod(loadingMethod_), url(std::move(url_)), tileData(std::move(tileData_)) { } + bool hasLoadingMethod(LoadingMethod method); + static Resource style(const std::string& url); static Resource source(const std::string& url); static Resource tile(const std::string& urlTemplate, @@ -50,7 +62,7 @@ public: int32_t y, int8_t z, Tileset::Scheme scheme, - Necessity = Required); + LoadingMethod = LoadingMethod::All); static Resource glyphs(const std::string& urlTemplate, const FontStack& fontStack, const std::pair<uint16_t, uint16_t>& glyphRange); @@ -59,7 +71,7 @@ public: static Resource image(const std::string& url); Kind kind; - Necessity necessity; + LoadingMethod loadingMethod; std::string url; // Includes auxiliary data if this is a tile request. @@ -68,6 +80,24 @@ public: optional<Timestamp> priorModified = {}; optional<Timestamp> priorExpires = {}; optional<std::string> priorEtag = {}; + std::shared_ptr<const std::string> priorData; }; + +MBGL_CONSTEXPR Resource::LoadingMethod operator|(Resource::LoadingMethod a, Resource::LoadingMethod b) { + return Resource::LoadingMethod(mbgl::underlying_type(a) | mbgl::underlying_type(b)); +} + +MBGL_CONSTEXPR Resource::LoadingMethod& operator|=(Resource::LoadingMethod& a, Resource::LoadingMethod b) { + return (a = a | b); +} + +MBGL_CONSTEXPR Resource::LoadingMethod operator&(Resource::LoadingMethod a, Resource::LoadingMethod b) { + return Resource::LoadingMethod(mbgl::underlying_type(a) & mbgl::underlying_type(b)); +} + +inline bool Resource::hasLoadingMethod(Resource::LoadingMethod method) { + return (loadingMethod & method) != Resource::LoadingMethod::None; +} + } // namespace mbgl diff --git a/include/mbgl/storage/response.hpp b/include/mbgl/storage/response.hpp index 32fe4e0c8a..508400141b 100644 --- a/include/mbgl/storage/response.hpp +++ b/include/mbgl/storage/response.hpp @@ -2,7 +2,6 @@ #include <mbgl/util/chrono.hpp> #include <mbgl/util/optional.hpp> -#include <mbgl/util/variant.hpp> #include <string> #include <memory> @@ -27,6 +26,10 @@ public: // This is set to true for 304 Not Modified responses. bool notModified = false; + // This is set to true when the server requested that no expired resources be used by + // specifying "Cache-Control: must-revalidate". + bool mustRevalidate = false; + // The actual data of the response. Present only for non-error, non-notModified responses. std::shared_ptr<const std::string> data; @@ -37,6 +40,12 @@ public: bool isFresh() const { return expires ? *expires > util::now() : !error; } + + // Indicates whether we are allowed to use this response according to HTTP caching rules. + // It may or may not be stale. + bool isUsable() const { + return !mustRevalidate || (expires && *expires > util::now()); + } }; class Response::Error { diff --git a/include/mbgl/style/conversion.hpp b/include/mbgl/style/conversion.hpp index 27504a89b1..71c2cec237 100644 --- a/include/mbgl/style/conversion.hpp +++ b/include/mbgl/style/conversion.hpp @@ -1,6 +1,8 @@ #pragma once #include <mbgl/util/optional.hpp> +#include <mbgl/util/feature.hpp> +#include <mbgl/util/geojson.hpp> #include <string> @@ -9,9 +11,8 @@ namespace style { namespace conversion { /* - The `conversion` namespace defines conversions from a templated type `V` representing a JSON - object conforming to the schema defined by the Mapbox Style Specification, to the various C++ - types that form the C++ model of that domain: + The `conversion` namespace defines conversions from JSON structures conforming to the schema defined by + the Mapbox Style Specification, to the various C++ types that form the C++ model of that domain: * `std::unique_ptr<Source>` * `std::unique_ptr<Layer>` @@ -20,15 +21,31 @@ namespace conversion { A single template function serves as the public interface: - template <class T, class V> - optional<T> convert(const V& value, Error& error); + template <class T> + optional<T> convert(const Convertible& input, Error& error); Where `T` is one of the above types. If the conversion fails, the result is empty, and the error parameter includes diagnostic text suitable for presentation to a library user. Otherwise, a filled optional is returned. - The implementation of `convert` requires that the following are legal expressions for a value `v` - of type `const V&`: + `Convertible` is a type that encapsulates a special form of polymorphism over various underlying types that + can serve as input to the conversion algorithm. For instance, on macOS, we need to support + conversion from both RapidJSON types, and a JSON structure represented with `NSArray`/`NSDictionary`/etc. + On Qt, we need to support conversion from RapidJSON types and QVariant. + + We don't want to use traditional forms of polymorphism to accomplish this: + + * Compile time polymorphism using a template parameter for the actual value type leads to + excessive code bloat and long compile times. + * Runtime polymorphism using virtual methods requires extra heap allocation and ubiquitous + use of std::unique_ptr, unsuitable for this performance-sensitive code. + + Therefore, we're using a custom implementation of runtime polymorphism where we manually create and + dispatch through a table of function pointers (vtable), while keeping the storage for any of the possible + underlying types inline on the stack, using `std::aligned_storage`. + + For a given underlying type T, an explicit specialization of `ConversionTraits<T>` must be provided. This + specialization must provide the following static methods: * `isUndefined(v)` -- returns a boolean indication whether `v` is undefined or a JSON null @@ -48,21 +65,239 @@ namespace conversion { * `toNumber(v)` -- returns `optional<float>`, absence indicating `v` is not a JSON number * `toDouble(v)` -- returns `optional<double>`, absence indicating `v` is not a JSON number * `toString(v)` -- returns `optional<std::string>`, absence indicating `v` is not a JSON string - * `toValue(v)` -- returns `optional<mbgl::Value>`, a variant type, for generic conversion, + * `toValue(v)` -- returns `optional<Value>`, a variant type, for generic conversion, absence indicating `v` is not a boolean, number, or string. Numbers should be converted to unsigned integer, signed integer, or floating point, in descending preference. - The mbgl core implements these requirements for RapidJSON types, and the node bindings implement - them for v8 types. + In addition, the type T must be move-constructable. And finally, `Convertible::Storage`, a typedef for + `std::aligned_storage_t`, must be large enough to satisfy the memory requirements for any of the + possible underlying types. (A static assert will fail if this is not the case.) + + `Convertible` itself is movable, but not copyable. A moved-from `Convertible` is in an invalid state; + you must not do anything with it except let it go out of scope. */ struct Error { std::string message; }; +template <typename T> +class ConversionTraits; + +class Convertible { +public: + template <typename T> + Convertible(T&& value) : vtable(vtableForType<std::decay_t<T>>()) { + static_assert(sizeof(Storage) >= sizeof(std::decay_t<T>), "Storage must be large enough to hold value type"); + new (static_cast<void*>(&storage)) std::decay_t<T>(std::forward<T>(value)); + } + + Convertible(Convertible&& v) + : vtable(v.vtable) + { + if (vtable) { + vtable->move(std::move(v.storage), this->storage); + } + } + + ~Convertible() { + if (vtable) { + vtable->destroy(storage); + } + } + + Convertible& operator=(Convertible&& v) { + if (vtable) { + vtable->destroy(storage); + } + vtable = v.vtable; + if (vtable) { + vtable->move(std::move(v.storage), this->storage); + } + v.vtable = nullptr; + return *this; + } + + Convertible() = delete; + Convertible(const Convertible&) = delete; + Convertible& operator=(const Convertible&) = delete; + + friend inline bool isUndefined(const Convertible& v) { + assert(v.vtable); + return v.vtable->isUndefined(v.storage); + } + + friend inline bool isArray(const Convertible& v) { + assert(v.vtable); + return v.vtable->isArray(v.storage); + } + + friend inline std::size_t arrayLength(const Convertible& v) { + assert(v.vtable); + return v.vtable->arrayLength(v.storage); + } + + friend inline Convertible arrayMember(const Convertible& v, std::size_t i) { + assert(v.vtable); + return v.vtable->arrayMember(v.storage, i); + } + + friend inline bool isObject(const Convertible& v) { + assert(v.vtable); + return v.vtable->isObject(v.storage); + } + + friend inline optional<Convertible> objectMember(const Convertible& v, const char * name) { + assert(v.vtable); + return v.vtable->objectMember(v.storage, name); + } + + friend inline optional<Error> eachMember(const Convertible& v, const std::function<optional<Error> (const std::string&, const Convertible&)>& fn) { + assert(v.vtable); + return v.vtable->eachMember(v.storage, fn); + } + + friend inline optional<bool> toBool(const Convertible& v) { + assert(v.vtable); + return v.vtable->toBool(v.storage); + } + + friend inline optional<float> toNumber(const Convertible& v) { + assert(v.vtable); + return v.vtable->toNumber(v.storage); + } + + friend inline optional<double> toDouble(const Convertible& v) { + assert(v.vtable); + return v.vtable->toDouble(v.storage); + } + + friend inline optional<std::string> toString(const Convertible& v) { + assert(v.vtable); + return v.vtable->toString(v.storage); + } + + friend inline optional<Value> toValue(const Convertible& v) { + assert(v.vtable); + return v.vtable->toValue(v.storage); + } + + friend inline optional<GeoJSON> toGeoJSON(const Convertible& v, Error& error) { + assert(v.vtable); + return v.vtable->toGeoJSON(v.storage, error); + } + +private: +#if __ANDROID__ + // Android: JSValue* or mbgl::android::Value + using Storage = std::aligned_storage_t<32, 8>; +#elif __QT__ + // Qt: JSValue* or QVariant + using Storage = std::aligned_storage_t<32, 8>; +#else + // Node: JSValue* or v8::Local<v8::Value> + // iOS/macOS: JSValue* or id + using Storage = std::aligned_storage_t<8, 8>; +#endif + + struct VTable { + void (*move) (Storage&& src, Storage& dest); + void (*destroy) (Storage&); + + bool (*isUndefined) (const Storage&); + + bool (*isArray) (const Storage&); + std::size_t (*arrayLength) (const Storage&); + Convertible (*arrayMember) (const Storage&, std::size_t); + + bool (*isObject) (const Storage&); + optional<Convertible> (*objectMember) (const Storage&, const char *); + optional<Error> (*eachMember) (const Storage&, const std::function<optional<Error> (const std::string&, const Convertible&)>&); + + optional<bool> (*toBool) (const Storage&); + optional<float> (*toNumber) (const Storage&); + optional<double> (*toDouble) (const Storage&); + optional<std::string> (*toString) (const Storage&); + optional<Value> (*toValue) (const Storage&); + + // https://github.com/mapbox/mapbox-gl-native/issues/5623 + optional<GeoJSON> (*toGeoJSON) (const Storage&, Error&); + }; + + // Extracted this function from the table below to work around a GCC bug with differing + // visibility settings for capturing lambdas: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80947 + template <typename T> + static auto vtableEachMember(const Storage& s, const std::function<optional<Error>(const std::string&, const Convertible&)>& fn) { + return ConversionTraits<T>::eachMember(reinterpret_cast<const T&>(s), [&](const std::string& k, T&& v) { + return fn(k, Convertible(std::move(v))); + }); + } + + template <typename T> + static VTable* vtableForType() { + using Traits = ConversionTraits<T>; + static VTable vtable = { + [] (Storage&& src, Storage& dest) { + auto srcValue = reinterpret_cast<T&&>(src); + new (static_cast<void*>(&dest)) T(std::move(srcValue)); + srcValue.~T(); + }, + [] (Storage& s) { + reinterpret_cast<T&>(s).~T(); + }, + [] (const Storage& s) { + return Traits::isUndefined(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::isArray(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::arrayLength(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s, std::size_t i) { + return Convertible(Traits::arrayMember(reinterpret_cast<const T&>(s), i)); + }, + [] (const Storage& s) { + return Traits::isObject(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s, const char * key) { + optional<T> member = Traits::objectMember(reinterpret_cast<const T&>(s), key); + if (member) { + return optional<Convertible>(Convertible(std::move(*member))); + } else { + return optional<Convertible>(); + } + }, + vtableEachMember<T>, + [] (const Storage& s) { + return Traits::toBool(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::toNumber(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::toDouble(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::toString(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::toValue(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s, Error& err) { + return Traits::toGeoJSON(reinterpret_cast<const T&>(s), err); + } + }; + return &vtable; + } + + VTable* vtable; + Storage storage; +}; + template <class T, class Enable = void> struct Converter; -template <class T, class V, class...Args> -optional<T> convert(const V& value, Error& error, Args&&...args) { +template <class T, class...Args> +optional<T> convert(const Convertible& value, Error& error, Args&&...args) { return Converter<T>()(value, error, std::forward<Args>(args)...); } diff --git a/include/mbgl/style/conversion/constant.hpp b/include/mbgl/style/conversion/constant.hpp index 07c0a35fae..7b3249da52 100644 --- a/include/mbgl/style/conversion/constant.hpp +++ b/include/mbgl/style/conversion/constant.hpp @@ -1,7 +1,6 @@ #pragma once #include <mbgl/style/conversion.hpp> -#include <mbgl/util/optional.hpp> #include <mbgl/util/color.hpp> #include <mbgl/util/enum.hpp> #include <mbgl/util/string.hpp> @@ -16,47 +15,22 @@ namespace conversion { template <> struct Converter<bool> { - template <class V> - optional<bool> operator()(const V& value, Error& error) const { - optional<bool> converted = toBool(value); - if (!converted) { - error = { "value must be a boolean" }; - return {}; - } - return *converted; - } + optional<bool> operator()(const Convertible& value, Error& error) const; }; template <> struct Converter<float> { - template <class V> - optional<float> operator()(const V& value, Error& error) const { - optional<float> converted = toNumber(value); - if (!converted) { - error = { "value must be a number" }; - return {}; - } - return *converted; - } + optional<float> operator()(const Convertible& value, Error& error) const; }; template <> struct Converter<std::string> { - template <class V> - optional<std::string> operator()(const V& value, Error& error) const { - optional<std::string> converted = toString(value); - if (!converted) { - error = { "value must be a string" }; - return {}; - } - return *converted; - } + optional<std::string> operator()(const Convertible& value, Error& error) const; }; template <class T> struct Converter<T, typename std::enable_if_t<std::is_enum<T>::value>> { - template <class V> - optional<T> operator()(const V& value, Error& error) const { + optional<T> operator()(const Convertible& value, Error& error) const { optional<std::string> string = toString(value); if (!string) { error = { "value must be a string" }; @@ -75,28 +49,12 @@ struct Converter<T, typename std::enable_if_t<std::is_enum<T>::value>> { template <> struct Converter<Color> { - template <class V> - optional<Color> operator()(const V& value, Error& error) const { - optional<std::string> string = toString(value); - if (!string) { - error = { "value must be a string" }; - return {}; - } - - optional<Color> color = Color::parse(*string); - if (!color) { - error = { "value must be a valid color" }; - return {}; - } - - return *color; - } + optional<Color> operator()(const Convertible& value, Error& error) const; }; template <size_t N> struct Converter<std::array<float, N>> { - template <class V> - optional<std::array<float, N>> operator()(const V& value, Error& error) const { + optional<std::array<float, N>> operator()(const Convertible& value, Error& error) const { if (!isArray(value) || arrayLength(value) != N) { error = { "value must be an array of " + util::toString(N) + " numbers" }; return {}; @@ -117,52 +75,12 @@ struct Converter<std::array<float, N>> { template <> struct Converter<std::vector<float>> { - template <class V> - optional<std::vector<float>> operator()(const V& value, Error& error) const { - if (!isArray(value)) { - error = { "value must be an array" }; - return {}; - } - - std::vector<float> result; - result.reserve(arrayLength(value)); - - for (std::size_t i = 0; i < arrayLength(value); ++i) { - optional<float> number = toNumber(arrayMember(value, i)); - if (!number) { - error = { "value must be an array of numbers" }; - return {}; - } - result.push_back(*number); - } - - return result; - } + optional<std::vector<float>> operator()(const Convertible& value, Error& error) const; }; template <> struct Converter<std::vector<std::string>> { - template <class V> - optional<std::vector<std::string>> operator()(const V& value, Error& error) const { - if (!isArray(value)) { - error = { "value must be an array" }; - return {}; - } - - std::vector<std::string> result; - result.reserve(arrayLength(value)); - - for (std::size_t i = 0; i < arrayLength(value); ++i) { - optional<std::string> string = toString(arrayMember(value, i)); - if (!string) { - error = { "value must be an array of strings" }; - return {}; - } - result.push_back(*string); - } - - return result; - } + optional<std::vector<std::string>> operator()(const Convertible& value, Error& error) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/coordinate.hpp b/include/mbgl/style/conversion/coordinate.hpp index 732624e77f..e11db5e32f 100644 --- a/include/mbgl/style/conversion/coordinate.hpp +++ b/include/mbgl/style/conversion/coordinate.hpp @@ -10,26 +10,7 @@ namespace conversion { template<> struct Converter<LatLng> { public: - template <class V> - optional<LatLng> operator() (const V& value, Error& error) const { - if (!isArray(value) || arrayLength(value) < 2 ) { - error = { "coordinate array must contain numeric longitude and latitude values" }; - return {}; - } - //Style spec uses GeoJSON convention for specifying coordinates - optional<double> latitude = toDouble(arrayMember(value, 1)); - optional<double> longitude = toDouble(arrayMember(value, 0)); - - if (!latitude || !longitude) { - error = { "coordinate array must contain numeric longitude and latitude values" }; - return {}; - } - if (*latitude < -90 || *latitude > 90 ){ - error = { "coordinate latitude must be between -90 and 90" }; - return {}; - } - return LatLng(*latitude, *longitude); - } + optional<LatLng> operator() (const Convertible& value, Error& error) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/custom_geometry_source_options.hpp b/include/mbgl/style/conversion/custom_geometry_source_options.hpp new file mode 100644 index 0000000000..73b141e799 --- /dev/null +++ b/include/mbgl/style/conversion/custom_geometry_source_options.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/sources/custom_geometry_source.hpp> + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter<CustomGeometrySource::Options> { + + template <class V> + optional<CustomGeometrySource::Options> operator()(const V& value, Error& error) const { + CustomGeometrySource::Options options; + + const auto minzoomValue = objectMember(value, "minzoom"); + if (minzoomValue) { + if (toNumber(*minzoomValue)) { + options.zoomRange.min = static_cast<uint8_t>(*toNumber(*minzoomValue)); + } else { + error = { "GeoJSON source minzoom value must be a number" }; + return {}; + } + } + + const auto maxzoomValue = objectMember(value, "maxzoom"); + if (maxzoomValue) { + if (toNumber(*maxzoomValue)) { + options.zoomRange.max = static_cast<uint8_t>(*toNumber(*maxzoomValue)); + } else { + error = { "GeoJSON source maxzoom value must be a number" }; + return {}; + } + } + + const auto bufferValue = objectMember(value, "buffer"); + if (bufferValue) { + if (toNumber(*bufferValue)) { + options.tileOptions.buffer = static_cast<uint16_t>(*toNumber(*bufferValue)); + } else { + error = { "GeoJSON source buffer value must be a number" }; + return {}; + } + } + + const auto toleranceValue = objectMember(value, "tolerance"); + if (toleranceValue) { + if (toNumber(*toleranceValue)) { + options.tileOptions.tolerance = static_cast<double>(*toNumber(*toleranceValue)); + } else { + error = { "GeoJSON source tolerance value must be a number" }; + return {}; + } + } + + return { options }; + } + +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/conversion/data_driven_property_value.hpp b/include/mbgl/style/conversion/data_driven_property_value.hpp index 79b15dcfb0..8880d28fb1 100644 --- a/include/mbgl/style/conversion/data_driven_property_value.hpp +++ b/include/mbgl/style/conversion/data_driven_property_value.hpp @@ -4,6 +4,13 @@ #include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/constant.hpp> #include <mbgl/style/conversion/function.hpp> +#include <mbgl/style/conversion/expression.hpp> +#include <mbgl/style/expression/is_expression.hpp> +#include <mbgl/style/expression/is_constant.hpp> +#include <mbgl/style/expression/find_zoom_curve.hpp> + +#include <unordered_set> + namespace mbgl { namespace style { @@ -11,10 +18,27 @@ namespace conversion { template <class T> struct Converter<DataDrivenPropertyValue<T>> { - template <class V> - optional<DataDrivenPropertyValue<T>> operator()(const V& value, Error& error) const { + + optional<DataDrivenPropertyValue<T>> operator()(const Convertible& value, Error& error) const { if (isUndefined(value)) { return DataDrivenPropertyValue<T>(); + } else if (expression::isExpression(value)) { + optional<std::unique_ptr<Expression>> expression = convert<std::unique_ptr<Expression>>( + value, + error, + valueTypeToExpressionType<T>()); + + if (!expression) { + return {}; + } + + if (isFeatureConstant(**expression)) { + return DataDrivenPropertyValue<T>(CameraFunction<T>(std::move(*expression))); + } else if (isZoomConstant(**expression)) { + return DataDrivenPropertyValue<T>(SourceFunction<T>(std::move(*expression))); + } else { + return DataDrivenPropertyValue<T>(CompositeFunction<T>(std::move(*expression))); + } } else if (!isObject(value)) { optional<T> constant = convert<T>(value, error); if (!constant) { diff --git a/include/mbgl/style/conversion/expression.hpp b/include/mbgl/style/conversion/expression.hpp new file mode 100644 index 0000000000..c5fcf906a7 --- /dev/null +++ b/include/mbgl/style/conversion/expression.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/expression/type.hpp> +#include <mbgl/style/conversion.hpp> + +#include <memory> + +namespace mbgl { +namespace style { +namespace conversion { + +using namespace mbgl::style::expression; + +template<> struct Converter<std::unique_ptr<Expression>> { + optional<std::unique_ptr<Expression>> operator()(const Convertible& value, Error& error, type::Type expected) const { + ParsingContext ctx(optional<type::Type> {expected}); + ParseResult parsed = ctx.parse(value); + if (parsed) { + return std::move(*parsed); + } + std::string combinedError; + for (const ParsingError& parsingError : ctx.getErrors()) { + if (combinedError.size() > 0) { + combinedError += "\n"; + } + if (parsingError.key.size() > 0) { + combinedError += parsingError.key + ": "; + } + combinedError += parsingError.message; + } + error = { combinedError }; + return {}; + }; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/conversion/filter.hpp b/include/mbgl/style/conversion/filter.hpp index 986d1bf80d..9daf6ea7a4 100644 --- a/include/mbgl/style/conversion/filter.hpp +++ b/include/mbgl/style/conversion/filter.hpp @@ -2,7 +2,6 @@ #include <mbgl/style/filter.hpp> #include <mbgl/style/conversion.hpp> -#include <mbgl/util/geometry.hpp> namespace mbgl { namespace style { @@ -11,247 +10,7 @@ namespace conversion { template <> struct Converter<Filter> { public: - template <class V> - optional<Filter> operator()(const V& value, Error& error) const { - if (!isArray(value)) { - error = { "filter expression must be an array" }; - return {}; - } - - if (arrayLength(value) < 1) { - error = { "filter expression must have at least 1 element" }; - return {}; - } - - optional<std::string> op = toString(arrayMember(value, 0)); - if (!op) { - error = { "filter operator must be a string" }; - return {}; - } - - if (*op == "==") { - return convertEqualityFilter<EqualsFilter, TypeEqualsFilter, IdentifierEqualsFilter>(value, error); - } else if (*op == "!=") { - return convertEqualityFilter<NotEqualsFilter, TypeNotEqualsFilter, IdentifierNotEqualsFilter>(value, error); - } else if (*op == ">") { - return convertBinaryFilter<GreaterThanFilter>(value, error); - } else if (*op == ">=") { - return convertBinaryFilter<GreaterThanEqualsFilter>(value, error); - } else if (*op == "<") { - return convertBinaryFilter<LessThanFilter>(value, error); - } else if (*op == "<=") { - return convertBinaryFilter<LessThanEqualsFilter>(value, error); - } else if (*op == "in") { - return convertSetFilter<InFilter, TypeInFilter, IdentifierInFilter>(value, error); - } else if (*op == "!in") { - return convertSetFilter<NotInFilter, TypeNotInFilter, IdentifierNotInFilter>(value, error); - } else if (*op == "all") { - return convertCompoundFilter<AllFilter>(value, error); - } else if (*op == "any") { - return convertCompoundFilter<AnyFilter>(value, error); - } else if (*op == "none") { - return convertCompoundFilter<NoneFilter>(value, error); - } else if (*op == "has") { - return convertUnaryFilter<HasFilter, HasIdentifierFilter>(value, error); - } else if (*op == "!has") { - return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value, error); - } - - error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" }; - return {}; - } - -private: - optional<Value> normalizeValue(const optional<Value>& value, Error& error) const { - if (!value) { - error = { "filter expression value must be a boolean, number, or string" }; - return {}; - } else { - return *value; - } - } - - template <class V> - optional<FeatureType> toFeatureType(const V& value, Error& error) const { - optional<std::string> type = toString(value); - if (!type) { - error = { "value for $type filter must be a string" }; - return {}; - } else if (*type == "Point") { - return FeatureType::Point; - } else if (*type == "LineString") { - return FeatureType::LineString; - } else if (*type == "Polygon") { - return FeatureType::Polygon; - } else { - error = { "value for $type filter must be Point, LineString, or Polygon" }; - return {}; - } - } - - template <class V> - optional<FeatureIdentifier> toFeatureIdentifier(const V& value, Error& error) const { - optional<Value> identifier = toValue(value); - if (!identifier) { - error = { "filter expression value must be a boolean, number, or string" }; - return {}; - } else { - return (*identifier).match( - [] (uint64_t t) -> optional<FeatureIdentifier> { return { t }; }, - [] ( int64_t t) -> optional<FeatureIdentifier> { return { t }; }, - [] ( double t) -> optional<FeatureIdentifier> { return { t }; }, - [] (const std::string& t) -> optional<FeatureIdentifier> { return { t }; }, - [&] (const auto&) -> optional<FeatureIdentifier> { - error = { "filter expression value must be a boolean, number, or string" }; - return {}; - }); - } - } - - template <class FilterType, class IdentifierFilterType, class V> - optional<Filter> convertUnaryFilter(const V& value, Error& error) const { - if (arrayLength(value) < 2) { - error = { "filter expression must have 2 elements" }; - return {}; - } - - optional<std::string> key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - if (*key == "$id") { - return { IdentifierFilterType {} }; - } else { - return { FilterType { *key } }; - } - } - - template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V> - optional<Filter> convertEqualityFilter(const V& value, Error& error) const { - if (arrayLength(value) < 3) { - error = { "filter expression must have 3 elements" }; - return {}; - } - - optional<std::string> key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - if (*key == "$type") { - optional<FeatureType> filterValue = toFeatureType(arrayMember(value, 2), error); - if (!filterValue) { - return {}; - } - - return { TypeFilterType { *filterValue } }; - - } else if (*key == "$id") { - optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, 2), error); - if (!filterValue) { - return {}; - } - - return { IdentifierFilterType { *filterValue } }; - - } else { - optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); - if (!filterValue) { - return {}; - } - - return { FilterType { *key, *filterValue } }; - } - } - - template <class FilterType, class V> - optional<Filter> convertBinaryFilter(const V& value, Error& error) const { - if (arrayLength(value) < 3) { - error = { "filter expression must have 3 elements" }; - return {}; - } - - optional<std::string> key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); - if (!filterValue) { - return {}; - } - - return { FilterType { *key, *filterValue } }; - } - - template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V> - optional<Filter> convertSetFilter(const V& value, Error& error) const { - if (arrayLength(value) < 2) { - error = { "filter expression must at least 2 elements" }; - return {}; - } - - optional<std::string> key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - if (*key == "$type") { - std::vector<FeatureType> values; - for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional<FeatureType> filterValue = toFeatureType(arrayMember(value, i), error); - if (!filterValue) { - return {}; - } - values.push_back(*filterValue); - } - - return { TypeFilterType { std::move(values) } }; - - } else if (*key == "$id") { - std::vector<FeatureIdentifier> values; - for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, i), error); - if (!filterValue) { - return {}; - } - values.push_back(*filterValue); - } - - return { IdentifierFilterType { std::move(values) } }; - - } else { - std::vector<Value> values; - for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, i)), error); - if (!filterValue) { - return {}; - } - values.push_back(*filterValue); - } - - return { FilterType { *key, std::move(values) } }; - } - } - - template <class FilterType, class V> - optional<Filter> convertCompoundFilter(const V& value, Error& error) const { - std::vector<Filter> filters; - for (std::size_t i = 1; i < arrayLength(value); ++i) { - optional<Filter> element = operator()(arrayMember(value, i), error); - if (!element) { - return {}; - } - filters.push_back(*element); - } - - return { FilterType { std::move(filters) } }; - } + optional<Filter> operator()(const Convertible& value, Error& error) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/function.hpp b/include/mbgl/style/conversion/function.hpp index bf5b27a9a6..e230884944 100644 --- a/include/mbgl/style/conversion/function.hpp +++ b/include/mbgl/style/conversion/function.hpp @@ -11,8 +11,8 @@ namespace mbgl { namespace style { namespace conversion { -template <class D, class R, class V> -optional<std::map<D, R>> convertStops(const V& value, Error& error) { +template <class D, class R> +optional<std::map<D, R>> convertStops(const Convertible& value, Error& error) { auto stopsValue = objectMember(value, "stops"); if (!stopsValue) { error = { "function value must specify stops" }; @@ -63,8 +63,7 @@ template <class T> struct Converter<ExponentialStops<T>> { static constexpr const char * type = "exponential"; - template <class V> - optional<ExponentialStops<T>> operator()(const V& value, Error& error) const { + optional<ExponentialStops<T>> operator()(const Convertible& value, Error& error) const { auto stops = convertStops<float, T>(value, error); if (!stops) { return {}; @@ -89,8 +88,7 @@ template <class T> struct Converter<IntervalStops<T>> { static constexpr const char * type = "interval"; - template <class V> - optional<IntervalStops<T>> operator()(const V& value, Error& error) const { + optional<IntervalStops<T>> operator()(const Convertible& value, Error& error) const { auto stops = convertStops<float, T>(value, error); if (!stops) { return {}; @@ -101,8 +99,7 @@ struct Converter<IntervalStops<T>> { template <> struct Converter<CategoricalValue> { - template <class V> - optional<CategoricalValue> operator()(const V& value, Error& error) const { + optional<CategoricalValue> operator()(const Convertible& value, Error& error) const { auto b = toBool(value); if (b) { return { *b }; @@ -127,8 +124,7 @@ template <class T> struct Converter<CategoricalStops<T>> { static constexpr const char * type = "categorical"; - template <class V> - optional<CategoricalStops<T>> operator()(const V& value, Error& error) const { + optional<CategoricalStops<T>> operator()(const Convertible& value, Error& error) const { auto stops = convertStops<CategoricalValue, T>(value, error); if (!stops) { return {}; @@ -142,8 +138,7 @@ template <class T> struct Converter<IdentityStops<T>> { static constexpr const char * type = "identity"; - template <class V> - optional<IdentityStops<T>> operator()(const V&, Error&) const { + optional<IdentityStops<T>> operator()(const Convertible&, Error&) const { return IdentityStops<T>(); } }; @@ -154,9 +149,8 @@ struct StopsConverter; template <class T, class... Ts> struct StopsConverter<T, variant<Ts...>> { public: - template <class V> - optional<variant<Ts...>> operator()(const V& value, Error& error) const { - std::string type = util::Interpolatable<T> ? "exponential" : "interval"; + optional<variant<Ts...>> operator()(const Convertible& value, Error& error) const { + std::string type = util::Interpolatable<T>::value ? "exponential" : "interval"; auto typeValue = objectMember(value, "type"); if (typeValue && toString(*typeValue)) { @@ -193,8 +187,7 @@ public: template <class T> struct Converter<CameraFunction<T>> { - template <class V> - optional<CameraFunction<T>> operator()(const V& value, Error& error) const { + optional<CameraFunction<T>> operator()(const Convertible& value, Error& error) const { if (!isObject(value)) { error = { "function must be an object" }; return {}; @@ -209,8 +202,8 @@ struct Converter<CameraFunction<T>> { } }; -template <class T, class V> -optional<optional<T>> convertDefaultValue(const V& value, Error& error) { +template <class T> +optional<optional<T>> convertDefaultValue(const Convertible& value, Error& error) { auto defaultValueValue = objectMember(value, "default"); if (!defaultValueValue) { return optional<T>(); @@ -227,8 +220,7 @@ optional<optional<T>> convertDefaultValue(const V& value, Error& error) { template <class T> struct Converter<SourceFunction<T>> { - template <class V> - optional<SourceFunction<T>> operator()(const V& value, Error& error) const { + optional<SourceFunction<T>> operator()(const Convertible& value, Error& error) const { if (!isObject(value)) { error = { "function must be an object" }; return {}; @@ -267,8 +259,7 @@ struct CompositeValue : std::pair<float, S> { template <class S> struct Converter<CompositeValue<S>> { - template <class V> - optional<CompositeValue<S>> operator()(const V& value, Error& error) const { + optional<CompositeValue<S>> operator()(const Convertible& value, Error& error) const { if (!isObject(value)) { error = { "stop must be an object" }; return {}; @@ -304,8 +295,7 @@ template <class T> struct Converter<CompositeExponentialStops<T>> { static constexpr const char * type = "exponential"; - template <class V> - optional<CompositeExponentialStops<T>> operator()(const V& value, Error& error) const { + optional<CompositeExponentialStops<T>> operator()(const Convertible& value, Error& error) const { auto stops = convertStops<CompositeValue<float>, T>(value, error); if (!stops) { return {}; @@ -330,8 +320,7 @@ template <class T> struct Converter<CompositeIntervalStops<T>> { static constexpr const char * type = "interval"; - template <class V> - optional<CompositeIntervalStops<T>> operator()(const V& value, Error& error) const { + optional<CompositeIntervalStops<T>> operator()(const Convertible& value, Error& error) const { auto stops = convertStops<CompositeValue<float>, T>(value, error); if (!stops) { return {}; @@ -350,8 +339,7 @@ template <class T> struct Converter<CompositeCategoricalStops<T>> { static constexpr const char * type = "categorical"; - template <class V> - optional<CompositeCategoricalStops<T>> operator()(const V& value, Error& error) const { + optional<CompositeCategoricalStops<T>> operator()(const Convertible& value, Error& error) const { auto stops = convertStops<CompositeValue<CategoricalValue>, T>(value, error); if (!stops) { return {}; @@ -368,8 +356,7 @@ struct Converter<CompositeCategoricalStops<T>> { template <class T> struct Converter<CompositeFunction<T>> { - template <class V> - optional<CompositeFunction<T>> operator()(const V& value, Error& error) const { + optional<CompositeFunction<T>> operator()(const Convertible& value, Error& error) const { if (!isObject(value)) { error = { "function must be an object" }; return {}; diff --git a/include/mbgl/style/conversion/geojson.hpp b/include/mbgl/style/conversion/geojson.hpp index 0b594f066c..403c5f953b 100644 --- a/include/mbgl/style/conversion/geojson.hpp +++ b/include/mbgl/style/conversion/geojson.hpp @@ -7,15 +7,13 @@ namespace mbgl { namespace style { namespace conversion { +// Workaround until https://github.com/mapbox/mapbox-gl-native/issues/5623 is done. +optional<GeoJSON> parseGeoJSON(const std::string&, Error&); + template <> struct Converter<GeoJSON> { public: - optional<GeoJSON> operator()(const std::string&, Error&) const; - - // This is explicitly specialized in the .cpp file for JSValue. It may also be explicitly - // specialized for SDK-specific types (e.g. mbgl::android::Value). - template <class V> - optional<GeoJSON> operator()(const V&, Error&) const; + optional<GeoJSON> operator()(const Convertible&, Error&) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/geojson_options.hpp b/include/mbgl/style/conversion/geojson_options.hpp index 19383d90ce..3f625babb6 100644 --- a/include/mbgl/style/conversion/geojson_options.hpp +++ b/include/mbgl/style/conversion/geojson_options.hpp @@ -9,74 +9,7 @@ namespace conversion { template <> struct Converter<GeoJSONOptions> { - - template <class V> - optional<GeoJSONOptions> operator()(const V& value, Error& error) const { - GeoJSONOptions options; - - const auto maxzoomValue = objectMember(value, "maxzoom"); - if (maxzoomValue) { - if (toNumber(*maxzoomValue)) { - options.maxzoom = static_cast<uint8_t>(*toNumber(*maxzoomValue)); - } else { - error = { "GeoJSON source maxzoom value must be a number" }; - return {}; - } - } - - const auto bufferValue = objectMember(value, "buffer"); - if (bufferValue) { - if (toNumber(*bufferValue)) { - options.buffer = static_cast<uint16_t>(*toNumber(*bufferValue)); - } else { - error = { "GeoJSON source buffer value must be a number" }; - return {}; - } - } - - const auto toleranceValue = objectMember(value, "tolerance"); - if (toleranceValue) { - if (toNumber(*toleranceValue)) { - options.tolerance = static_cast<double>(*toNumber(*toleranceValue)); - } else { - error = { "GeoJSON source tolerance value must be a number" }; - return {}; - } - } - - const auto clusterValue = objectMember(value, "cluster"); - if (clusterValue) { - if (toBool(*clusterValue)) { - options.cluster = *toBool(*clusterValue); - } else { - error = { "GeoJSON source cluster value must be a boolean" }; - return {}; - } - } - - const auto clusterMaxZoomValue = objectMember(value, "clusterMaxZoom"); - if (clusterMaxZoomValue) { - if (toNumber(*clusterMaxZoomValue)) { - options.clusterMaxZoom = static_cast<uint8_t>(*toNumber(*clusterMaxZoomValue)); - } else { - error = { "GeoJSON source clusterMaxZoom value must be a number" }; - return {}; - } - } - - const auto clusterRadiusValue = objectMember(value, "clusterRadius"); - if (clusterRadiusValue) { - if (toNumber(*clusterRadiusValue)) { - options.clusterRadius = static_cast<double>(*toNumber(*clusterRadiusValue)); - } else { - error = { "GeoJSON source clusterRadius value must be a number" }; - return {}; - } - } - - return { options }; - } - + optional<GeoJSONOptions> operator()(const Convertible& value, Error& error) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/get_json_type.hpp b/include/mbgl/style/conversion/get_json_type.hpp new file mode 100644 index 0000000000..f7efebccce --- /dev/null +++ b/include/mbgl/style/conversion/get_json_type.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include <mbgl/style/conversion.hpp> +#include <string> + +namespace mbgl { +namespace style { +namespace conversion { + +std::string getJSONType(const Convertible& value); + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/conversion/layer.hpp b/include/mbgl/style/conversion/layer.hpp index 1fe467165d..1c0e2e2f07 100644 --- a/include/mbgl/style/conversion/layer.hpp +++ b/include/mbgl/style/conversion/layer.hpp @@ -1,220 +1,24 @@ #pragma once #include <mbgl/style/layer.hpp> -#include <mbgl/style/layers/background_layer.hpp> -#include <mbgl/style/layers/circle_layer.hpp> -#include <mbgl/style/layers/fill_layer.hpp> -#include <mbgl/style/layers/fill_extrusion_layer.hpp> -#include <mbgl/style/layers/line_layer.hpp> -#include <mbgl/style/layers/raster_layer.hpp> -#include <mbgl/style/layers/symbol_layer.hpp> #include <mbgl/style/conversion.hpp> -#include <mbgl/style/conversion/constant.hpp> -#include <mbgl/style/conversion/filter.hpp> -#include <mbgl/style/conversion/make_property_setters.hpp> + +#include <memory> namespace mbgl { namespace style { namespace conversion { -template <class V> -optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const V& value) { - static const auto setters = makeLayoutPropertySetters<V>(); - auto it = setters.find(name); - if (it == setters.end()) { - return Error { "property not found" }; - } - return it->second(layer, value); -} - -template <class V> -optional<Error> setPaintProperty(Layer& layer, const std::string& name, const V& value) { - static const auto setters = makePaintPropertySetters<V>(); - auto it = setters.find(name); - if (it == setters.end()) { - return Error { "property not found" }; - } - return it->second(layer, value); -} - -template <class V> -optional<Error> setPaintProperties(Layer& layer, const V& value) { - auto paintValue = objectMember(value, "paint"); - if (!paintValue) { - return {}; - } - return eachMember(*paintValue, [&] (const std::string& k, const V& v) { - return setPaintProperty(layer, k, v); - }); -} - template <> struct Converter<std::unique_ptr<Layer>> { public: - template <class V> - optional<std::unique_ptr<Layer>> operator()(const V& value, Error& error) const { - if (!isObject(value)) { - error = { "layer must be an object" }; - return {}; - } - - auto idValue = objectMember(value, "id"); - if (!idValue) { - error = { "layer must have an id" }; - return {}; - } - - optional<std::string> id = toString(*idValue); - if (!id) { - error = { "layer id must be a string" }; - return {}; - } - - auto typeValue = objectMember(value, "type"); - if (!typeValue) { - error = { "layer must have a type" }; - return {}; - } - - optional<std::string> type = toString(*typeValue); - if (!type) { - error = { "layer type must be a string" }; - return {}; - } - - optional<std::unique_ptr<Layer>> converted; - - if (*type == "fill") { - converted = convertVectorLayer<FillLayer>(*id, value, error); - } else if (*type == "fill-extrusion") { - converted = convertVectorLayer<FillExtrusionLayer>(*id, value, error); - } else if (*type == "line") { - converted = convertVectorLayer<LineLayer>(*id, value, error); - } else if (*type == "circle") { - converted = convertVectorLayer<CircleLayer>(*id, value, error); - } else if (*type == "symbol") { - converted = convertVectorLayer<SymbolLayer>(*id, value, error); - } else if (*type == "raster") { - converted = convertRasterLayer(*id, value, error); - } else if (*type == "background") { - converted = convertBackgroundLayer(*id, value, error); - } else { - error = { "invalid layer type" }; - return {}; - } - - if (!converted) { - return converted; - } - - std::unique_ptr<Layer> layer = std::move(*converted); - - auto minzoomValue = objectMember(value, "minzoom"); - if (minzoomValue) { - optional<float> minzoom = toNumber(*minzoomValue); - if (!minzoom) { - error = { "minzoom must be numeric" }; - return {}; - } - layer->setMinZoom(*minzoom); - } - - auto maxzoomValue = objectMember(value, "maxzoom"); - if (maxzoomValue) { - optional<float> maxzoom = toNumber(*maxzoomValue); - if (!maxzoom) { - error = { "maxzoom must be numeric" }; - return {}; - } - layer->setMaxZoom(*maxzoom); - } - - auto layoutValue = objectMember(value, "layout"); - if (layoutValue) { - if (!isObject(*layoutValue)) { - error = { "layout must be an object" }; - return {}; - } - optional<Error> error_ = eachMember(*layoutValue, [&] (const std::string& k, const V& v) { - return setLayoutProperty(*layer, k, v); - }); - if (error_) { - error = *error_; - return {}; - } - } - - optional<Error> error_ = setPaintProperties(*layer, value); - if (error_) { - error = *error_; - return {}; - } - - return std::move(layer); - } - -private: - template <class LayerType, class V> - optional<std::unique_ptr<Layer>> convertVectorLayer(const std::string& id, const V& value, Error& error) const { - auto sourceValue = objectMember(value, "source"); - if (!sourceValue) { - error = { "layer must have a source" }; - return {}; - } - - optional<std::string> source = toString(*sourceValue); - if (!source) { - error = { "layer source must be a string" }; - return {}; - } - - std::unique_ptr<LayerType> layer = std::make_unique<LayerType>(id, *source); - - auto sourceLayerValue = objectMember(value, "source-layer"); - if (sourceLayerValue) { - optional<std::string> sourceLayer = toString(*sourceLayerValue); - if (!sourceLayer) { - error = { "layer source-layer must be a string" }; - return {}; - } - layer->setSourceLayer(*sourceLayer); - } - - auto filterValue = objectMember(value, "filter"); - if (filterValue) { - optional<Filter> filter = convert<Filter>(*filterValue, error); - if (!filter) { - return {}; - } - layer->setFilter(*filter); - } - - return { std::move(layer) }; - } - - template <class V> - optional<std::unique_ptr<Layer>> convertRasterLayer(const std::string& id, const V& value, Error& error) const { - auto sourceValue = objectMember(value, "source"); - if (!sourceValue) { - error = { "layer must have a source" }; - return {}; - } - - optional<std::string> source = toString(*sourceValue); - if (!source) { - error = { "layer source must be a string" }; - return {}; - } - - return { std::make_unique<RasterLayer>(id, *source) }; - } - - template <class V> - optional<std::unique_ptr<Layer>> convertBackgroundLayer(const std::string& id, const V&, Error&) const { - return { std::make_unique<BackgroundLayer>(id) }; - } + optional<std::unique_ptr<Layer>> operator()(const Convertible& value, Error& error) const; }; +optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const Convertible& value); +optional<Error> setPaintProperty(Layer& layer, const std::string& name, const Convertible& value); +optional<Error> setPaintProperties(Layer& layer, const Convertible& value); + } // namespace conversion } // namespace style } // namespace mbgl diff --git a/include/mbgl/style/conversion/light.hpp b/include/mbgl/style/conversion/light.hpp index ba162516c0..289fca2e31 100644 --- a/include/mbgl/style/conversion/light.hpp +++ b/include/mbgl/style/conversion/light.hpp @@ -2,9 +2,6 @@ #include <mbgl/style/light.hpp> #include <mbgl/style/conversion.hpp> -#include <mbgl/style/conversion/position.hpp> -#include <mbgl/style/conversion/property_value.hpp> -#include <mbgl/style/conversion/transition_options.hpp> namespace mbgl { namespace style { @@ -13,108 +10,7 @@ namespace conversion { template <> struct Converter<Light> { public: - template <class V> - optional<Light> operator()(const V& value, Error& error) const { - if (!isObject(value)) { - error = { "light must be an object" }; - return {}; - } - - Light light; - - const auto anchor = objectMember(value, "anchor"); - if (anchor) { - optional<PropertyValue<LightAnchorType>> convertedAnchor = - convert<PropertyValue<LightAnchorType>>(*anchor, error); - - if (convertedAnchor) { - light.setAnchor(*convertedAnchor); - } else { - return {}; - } - } - - const auto anchorTransition = objectMember(value, "anchor-transition"); - if (anchorTransition) { - optional<TransitionOptions> transition = - convert<TransitionOptions>(*anchorTransition, error); - if (transition) { - light.setAnchorTransition(*transition); - } else { - return {}; - } - } - - const auto color = objectMember(value, "color"); - if (color) { - optional<PropertyValue<Color>> convertedColor = - convert<PropertyValue<Color>>(*color, error); - - if (convertedColor) { - light.setColor(*convertedColor); - } else { - return {}; - } - } - - const auto colorTransition = objectMember(value, "color-transition"); - if (colorTransition) { - optional<TransitionOptions> transition = - convert<TransitionOptions>(*colorTransition, error); - if (transition) { - light.setColorTransition(*transition); - } else { - return {}; - } - } - - const auto position = objectMember(value, "position"); - if (position) { - optional<PropertyValue<Position>> convertedPosition = - convert<PropertyValue<Position>>(*position, error); - - if (convertedPosition) { - light.setPosition(*convertedPosition); - } else { - return {}; - } - } - - const auto positionTransition = objectMember(value, "position-transition"); - if (positionTransition) { - optional<TransitionOptions> transition = - convert<TransitionOptions>(*positionTransition, error); - if (transition) { - light.setPositionTransition(*transition); - } else { - return {}; - } - } - - const auto intensity = objectMember(value, "intensity"); - if (intensity) { - optional<PropertyValue<float>> convertedIntensity = - convert<PropertyValue<float>>(*intensity, error); - - if (convertedIntensity) { - light.setIntensity(*convertedIntensity); - } else { - return {}; - } - } - - const auto intensityTransition = objectMember(value, "intensity-transition"); - if (intensityTransition) { - optional<TransitionOptions> transition = - convert<TransitionOptions>(*intensityTransition, error); - if (transition) { - light.setIntensityTransition(*transition); - } else { - return {}; - } - } - return { std::move(light) }; - }; + optional<Light> operator()(const Convertible& value, Error& error) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/make_property_setters.hpp b/include/mbgl/style/conversion/make_property_setters.hpp deleted file mode 100644 index eaea2e89dc..0000000000 --- a/include/mbgl/style/conversion/make_property_setters.hpp +++ /dev/null @@ -1,207 +0,0 @@ -#pragma once - -// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`. - -#include <mbgl/style/conversion/property_setter.hpp> - -#include <mbgl/style/layers/fill_layer.hpp> -#include <mbgl/style/layers/line_layer.hpp> -#include <mbgl/style/layers/symbol_layer.hpp> -#include <mbgl/style/layers/circle_layer.hpp> -#include <mbgl/style/layers/fill_extrusion_layer.hpp> -#include <mbgl/style/layers/raster_layer.hpp> -#include <mbgl/style/layers/background_layer.hpp> - -#include <unordered_map> - -namespace mbgl { -namespace style { -namespace conversion { - -template <class V> -auto makeLayoutPropertySetters() { - std::unordered_map<std::string, PropertySetter<V>> result; - - result["visibility"] = &setVisibility<V>; - - - result["line-cap"] = &setProperty<V, LineLayer, PropertyValue<LineCapType>, &LineLayer::setLineCap>; - result["line-join"] = &setProperty<V, LineLayer, PropertyValue<LineJoinType>, &LineLayer::setLineJoin>; - result["line-miter-limit"] = &setProperty<V, LineLayer, PropertyValue<float>, &LineLayer::setLineMiterLimit>; - result["line-round-limit"] = &setProperty<V, LineLayer, PropertyValue<float>, &LineLayer::setLineRoundLimit>; - - result["symbol-placement"] = &setProperty<V, SymbolLayer, PropertyValue<SymbolPlacementType>, &SymbolLayer::setSymbolPlacement>; - result["symbol-spacing"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setSymbolSpacing>; - result["symbol-avoid-edges"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setSymbolAvoidEdges>; - result["icon-allow-overlap"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconAllowOverlap>; - result["icon-ignore-placement"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconIgnorePlacement>; - result["icon-optional"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconOptional>; - result["icon-rotation-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconRotationAlignment>; - result["icon-size"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconSize>; - result["icon-text-fit"] = &setProperty<V, SymbolLayer, PropertyValue<IconTextFitType>, &SymbolLayer::setIconTextFit>; - result["icon-text-fit-padding"] = &setProperty<V, SymbolLayer, PropertyValue<std::array<float, 4>>, &SymbolLayer::setIconTextFitPadding>; - result["icon-image"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setIconImage>; - result["icon-rotate"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconRotate>; - result["icon-padding"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconPadding>; - result["icon-keep-upright"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconKeepUpright>; - result["icon-offset"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setIconOffset>; - result["text-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextPitchAlignment>; - result["text-rotation-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment>; - result["text-field"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField>; - result["text-font"] = &setProperty<V, SymbolLayer, PropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont>; - result["text-size"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextSize>; - result["text-max-width"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxWidth>; - result["text-line-height"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight>; - result["text-letter-spacing"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLetterSpacing>; - result["text-justify"] = &setProperty<V, SymbolLayer, PropertyValue<TextJustifyType>, &SymbolLayer::setTextJustify>; - result["text-anchor"] = &setProperty<V, SymbolLayer, PropertyValue<TextAnchorType>, &SymbolLayer::setTextAnchor>; - result["text-max-angle"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxAngle>; - result["text-rotate"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextRotate>; - result["text-padding"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextPadding>; - result["text-keep-upright"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextKeepUpright>; - result["text-transform"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<TextTransformType>, &SymbolLayer::setTextTransform>; - result["text-offset"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setTextOffset>; - result["text-allow-overlap"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextAllowOverlap>; - result["text-ignore-placement"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextIgnorePlacement>; - result["text-optional"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextOptional>; - - - - - - return result; -} - -template <class V> -auto makePaintPropertySetters() { - std::unordered_map<std::string, PropertySetter<V>> result; - - result["fill-antialias"] = &setProperty<V, FillLayer, PropertyValue<bool>, &FillLayer::setFillAntialias>; - result["fill-antialias-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillAntialiasTransition>; - result["fill-opacity"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<float>, &FillLayer::setFillOpacity>; - result["fill-opacity-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillOpacityTransition>; - result["fill-color"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillColor>; - result["fill-color-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillColorTransition>; - result["fill-outline-color"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillOutlineColor>; - result["fill-outline-color-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillOutlineColorTransition>; - result["fill-translate"] = &setProperty<V, FillLayer, PropertyValue<std::array<float, 2>>, &FillLayer::setFillTranslate>; - result["fill-translate-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillTranslateTransition>; - result["fill-translate-anchor"] = &setProperty<V, FillLayer, PropertyValue<TranslateAnchorType>, &FillLayer::setFillTranslateAnchor>; - result["fill-translate-anchor-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillTranslateAnchorTransition>; - result["fill-pattern"] = &setProperty<V, FillLayer, PropertyValue<std::string>, &FillLayer::setFillPattern>; - result["fill-pattern-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillPatternTransition>; - - result["line-opacity"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOpacity>; - result["line-opacity-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineOpacityTransition>; - result["line-color"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<Color>, &LineLayer::setLineColor>; - result["line-color-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineColorTransition>; - result["line-translate"] = &setProperty<V, LineLayer, PropertyValue<std::array<float, 2>>, &LineLayer::setLineTranslate>; - result["line-translate-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineTranslateTransition>; - result["line-translate-anchor"] = &setProperty<V, LineLayer, PropertyValue<TranslateAnchorType>, &LineLayer::setLineTranslateAnchor>; - result["line-translate-anchor-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineTranslateAnchorTransition>; - result["line-width"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineWidth>; - result["line-width-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineWidthTransition>; - result["line-gap-width"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineGapWidth>; - result["line-gap-width-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineGapWidthTransition>; - result["line-offset"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOffset>; - result["line-offset-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineOffsetTransition>; - result["line-blur"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineBlur>; - result["line-blur-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineBlurTransition>; - result["line-dasharray"] = &setProperty<V, LineLayer, PropertyValue<std::vector<float>>, &LineLayer::setLineDasharray>; - result["line-dasharray-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineDasharrayTransition>; - result["line-pattern"] = &setProperty<V, LineLayer, PropertyValue<std::string>, &LineLayer::setLinePattern>; - result["line-pattern-transition"] = &setTransition<V, LineLayer, &LineLayer::setLinePatternTransition>; - - result["icon-opacity"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconOpacity>; - result["icon-opacity-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconOpacityTransition>; - result["icon-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconColor>; - result["icon-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconColorTransition>; - result["icon-halo-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconHaloColor>; - result["icon-halo-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconHaloColorTransition>; - result["icon-halo-width"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloWidth>; - result["icon-halo-width-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconHaloWidthTransition>; - result["icon-halo-blur"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloBlur>; - result["icon-halo-blur-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconHaloBlurTransition>; - result["icon-translate"] = &setProperty<V, SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setIconTranslate>; - result["icon-translate-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconTranslateTransition>; - result["icon-translate-anchor"] = &setProperty<V, SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setIconTranslateAnchor>; - result["icon-translate-anchor-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconTranslateAnchorTransition>; - result["text-opacity"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextOpacity>; - result["text-opacity-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextOpacityTransition>; - result["text-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextColor>; - result["text-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextColorTransition>; - result["text-halo-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextHaloColor>; - result["text-halo-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextHaloColorTransition>; - result["text-halo-width"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloWidth>; - result["text-halo-width-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextHaloWidthTransition>; - result["text-halo-blur"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloBlur>; - result["text-halo-blur-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextHaloBlurTransition>; - result["text-translate"] = &setProperty<V, SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setTextTranslate>; - result["text-translate-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextTranslateTransition>; - result["text-translate-anchor"] = &setProperty<V, SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setTextTranslateAnchor>; - result["text-translate-anchor-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextTranslateAnchorTransition>; - - result["circle-radius"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleRadius>; - result["circle-radius-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleRadiusTransition>; - result["circle-color"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleColor>; - result["circle-color-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleColorTransition>; - result["circle-blur"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleBlur>; - result["circle-blur-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleBlurTransition>; - result["circle-opacity"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleOpacity>; - result["circle-opacity-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleOpacityTransition>; - result["circle-translate"] = &setProperty<V, CircleLayer, PropertyValue<std::array<float, 2>>, &CircleLayer::setCircleTranslate>; - result["circle-translate-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleTranslateTransition>; - result["circle-translate-anchor"] = &setProperty<V, CircleLayer, PropertyValue<TranslateAnchorType>, &CircleLayer::setCircleTranslateAnchor>; - result["circle-translate-anchor-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleTranslateAnchorTransition>; - result["circle-pitch-scale"] = &setProperty<V, CircleLayer, PropertyValue<CirclePitchScaleType>, &CircleLayer::setCirclePitchScale>; - result["circle-pitch-scale-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCirclePitchScaleTransition>; - result["circle-stroke-width"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeWidth>; - result["circle-stroke-width-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeWidthTransition>; - result["circle-stroke-color"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleStrokeColor>; - result["circle-stroke-color-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeColorTransition>; - result["circle-stroke-opacity"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeOpacity>; - result["circle-stroke-opacity-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeOpacityTransition>; - - result["fill-extrusion-opacity"] = &setProperty<V, FillExtrusionLayer, PropertyValue<float>, &FillExtrusionLayer::setFillExtrusionOpacity>; - result["fill-extrusion-opacity-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionOpacityTransition>; - result["fill-extrusion-color"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<Color>, &FillExtrusionLayer::setFillExtrusionColor>; - result["fill-extrusion-color-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionColorTransition>; - result["fill-extrusion-translate"] = &setProperty<V, FillExtrusionLayer, PropertyValue<std::array<float, 2>>, &FillExtrusionLayer::setFillExtrusionTranslate>; - result["fill-extrusion-translate-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateTransition>; - result["fill-extrusion-translate-anchor"] = &setProperty<V, FillExtrusionLayer, PropertyValue<TranslateAnchorType>, &FillExtrusionLayer::setFillExtrusionTranslateAnchor>; - result["fill-extrusion-translate-anchor-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateAnchorTransition>; - result["fill-extrusion-pattern"] = &setProperty<V, FillExtrusionLayer, PropertyValue<std::string>, &FillExtrusionLayer::setFillExtrusionPattern>; - result["fill-extrusion-pattern-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionPatternTransition>; - result["fill-extrusion-height"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionHeight>; - result["fill-extrusion-height-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionHeightTransition>; - result["fill-extrusion-base"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionBase>; - result["fill-extrusion-base-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionBaseTransition>; - - result["raster-opacity"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterOpacity>; - result["raster-opacity-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterOpacityTransition>; - result["raster-hue-rotate"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterHueRotate>; - result["raster-hue-rotate-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterHueRotateTransition>; - result["raster-brightness-min"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMin>; - result["raster-brightness-min-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterBrightnessMinTransition>; - result["raster-brightness-max"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMax>; - result["raster-brightness-max-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterBrightnessMaxTransition>; - result["raster-saturation"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterSaturation>; - result["raster-saturation-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterSaturationTransition>; - result["raster-contrast"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterContrast>; - result["raster-contrast-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterContrastTransition>; - result["raster-fade-duration"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterFadeDuration>; - result["raster-fade-duration-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterFadeDurationTransition>; - - result["background-color"] = &setProperty<V, BackgroundLayer, PropertyValue<Color>, &BackgroundLayer::setBackgroundColor>; - result["background-color-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundColorTransition>; - result["background-pattern"] = &setProperty<V, BackgroundLayer, PropertyValue<std::string>, &BackgroundLayer::setBackgroundPattern>; - result["background-pattern-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundPatternTransition>; - result["background-opacity"] = &setProperty<V, BackgroundLayer, PropertyValue<float>, &BackgroundLayer::setBackgroundOpacity>; - result["background-opacity-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundOpacityTransition>; - - return result; -} - -} // namespace conversion -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/conversion/make_property_setters.hpp.ejs b/include/mbgl/style/conversion/make_property_setters.hpp.ejs deleted file mode 100644 index 19c9f70538..0000000000 --- a/include/mbgl/style/conversion/make_property_setters.hpp.ejs +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`. - -#include <mbgl/style/conversion/property_setter.hpp> - -<% for (const layer of locals.layers) { -%> -#include <mbgl/style/layers/<%- layer.type.replace('-', '_') %>_layer.hpp> -<% } -%> - -#include <unordered_map> - -namespace mbgl { -namespace style { -namespace conversion { - -template <class V> -auto makeLayoutPropertySetters() { - std::unordered_map<std::string, PropertySetter<V>> result; - - result["visibility"] = &setVisibility<V>; - -<% for (const layer of locals.layers) { -%> -<% for (const property of layer.layoutProperties) { -%> - result["<%- property.name %>"] = &setProperty<V, <%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>; -<% } -%> - -<% } -%> - return result; -} - -template <class V> -auto makePaintPropertySetters() { - std::unordered_map<std::string, PropertySetter<V>> result; - -<% for (const layer of locals.layers) { -%> -<% for (const property of layer.paintProperties) { -%> - result["<%- property.name %>"] = &setProperty<V, <%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>; - result["<%- property.name %>-transition"] = &setTransition<V, <%- camelize(layer.type) %>Layer, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>Transition>; -<% } -%> - -<% } -%> - return result; -} - -} // namespace conversion -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/conversion/position.hpp b/include/mbgl/style/conversion/position.hpp index 7036b03822..044c45862d 100644 --- a/include/mbgl/style/conversion/position.hpp +++ b/include/mbgl/style/conversion/position.hpp @@ -2,9 +2,6 @@ #include <mbgl/style/conversion.hpp> #include <mbgl/style/position.hpp> -#include <mbgl/util/optional.hpp> - -#include <array> namespace mbgl { namespace style { @@ -12,16 +9,7 @@ namespace conversion { template <> struct Converter<Position> { - template <class V> - optional<Position> operator()(const V& value, Error& error) const { - optional<std::array<float, 3>> spherical = convert<std::array<float, 3>>(value, error); - - if (!spherical) { - return {}; - } - - return Position(*spherical); - } + optional<Position> operator()(const Convertible& value, Error& error) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/property_setter.hpp b/include/mbgl/style/conversion/property_setter.hpp deleted file mode 100644 index 759c4512cc..0000000000 --- a/include/mbgl/style/conversion/property_setter.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include <mbgl/style/layer.hpp> -#include <mbgl/style/conversion.hpp> -#include <mbgl/style/conversion/constant.hpp> -#include <mbgl/style/conversion/property_value.hpp> -#include <mbgl/style/conversion/data_driven_property_value.hpp> -#include <mbgl/style/conversion/transition_options.hpp> - -#include <string> - -namespace mbgl { -namespace style { -namespace conversion { - -template <class V> -using PropertySetter = optional<Error> (*) (Layer&, const V&); - -template <class V, class L, class PropertyValue, void (L::*setter)(PropertyValue)> -optional<Error> setProperty(Layer& layer, const V& value) { - auto* typedLayer = layer.as<L>(); - if (!typedLayer) { - return Error { "layer doesn't support this property" }; - } - - Error error; - optional<PropertyValue> typedValue = convert<PropertyValue>(value, error); - if (!typedValue) { - return error; - } - - (typedLayer->*setter)(*typedValue); - return {}; -} - -template <class V, class L, void (L::*setter)(const TransitionOptions&)> -optional<Error> setTransition(Layer& layer, const V& value) { - auto* typedLayer = layer.as<L>(); - if (!typedLayer) { - return Error { "layer doesn't support this property" }; - } - - Error error; - optional<TransitionOptions> transition = convert<TransitionOptions>(value, error); - if (!transition) { - return error; - } - - (typedLayer->*setter)(*transition); - return {}; -} - -template <class V> -optional<Error> setVisibility(Layer& layer, const V& value) { - if (isUndefined(value)) { - layer.setVisibility(VisibilityType::Visible); - return {}; - } - - Error error; - optional<VisibilityType> visibility = convert<VisibilityType>(value, error); - if (!visibility) { - return error; - } - - layer.setVisibility(*visibility); - return {}; -} - -} // namespace conversion -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/conversion/property_value.hpp b/include/mbgl/style/conversion/property_value.hpp index f8937da07d..97117de2ec 100644 --- a/include/mbgl/style/conversion/property_value.hpp +++ b/include/mbgl/style/conversion/property_value.hpp @@ -4,6 +4,11 @@ #include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/constant.hpp> #include <mbgl/style/conversion/function.hpp> +#include <mbgl/style/conversion/expression.hpp> +#include <mbgl/style/expression/value.hpp> +#include <mbgl/style/expression/is_constant.hpp> +#include <mbgl/style/expression/is_expression.hpp> +#include <mbgl/style/expression/find_zoom_curve.hpp> namespace mbgl { namespace style { @@ -11,10 +16,20 @@ namespace conversion { template <class T> struct Converter<PropertyValue<T>> { - template <class V> - optional<PropertyValue<T>> operator()(const V& value, Error& error) const { + optional<PropertyValue<T>> operator()(const Convertible& value, Error& error) const { if (isUndefined(value)) { return PropertyValue<T>(); + } else if (isExpression(value)) { + optional<std::unique_ptr<Expression>> expression = convert<std::unique_ptr<Expression>>(value, error, valueTypeToExpressionType<T>()); + if (!expression) { + return {}; + } + if (isFeatureConstant(**expression)) { + return { CameraFunction<T>(std::move(*expression)) }; + } else { + error = { "property expressions not supported" }; + return {}; + } } else if (isObject(value)) { optional<CameraFunction<T>> function = convert<CameraFunction<T>>(value, error); if (!function) { diff --git a/include/mbgl/style/conversion/source.hpp b/include/mbgl/style/conversion/source.hpp index e0563ac10b..2cf2e36da4 100644 --- a/include/mbgl/style/conversion/source.hpp +++ b/include/mbgl/style/conversion/source.hpp @@ -1,16 +1,9 @@ #pragma once #include <mbgl/style/conversion.hpp> -#include <mbgl/style/conversion/coordinate.hpp> -#include <mbgl/style/conversion/geojson.hpp> -#include <mbgl/style/conversion/geojson_options.hpp> -#include <mbgl/style/conversion/tileset.hpp> #include <mbgl/style/source.hpp> -#include <mbgl/style/sources/geojson_source.hpp> -#include <mbgl/style/sources/raster_source.hpp> -#include <mbgl/style/sources/vector_source.hpp> -#include <mbgl/style/sources/image_source.hpp> -#include <mbgl/util/geo.hpp> + +#include <memory> namespace mbgl { namespace style { @@ -19,170 +12,7 @@ namespace conversion { template <> struct Converter<std::unique_ptr<Source>> { public: - - template <class V> - optional<std::unique_ptr<Source>> operator()(const V& value, Error& error, const std::string& id) const { - if (!isObject(value)) { - error = { "source must be an object" }; - return {}; - } - - auto typeValue = objectMember(value, "type"); - if (!typeValue) { - error = { "source must have a type" }; - return {}; - } - - optional<std::string> type = toString(*typeValue); - if (!type) { - error = { "source type must be a string" }; - return {}; - } - - if (*type == "raster") { - return convertRasterSource(id, value, error); - } else if (*type == "vector") { - return convertVectorSource(id, value, error); - } else if (*type == "geojson") { - return convertGeoJSONSource(id, value, error); - } else if (*type == "image") { - return convertImageSource(id, value, error); - } else { - error = { "invalid source type" }; - return {}; - } - } - -private: - // A tile source can either specify a URL to TileJSON, or inline TileJSON. - template <class V> - optional<variant<std::string, Tileset>> convertURLOrTileset(const V& value, Error& error) const { - auto urlVal = objectMember(value, "url"); - if (!urlVal) { - optional<Tileset> tileset = convert<Tileset>(value, error); - if (!tileset) { - return {}; - } - return { *tileset }; - } - - optional<std::string> url = toString(*urlVal); - if (!url) { - error = { "source url must be a string" }; - return {}; - } - - return { *url }; - } - - template <class V> - optional<std::unique_ptr<Source>> convertRasterSource(const std::string& id, - const V& value, - Error& error) const { - optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error); - if (!urlOrTileset) { - return {}; - } - - uint16_t tileSize = util::tileSize; - auto tileSizeValue = objectMember(value, "tileSize"); - if (tileSizeValue) { - optional<float> size = toNumber(*tileSizeValue); - if (!size || *size < 0 || *size > std::numeric_limits<uint16_t>::max()) { - error = { "invalid tileSize" }; - return {}; - } - tileSize = *size; - } - - return { std::make_unique<RasterSource>(id, std::move(*urlOrTileset), tileSize) }; - } - - template <class V> - optional<std::unique_ptr<Source>> convertVectorSource(const std::string& id, - const V& value, - Error& error) const { - optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error); - if (!urlOrTileset) { - return {}; - } - - return { std::make_unique<VectorSource>(id, std::move(*urlOrTileset)) }; - } - - template <class V> - optional<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id, - const V& value, - Error& error) const { - auto dataValue = objectMember(value, "data"); - if (!dataValue) { - error = { "GeoJSON source must have a data value" }; - return {}; - } - - optional<GeoJSONOptions> options = convert<GeoJSONOptions>(value, error); - if (!options) { - return {}; - } - - auto result = std::make_unique<GeoJSONSource>(id, *options); - - if (isObject(*dataValue)) { - optional<GeoJSON> geoJSON = convert<GeoJSON>(*dataValue, error); - if (!geoJSON) { - return {}; - } - result->setGeoJSON(std::move(*geoJSON)); - } else if (toString(*dataValue)) { - result->setURL(*toString(*dataValue)); - } else { - error = { "GeoJSON data must be a URL or an object" }; - return {}; - } - - return { std::move(result) }; - } - - template <class V> - optional<std::unique_ptr<Source>> convertImageSource(const std::string& id, - const V& value, - Error& error) const { - auto urlValue = objectMember(value, "url"); - if (!urlValue) { - error = { "Image source must have a url value" }; - return {}; - } - - auto urlString = toString(*urlValue); - if (!urlString) { - error = { "Image url must be a URL string" }; - return {}; - } - - auto coordinatesValue = objectMember(value, "coordinates"); - if (!coordinatesValue) { - error = { "Image source must have a coordinates values" }; - return {}; - } - - if (!isArray(*coordinatesValue) || arrayLength(*coordinatesValue) != 4) { - error = { "Image coordinates must be an array of four longitude latitude pairs" }; - return {}; - } - - std::array<LatLng, 4> coordinates; - for (std::size_t i=0; i < 4; i++) { - auto latLng = conversion::convert<LatLng>(arrayMember(*coordinatesValue,i), error); - if (!latLng) { - return {}; - } - coordinates[i] = *latLng; - } - auto result = std::make_unique<ImageSource>(id, coordinates); - result->setURL(*urlString); - - return { std::move(result) }; - } + optional<std::unique_ptr<Source>> operator()(const Convertible& value, Error& error, const std::string& id) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/tileset.hpp b/include/mbgl/style/conversion/tileset.hpp index 377170aa6a..1fb4acf70d 100644 --- a/include/mbgl/style/conversion/tileset.hpp +++ b/include/mbgl/style/conversion/tileset.hpp @@ -10,70 +10,7 @@ namespace conversion { template <> struct Converter<Tileset> { public: - template <class V> - optional<Tileset> operator()(const V& value, Error& error) const { - Tileset result; - - auto tiles = objectMember(value, "tiles"); - if (!tiles) { - error = { "source must have tiles" }; - return {}; - } - - if (!isArray(*tiles)) { - error = { "source tiles must be an array" }; - return {}; - } - - for (std::size_t i = 0; i < arrayLength(*tiles); i++) { - optional<std::string> urlTemplate = toString(arrayMember(*tiles, i)); - if (!urlTemplate) { - error = { "source tiles member must be a string" }; - return {}; - } - result.tiles.push_back(std::move(*urlTemplate)); - } - - auto schemeValue = objectMember(value, "scheme"); - if (schemeValue) { - optional<std::string> scheme = toString(*schemeValue); - if (scheme && *scheme == "tms") { - result.scheme = Tileset::Scheme::TMS; - } - } - - auto minzoomValue = objectMember(value, "minzoom"); - if (minzoomValue) { - optional<float> minzoom = toNumber(*minzoomValue); - if (!minzoom || *minzoom < 0 || *minzoom > std::numeric_limits<uint8_t>::max()) { - error = { "invalid minzoom" }; - return {}; - } - result.zoomRange.min = *minzoom; - } - - auto maxzoomValue = objectMember(value, "maxzoom"); - if (maxzoomValue) { - optional<float> maxzoom = toNumber(*maxzoomValue); - if (!maxzoom || *maxzoom < 0 || *maxzoom > std::numeric_limits<uint8_t>::max()) { - error = { "invalid maxzoom" }; - return {}; - } - result.zoomRange.max = *maxzoom; - } - - auto attributionValue = objectMember(value, "attribution"); - if (attributionValue) { - optional<std::string> attribution = toString(*attributionValue); - if (!attribution) { - error = { "source attribution must be a string" }; - return {}; - } - result.attribution = std::move(*attribution); - } - - return result; - } + optional<Tileset> operator()(const Convertible& value, Error& error) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/transition_options.hpp b/include/mbgl/style/conversion/transition_options.hpp index de8834d578..0563f39ac3 100644 --- a/include/mbgl/style/conversion/transition_options.hpp +++ b/include/mbgl/style/conversion/transition_options.hpp @@ -10,37 +10,7 @@ namespace conversion { template <> struct Converter<TransitionOptions> { public: - template <class V> - optional<TransitionOptions> operator()(const V& value, Error& error) const { - if (!isObject(value)) { - error = { "transition must be an object" }; - return {}; - } - - TransitionOptions result; - - auto duration = objectMember(value, "duration"); - if (duration) { - auto number = toNumber(*duration); - if (!number) { - error = { "duration must be a number" }; - return {}; - } - result.duration = { std::chrono::milliseconds(int64_t(*number)) }; - } - - auto delay = objectMember(value, "delay"); - if (delay) { - auto number = toNumber(*delay); - if (!number) { - error = { "delay must be a number" }; - return {}; - } - result.delay = { std::chrono::milliseconds(int64_t(*number)) }; - } - - return result; - } + optional<TransitionOptions> operator()(const Convertible& value, Error& error) const; }; } // namespace conversion diff --git a/include/mbgl/style/expression/array_assertion.hpp b/include/mbgl/style/expression/array_assertion.hpp new file mode 100644 index 0000000000..2516eea024 --- /dev/null +++ b/include/mbgl/style/expression/array_assertion.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/expression/type.hpp> +#include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/conversion.hpp> + +#include <memory> + +namespace mbgl { +namespace style { +namespace expression { + +class ArrayAssertion : public Expression { +public: + ArrayAssertion(type::Array type_, std::unique_ptr<Expression> input_) : + Expression(type_), + input(std::move(input_)) + {} + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function<void(const Expression&)>& visit) const override; + + bool operator==(const Expression& e) const override { + if (auto rhs = dynamic_cast<const ArrayAssertion*>(&e)) { + return getType() == rhs->getType() && *input == *(rhs->input); + } + return false; + } + +private: + std::unique_ptr<Expression> input; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/assertion.hpp b/include/mbgl/style/expression/assertion.hpp new file mode 100644 index 0000000000..504d49f4e5 --- /dev/null +++ b/include/mbgl/style/expression/assertion.hpp @@ -0,0 +1,33 @@ +#pragma once +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/expression/parsing_context.hpp> +#include <memory> +#include <vector> + +namespace mbgl { +namespace style { +namespace expression { + +class Assertion : public Expression { +public: + Assertion(type::Type type_, std::vector<std::unique_ptr<Expression>> inputs_) : + Expression(type_), + inputs(std::move(inputs_)) + {} + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function<void(const Expression&)>& visit) const override; + + bool operator==(const Expression& e) const override; + +private: + std::vector<std::unique_ptr<Expression>> inputs; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl + diff --git a/include/mbgl/style/expression/at.hpp b/include/mbgl/style/expression/at.hpp new file mode 100644 index 0000000000..e3eefa4fe8 --- /dev/null +++ b/include/mbgl/style/expression/at.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/conversion.hpp> +#include <memory> + +namespace mbgl { +namespace style { +namespace expression { + +class At : public Expression { +public: + At(std::unique_ptr<Expression> index_, std::unique_ptr<Expression> input_) : + Expression(input_->getType().get<type::Array>().itemType), + index(std::move(index_)), + input(std::move(input_)) + {} + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function<void(const Expression&)>&) const override; + + bool operator==(const Expression& e) const override { + if (auto rhs = dynamic_cast<const At*>(&e)) { + return *index == *(rhs->index) && *input == *(rhs->input); + } + return false; + } + +private: + std::unique_ptr<Expression> index; + std::unique_ptr<Expression> input; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/boolean_operator.hpp b/include/mbgl/style/expression/boolean_operator.hpp new file mode 100644 index 0000000000..01231d706b --- /dev/null +++ b/include/mbgl/style/expression/boolean_operator.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/conversion.hpp> +#include <memory> + +namespace mbgl { +namespace style { +namespace expression { + +class Any : public Expression { +public: + Any(std::vector<std::unique_ptr<Expression>> inputs_) : + Expression(type::Boolean), + inputs(std::move(inputs_)) + {} + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function<void(const Expression&)>& visit) const override; + bool operator==(const Expression& e) const override; + +private: + std::vector<std::unique_ptr<Expression>> inputs; +}; + +class All : public Expression { +public: + All(std::vector<std::unique_ptr<Expression>> inputs_) : + Expression(type::Boolean), + inputs(std::move(inputs_)) + {} + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function<void(const Expression&)>& visit) const override; + + bool operator==(const Expression& e) const override; + +private: + std::vector<std::unique_ptr<Expression>> inputs; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl + diff --git a/include/mbgl/style/expression/case.hpp b/include/mbgl/style/expression/case.hpp new file mode 100644 index 0000000000..ece2fe0329 --- /dev/null +++ b/include/mbgl/style/expression/case.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/expression/parsing_context.hpp> + +#include <memory> +#include <vector> + +namespace mbgl { +namespace style { +namespace expression { + +class Case : public Expression { +public: + using Branch = std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression>>; + + Case(type::Type type_, std::vector<Branch> branches_, std::unique_ptr<Expression> otherwise_) + : Expression(type_), branches(std::move(branches_)), otherwise(std::move(otherwise_)) { + } + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function<void(const Expression&)>& visit) const override; + + bool operator==(const Expression& e) const override; + +private: + std::vector<Branch> branches; + std::unique_ptr<Expression> otherwise; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/check_subtype.hpp b/include/mbgl/style/expression/check_subtype.hpp new file mode 100644 index 0000000000..90e5169de7 --- /dev/null +++ b/include/mbgl/style/expression/check_subtype.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include <mbgl/style/expression/type.hpp> +#include <mbgl/util/optional.hpp> +#include <memory> + +namespace mbgl { +namespace style { +namespace expression { +namespace type { + +optional<std::string> checkSubtype(const Type& expected, const Type& t); + +} // namespace type +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/coalesce.hpp b/include/mbgl/style/expression/coalesce.hpp new file mode 100644 index 0000000000..4e6a9b3793 --- /dev/null +++ b/include/mbgl/style/expression/coalesce.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/expression/parsing_context.hpp> + +#include <memory> +#include <map> + +namespace mbgl { +namespace style { +namespace expression { + +class Coalesce : public Expression { +public: + using Args = std::vector<std::unique_ptr<Expression>>; + Coalesce(const type::Type& type_, Args args_) : + Expression(type_), + args(std::move(args_)) + {} + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + + EvaluationResult evaluate(const EvaluationContext& params) const override; + + void eachChild(const std::function<void(const Expression&)>& visit) const override; + + bool operator==(const Expression& e) const override; + + std::size_t getLength() const { + return args.size(); + } + + Expression* getChild(std::size_t i) const { + return args.at(i).get(); + } + +private: + Args args; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/coercion.hpp b/include/mbgl/style/expression/coercion.hpp new file mode 100644 index 0000000000..665bb7ce7c --- /dev/null +++ b/include/mbgl/style/expression/coercion.hpp @@ -0,0 +1,34 @@ +#pragma once +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/conversion.hpp> +#include <memory> +#include <vector> + +namespace mbgl { +namespace style { +namespace expression { + +/** + * Special form for error-coalescing coercion expressions "to-number", + * "to-color". Since these coercions can fail at runtime, they accept multiple + * arguments, only evaluating one at a time until one succeeds. + */ +class Coercion : public Expression { +public: + Coercion(type::Type type_, std::vector<std::unique_ptr<Expression>> inputs_); + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function<void(const Expression&)>& visit) const override; + + bool operator==(const Expression& e) const override; +private: + EvaluationResult (*coerceSingleValue) (const Value& v); + std::vector<std::unique_ptr<Expression>> inputs; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl + diff --git a/include/mbgl/style/expression/compound_expression.hpp b/include/mbgl/style/expression/compound_expression.hpp new file mode 100644 index 0000000000..fc3edbfd4a --- /dev/null +++ b/include/mbgl/style/expression/compound_expression.hpp @@ -0,0 +1,138 @@ +#pragma once + +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/expression/type.hpp> +#include <mbgl/style/expression/value.hpp> + +#include <mbgl/util/optional.hpp> +#include <mbgl/util/variant.hpp> + +#include <memory> +#include <vector> + +namespace mbgl { +namespace style { +namespace expression { + +/* + CompoundExpression provides a mechanism for implementing an expression + simply by providing a list of pure functions of the form + (const T0& arg0, const T1& arg1, ...) -> Result<U> where T0, T1, ..., U are + member types of mbgl::style::expression::Value. + + The majority of expressions specified in the style-spec are implemented in + this fashion (see compound_expression.cpp). +*/ + + +/* + Represents the parameter list for an expression that takes an arbitrary + number of arguments (of a specific type). +*/ +struct VarargsType { type::Type type; }; +template <typename T> +struct Varargs : std::vector<T> { using std::vector<T>::vector; }; + +namespace detail { +// Base class for the Signature<Fn> structs that are used to determine the +// each CompoundExpression definition's type::Type data from the type of its +// "evaluate" function. +struct SignatureBase { + SignatureBase(type::Type result_, variant<std::vector<type::Type>, VarargsType> params_) : + result(std::move(result_)), + params(std::move(params_)) + {} + virtual ~SignatureBase() = default; + virtual std::unique_ptr<Expression> makeExpression(const std::string& name, std::vector<std::unique_ptr<Expression>>) const = 0; + type::Type result; + variant<std::vector<type::Type>, VarargsType> params; +}; +} // namespace detail + + +/* + Common base class for CompoundExpression<Signature> instances. Used to + allow downcasting (and access to things like name & parameter list) during + an Expression tree traversal. +*/ +class CompoundExpressionBase : public Expression { +public: + CompoundExpressionBase(std::string name_, const detail::SignatureBase& signature) : + Expression(signature.result), + name(std::move(name_)), + params(signature.params) + {} + + std::string getName() const { return name; } + optional<std::size_t> getParameterCount() const { + return params.match( + [&](const VarargsType&) { return optional<std::size_t>(); }, + [&](const std::vector<type::Type>& p) -> optional<std::size_t> { return p.size(); } + ); + } + +private: + std::string name; + variant<std::vector<type::Type>, VarargsType> params; +}; + +template <typename Signature> +class CompoundExpression : public CompoundExpressionBase { +public: + using Args = typename Signature::Args; + + CompoundExpression(const std::string& name_, + Signature signature_, + typename Signature::Args args_) : + CompoundExpressionBase(name_, signature_), + signature(signature_), + args(std::move(args_)) + {} + + EvaluationResult evaluate(const EvaluationContext& evaluationParams) const override { + return signature.apply(evaluationParams, args); + } + + void eachChild(const std::function<void(const Expression&)>& visit) const override { + for (const std::unique_ptr<Expression>& e : args) { + visit(*e); + } + } + + bool operator==(const Expression& e) const override { + if (auto rhs = dynamic_cast<const CompoundExpression*>(&e)) { + return getName() == rhs->getName() && Expression::childrenEqual(args, rhs->args); + } + return false; + } + +private: + Signature signature; + typename Signature::Args args; +}; + +/* + Holds the map of expression name => implementation (which is just one or + more evaluation functions, each wrapped in a Signature struct). +*/ +struct CompoundExpressionRegistry { + using Definition = std::vector<std::unique_ptr<detail::SignatureBase>>; + static std::unordered_map<std::string, Definition> definitions; +}; + +ParseResult parseCompoundExpression(const std::string name, const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + +ParseResult createCompoundExpression(const std::string& name, + const CompoundExpressionRegistry::Definition& definition, + std::vector<std::unique_ptr<Expression>> args, + ParsingContext& ctx); + +ParseResult createCompoundExpression(const std::string& name, + std::vector<std::unique_ptr<Expression>> args, + ParsingContext& ctx); + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/expression.hpp b/include/mbgl/style/expression/expression.hpp new file mode 100644 index 0000000000..a22fc28724 --- /dev/null +++ b/include/mbgl/style/expression/expression.hpp @@ -0,0 +1,173 @@ +#pragma once + +#include <array> +#include <vector> +#include <memory> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/variant.hpp> +#include <mbgl/util/color.hpp> +#include <mbgl/style/expression/type.hpp> +#include <mbgl/style/expression/value.hpp> +#include <mbgl/style/expression/parsing_context.hpp> + +namespace mbgl { + +class GeometryTileFeature; + +namespace style { +namespace expression { + +class EvaluationError { +public: + std::string message; +}; + +class EvaluationContext { +public: + EvaluationContext(float zoom_) : zoom(zoom_), feature(nullptr) {} + EvaluationContext(GeometryTileFeature const * feature_) : zoom(optional<float>()), feature(feature_) {} + EvaluationContext(float zoom_, GeometryTileFeature const * feature_) : + zoom(zoom_), feature(feature_) + {} + EvaluationContext(optional<float> zoom_, GeometryTileFeature const * feature_, optional<double> heatmapDensity_) : + zoom(std::move(zoom_)), feature(feature_), heatmapDensity(std::move(heatmapDensity_)) + {} + + optional<float> zoom; + GeometryTileFeature const * feature; + optional<double> heatmapDensity; +}; + +template<typename T> +class Result : private variant<EvaluationError, T> { +public: + using variant<EvaluationError, T>::variant; + using Value = T; + + Result() = default; + + explicit operator bool () const { + return this->template is<T>(); + } + + // optional does some type trait magic for this one, so this might + // be problematic as is. + const T* operator->() const { + assert(this->template is<T>()); + return std::addressof(this->template get<T>()); + } + + T* operator->() { + assert(this->template is<T>()); + return std::addressof(this->template get<T>()); + } + + T& operator*() { + assert(this->template is<T>()); + return this->template get<T>(); + } + + const T& operator*() const { + assert(this->template is<T>()); + return this->template get<T>(); + } + + const EvaluationError& error() const { + assert(this->template is<EvaluationError>()); + return this->template get<EvaluationError>(); + } +}; + +class EvaluationResult : public Result<Value> { +public: + using Result::Result; // NOLINT + + EvaluationResult() = default; + + EvaluationResult(const std::array<double, 4>& arr) : + Result(toExpressionValue(arr)) + {} + + // used only for the special (private) "error" expression + EvaluationResult(const type::ErrorType&) { + assert(false); + } +}; + +/* + Expression is an abstract class that serves as an interface and base class + for particular expression implementations. + + CompoundExpression implements the majority of expressions in the spec by + inferring the argument and output from a simple function (const T0& arg0, + const T1& arg1, ...) -> Result<U> where T0, T1, ..., U are member types of + mbgl::style::expression::Value. + + The other Expression subclasses (Let, Curve, Match, etc.) exist in order to + implement expressions that need specialized parsing, type checking, or + evaluation logic that can't be handled by CompoundExpression's inference + mechanism. + + Each Expression subclass also provides a static + ParseResult ExpressionClass::parse(const V&, ParsingContext), + which handles parsing a style-spec JSON representation of the expression. +*/ +class Expression { +public: + Expression(type::Type type_) : type(std::move(type_)) {} + virtual ~Expression() = default; + + virtual EvaluationResult evaluate(const EvaluationContext& params) const = 0; + virtual void eachChild(const std::function<void(const Expression&)>&) const = 0; + virtual bool operator==(const Expression&) const = 0; + bool operator!=(const Expression& rhs) const { + return !operator==(rhs); + } + + type::Type getType() const { return type; }; + + EvaluationResult evaluate(optional<float> zoom, const Feature& feature, optional<double> heatmapDensity) const; + +protected: + template <typename T> + static bool childrenEqual(const T& lhs, const T& rhs) { + if (lhs.size() != rhs.size()) return false; + for (auto leftChild = lhs.begin(), rightChild = rhs.begin(); + leftChild != lhs.end(); + leftChild++, rightChild++) + { + if (!Expression::childEqual(*leftChild, *rightChild)) return false; + } + return true; + } + + static bool childEqual(const std::unique_ptr<Expression>& lhs, const std::unique_ptr<Expression>& rhs) { + return *lhs == *rhs; + } + + template <typename T> + static bool childEqual(const std::pair<T, std::unique_ptr<Expression>>& lhs, + const std::pair<T, std::unique_ptr<Expression>>& rhs) { + return lhs.first == rhs.first && *(lhs.second) == *(rhs.second); + } + + template <typename T> + static bool childEqual(const std::pair<T, std::shared_ptr<Expression>>& lhs, + const std::pair<T, std::shared_ptr<Expression>>& rhs) { + return lhs.first == rhs.first && *(lhs.second) == *(rhs.second); + } + + static bool childEqual(const std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression>>& lhs, + const std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression>>& rhs) { + return *(lhs.first) == *(rhs.first) && *(lhs.second) == *(rhs.second); + } + + + +private: + type::Type type; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/find_zoom_curve.hpp b/include/mbgl/style/expression/find_zoom_curve.hpp new file mode 100644 index 0000000000..6301938033 --- /dev/null +++ b/include/mbgl/style/expression/find_zoom_curve.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/expression/interpolate.hpp> +#include <mbgl/style/expression/step.hpp> + +#include <mbgl/util/variant.hpp> +#include <mbgl/util/optional.hpp> + +namespace mbgl { +namespace style { +namespace expression { + +optional<variant<const InterpolateBase*, const Step*, ParsingError>> findZoomCurve(const expression::Expression* e); + +variant<const InterpolateBase*, const Step*> findZoomCurveChecked(const expression::Expression* e); + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/get_covering_stops.hpp b/include/mbgl/style/expression/get_covering_stops.hpp new file mode 100644 index 0000000000..157aefe7bc --- /dev/null +++ b/include/mbgl/style/expression/get_covering_stops.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/util/range.hpp> +#include <memory> +#include <map> + +namespace mbgl { +namespace style { +namespace expression { + +// Return the smallest range of stops that covers the interval [lower, upper] +Range<float> getCoveringStops(const std::map<double, std::unique_ptr<Expression>>& stops, + const double lower, const double upper); + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/interpolate.hpp b/include/mbgl/style/expression/interpolate.hpp new file mode 100644 index 0000000000..439122f91c --- /dev/null +++ b/include/mbgl/style/expression/interpolate.hpp @@ -0,0 +1,177 @@ +#pragma once + +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/expression/get_covering_stops.hpp> +#include <mbgl/style/conversion.hpp> + +#include <mbgl/util/interpolate.hpp> +#include <mbgl/util/range.hpp> +#include <mbgl/util/unitbezier.hpp> + +#include <memory> +#include <map> + + +namespace mbgl { +namespace style { +namespace expression { + +class ExponentialInterpolator { +public: + ExponentialInterpolator(double base_) : base(base_) {} + + double base; + + double interpolationFactor(const Range<double>& inputLevels, const double input) const { + return util::interpolationFactor(base, + Range<float> { + static_cast<float>(inputLevels.min), + static_cast<float>(inputLevels.max) + }, + input); + } + + bool operator==(const ExponentialInterpolator& rhs) const { + return base == rhs.base; + } +}; + +class CubicBezierInterpolator { +public: + CubicBezierInterpolator(double x1_, double y1_, double x2_, double y2_) : ub(x1_, y1_, x2_, y2_) {} + + double interpolationFactor(const Range<double>& inputLevels, const double input) const { + return ub.solve(input / (inputLevels.max - inputLevels.min), 1e-6); + } + + bool operator==(const CubicBezierInterpolator& rhs) const { + return ub == rhs.ub; + } + + util::UnitBezier ub; +}; + + +ParseResult parseInterpolate(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + +class InterpolateBase : public Expression { +public: + using Interpolator = variant<ExponentialInterpolator, CubicBezierInterpolator>; + + InterpolateBase(const type::Type& type_, + Interpolator interpolator_, + std::unique_ptr<Expression> input_, + std::map<double, std::unique_ptr<Expression>> stops_ + ) : Expression(type_), + interpolator(std::move(interpolator_)), + input(std::move(input_)), + stops(std::move(stops_)) + {} + + const std::unique_ptr<Expression>& getInput() const { return input; } + + void eachChild(const std::function<void(const Expression&)>& visit) const override { + visit(*input); + for (const auto& stop : stops) { + visit(*stop.second); + } + } + + // Return the smallest range of stops that covers the interval [lower, upper] + Range<float> getCoveringStops(const double lower, const double upper) const { + return ::mbgl::style::expression::getCoveringStops(stops, lower, upper); + } + + double interpolationFactor(const Range<double>& inputLevels, const double inputValue) const { + return interpolator.match( + [&](const auto& interp) { return interp.interpolationFactor(inputLevels, inputValue); } + ); + } + +protected: + const Interpolator interpolator; + const std::unique_ptr<Expression> input; + const std::map<double, std::unique_ptr<Expression>> stops; +}; + +template <typename T> +class Interpolate : public InterpolateBase { +public: + Interpolate(type::Type type_, + Interpolator interpolator_, + std::unique_ptr<Expression> input_, + std::map<double, std::unique_ptr<Expression>> stops_ + ) : InterpolateBase(std::move(type_), std::move(interpolator_), std::move(input_), std::move(stops_)) + { + static_assert(util::Interpolatable<T>::value, "Interpolate expression requires an interpolatable value type."); + } + + EvaluationResult evaluate(const EvaluationContext& params) const override { + const EvaluationResult evaluatedInput = input->evaluate(params); + if (!evaluatedInput) { return evaluatedInput.error(); } + float x = *fromExpressionValue<float>(*evaluatedInput); + + if (stops.empty()) { + return EvaluationError { "No stops in exponential curve." }; + } + + auto it = stops.upper_bound(x); + if (it == stops.end()) { + return stops.rbegin()->second->evaluate(params); + } else if (it == stops.begin()) { + return stops.begin()->second->evaluate(params); + } else { + float t = interpolationFactor({ std::prev(it)->first, it->first }, x); + + if (t == 0.0f) { + return std::prev(it)->second->evaluate(params); + } + if (t == 1.0f) { + return it->second->evaluate(params); + } + + EvaluationResult lower = std::prev(it)->second->evaluate(params); + if (!lower) { + return lower.error(); + } + EvaluationResult upper = it->second->evaluate(params); + if (!upper) { + return upper.error(); + } + + if (!lower->is<T>()) { + return EvaluationError { + "Expected value to be of type " + toString(valueTypeToExpressionType<T>()) + + ", but found " + toString(typeOf(*lower)) + " instead." + }; + } + + if (!upper->is<T>()) { + return EvaluationError { + "Expected value to be of type " + toString(valueTypeToExpressionType<T>()) + + ", but found " + toString(typeOf(*upper)) + " instead." + }; + } + return util::interpolate(lower->get<T>(), upper->get<T>(), t); + } + } + + bool operator==(const Expression& e) const override { + if (auto rhs = dynamic_cast<const Interpolate*>(&e)) { + if (interpolator != rhs->interpolator || + *input != *(rhs->input) || + stops.size() != rhs->stops.size()) + { + return false; + } + + return Expression::childrenEqual(stops, rhs->stops); + } + return false; + } +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/is_constant.hpp b/include/mbgl/style/expression/is_constant.hpp new file mode 100644 index 0000000000..29e03ccbc0 --- /dev/null +++ b/include/mbgl/style/expression/is_constant.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/expression/compound_expression.hpp> + +namespace mbgl { +namespace style { +namespace expression { + +template <typename T> +bool isGlobalPropertyConstant(const Expression& expression, const T& properties) { + if (auto e = dynamic_cast<const CompoundExpressionBase*>(&expression)) { + for (const std::string& property : properties) { + if (e->getName() == property) { + return false; + } + } + } + + bool isConstant = true; + expression.eachChild([&](const Expression& e) { + if (isConstant && !isGlobalPropertyConstant(e, properties)) { + isConstant = false; + } + }); + return isConstant; +}; + +bool isFeatureConstant(const Expression& expression); +bool isZoomConstant(const Expression& e); + + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/is_expression.hpp b/include/mbgl/style/expression/is_expression.hpp new file mode 100644 index 0000000000..77c489619c --- /dev/null +++ b/include/mbgl/style/expression/is_expression.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include <mbgl/style/expression/expression.hpp> + +namespace mbgl { +namespace style { +namespace expression { + +bool isExpression(const conversion::Convertible& value); + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/let.hpp b/include/mbgl/style/expression/let.hpp new file mode 100644 index 0000000000..aaa16ca0c2 --- /dev/null +++ b/include/mbgl/style/expression/let.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/conversion.hpp> + +#include <memory> +#include <map> + +namespace mbgl { +namespace style { +namespace expression { + +class Let : public Expression { +public: + using Bindings = std::map<std::string, std::shared_ptr<Expression>>; + + Let(Bindings bindings_, std::unique_ptr<Expression> result_) : + Expression(result_->getType()), + bindings(std::move(bindings_)), + result(std::move(result_)) + {} + + static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function<void(const Expression&)>&) const override; + + bool operator==(const Expression& e) const override { + if (auto rhs = dynamic_cast<const Let*>(&e)) { + return *result == *(rhs->result); + } + return false; + } + + Expression* getResult() const { + return result.get(); + } + +private: + Bindings bindings; + std::unique_ptr<Expression> result; +}; + +class Var : public Expression { +public: + Var(std::string name_, std::shared_ptr<Expression> value_) : + Expression(value_->getType()), + name(std::move(name_)), + value(value_) + {} + + static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function<void(const Expression&)>&) const override; + + bool operator==(const Expression& e) const override { + if (auto rhs = dynamic_cast<const Var*>(&e)) { + return *value == *(rhs->value); + } + return false; + } + +private: + std::string name; + std::shared_ptr<Expression> value; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/literal.hpp b/include/mbgl/style/expression/literal.hpp new file mode 100644 index 0000000000..a0819c7e73 --- /dev/null +++ b/include/mbgl/style/expression/literal.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/conversion.hpp> + +#include <memory> + +namespace mbgl { +namespace style { +namespace expression { + +class Literal : public Expression { +public: + Literal(Value value_) : Expression(typeOf(value_)), value(value_) {} + Literal(type::Array type_, std::vector<Value> value_) : Expression(type_), value(value_) {} + EvaluationResult evaluate(const EvaluationContext&) const override { + return value; + } + + static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&); + + void eachChild(const std::function<void(const Expression&)>&) const override {} + + bool operator==(const Expression& e) const override { + if (auto rhs = dynamic_cast<const Literal*>(&e)) { + return value == rhs->value; + } + return false; + } + +private: + Value value; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/match.hpp b/include/mbgl/style/expression/match.hpp new file mode 100644 index 0000000000..e17fe96bfe --- /dev/null +++ b/include/mbgl/style/expression/match.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/conversion.hpp> + +#include <memory> + +namespace mbgl { +namespace style { +namespace expression { + +template <typename T> +class Match : public Expression { +public: + using Branches = std::unordered_map<T, std::shared_ptr<Expression>>; + + Match(type::Type type_, + std::unique_ptr<Expression> input_, + Branches branches_, + std::unique_ptr<Expression> otherwise_ + ) : Expression(type_), + input(std::move(input_)), + branches(std::move(branches_)), + otherwise(std::move(otherwise_)) + {} + + void eachChild(const std::function<void(const Expression&)>& visit) const override; + + bool operator==(const Expression& e) const override; + + EvaluationResult evaluate(const EvaluationContext& params) const override; + +private: + + std::unique_ptr<Expression> input; + Branches branches; + std::unique_ptr<Expression> otherwise; +}; + +ParseResult parseMatch(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/parsing_context.hpp b/include/mbgl/style/expression/parsing_context.hpp new file mode 100644 index 0000000000..a983789cbd --- /dev/null +++ b/include/mbgl/style/expression/parsing_context.hpp @@ -0,0 +1,148 @@ +#pragma once + +#include <mbgl/util/optional.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/style/expression/type.hpp> +#include <mbgl/style/conversion.hpp> + +#include <map> +#include <string> +#include <vector> +#include <memory> + +namespace mbgl { +namespace style { +namespace expression { + +class Expression; + +struct ParsingError { + std::string message; + std::string key; + bool operator==(const ParsingError& rhs) const { return message == rhs.message && key == rhs.key; } +}; + +using ParseResult = optional<std::unique_ptr<Expression>>; + +namespace detail { + +class Scope { +public: + Scope(const std::map<std::string, std::shared_ptr<Expression>>& bindings_, std::shared_ptr<Scope> parent_ = nullptr) : + bindings(bindings_), + parent(std::move(parent_)) + {} + + const std::map<std::string, std::shared_ptr<Expression>>& bindings; + std::shared_ptr<Scope> parent; + + optional<std::shared_ptr<Expression>> get(const std::string& name) { + auto it = bindings.find(name); + if (it != bindings.end()) { + return {it->second}; + } else if (parent) { + return parent->get(name); + } else { + return optional<std::shared_ptr<Expression>>(); + } + } +}; + +} // namespace detail + +class ParsingContext { +public: + ParsingContext() : errors(std::make_shared<std::vector<ParsingError>>()) {} + ParsingContext(std::string key_) : key(std::move(key_)), errors(std::make_shared<std::vector<ParsingError>>()) {} + explicit ParsingContext(optional<type::Type> expected_) + : expected(std::move(expected_)), + errors(std::make_shared<std::vector<ParsingError>>()) + {} + ParsingContext(ParsingContext&&) = default; + + ParsingContext(const ParsingContext&) = delete; + ParsingContext& operator=(const ParsingContext&) = delete; + + std::string getKey() const { return key; } + optional<type::Type> getExpected() const { return expected; } + const std::vector<ParsingError>& getErrors() const { return *errors; } + + /* + Parse the given style-spec JSON value into an Expression object. + Specifically, this function is responsible for determining the expression + type (either Literal, or the one named in value[0]) and dispatching to the + appropriate ParseXxxx::parse(const V&, ParsingContext) method. + */ + ParseResult parse(const mbgl::style::conversion::Convertible& value); + + /* + Parse a child expression. + */ + ParseResult parse(const mbgl::style::conversion::Convertible&, + std::size_t, + optional<type::Type> = {}); + + /* + Parse a child expression. + */ + ParseResult parse(const mbgl::style::conversion::Convertible&, + std::size_t index, + optional<type::Type>, + const std::map<std::string, std::shared_ptr<Expression>>&); + + /* + Check whether `t` is a subtype of `expected`, collecting an error if not. + */ + optional<std::string> checkType(const type::Type& t); + + optional<std::shared_ptr<Expression>> getBinding(const std::string name) { + if (!scope) return optional<std::shared_ptr<Expression>>(); + return scope->get(name); + } + + void error(std::string message) { + errors->push_back({message, key}); + } + + void error(std::string message, std::size_t child) { + errors->push_back({message, key + "[" + util::toString(child) + "]"}); + } + + void error(std::string message, std::size_t child, std::size_t grandchild) { + errors->push_back({message, key + "[" + util::toString(child) + "][" + util::toString(grandchild) + "]"}); + } + + void appendErrors(ParsingContext&& ctx) { + errors->reserve(errors->size() + ctx.errors->size()); + std::move(ctx.errors->begin(), ctx.errors->end(), std::inserter(*errors, errors->end())); + ctx.errors->clear(); + } + + void clearErrors() { + errors->clear(); + } + +private: + ParsingContext(std::string key_, + std::shared_ptr<std::vector<ParsingError>> errors_, + optional<type::Type> expected_, + std::shared_ptr<detail::Scope> scope_) + : key(std::move(key_)), + expected(std::move(expected_)), + scope(std::move(scope_)), + errors(std::move(errors_)) + {} + + std::string key; + optional<type::Type> expected; + std::shared_ptr<detail::Scope> scope; + std::shared_ptr<std::vector<ParsingError>> errors; +}; + +using ParseFunction = ParseResult (*)(const conversion::Convertible&, ParsingContext&); +using ExpressionRegistry = std::unordered_map<std::string, ParseFunction>; +const ExpressionRegistry& getExpressionRegistry(); + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/step.hpp b/include/mbgl/style/expression/step.hpp new file mode 100644 index 0000000000..e3c49bc609 --- /dev/null +++ b/include/mbgl/style/expression/step.hpp @@ -0,0 +1,45 @@ + +#pragma once + +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/conversion.hpp> + +#include <mbgl/util/range.hpp> + +#include <memory> +#include <map> + + +namespace mbgl { +namespace style { +namespace expression { + +class Step : public Expression { +public: + Step(const type::Type& type_, + std::unique_ptr<Expression> input_, + std::map<double, std::unique_ptr<Expression>> stops_ + ) : Expression(type_), + input(std::move(input_)), + stops(std::move(stops_)) + {} + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function<void(const Expression&)>& visit) const override; + + const std::unique_ptr<Expression>& getInput() const { return input; } + Range<float> getCoveringStops(const double lower, const double upper) const; + + bool operator==(const Expression& e) const override; + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + +private: + const std::unique_ptr<Expression> input; + const std::map<double, std::unique_ptr<Expression>> stops; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/type.hpp b/include/mbgl/style/expression/type.hpp new file mode 100644 index 0000000000..513c4bdc17 --- /dev/null +++ b/include/mbgl/style/expression/type.hpp @@ -0,0 +1,112 @@ +#pragma once + +#include <mbgl/util/optional.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/util/variant.hpp> +#include <vector> + +namespace mbgl { +namespace style { +namespace expression { +namespace type { + +template <class T> +std::string toString(const T& t); + +struct NullType { + constexpr NullType() {}; + std::string getName() const { return "null"; } + bool operator==(const NullType&) const { return true; } +}; + +struct NumberType { + constexpr NumberType() {}; + std::string getName() const { return "number"; } + bool operator==(const NumberType&) const { return true; } +}; + +struct BooleanType { + constexpr BooleanType() {}; + std::string getName() const { return "boolean"; } + bool operator==(const BooleanType&) const { return true; } +}; + +struct StringType { + constexpr StringType() {}; + std::string getName() const { return "string"; } + bool operator==(const StringType&) const { return true; } +}; + +struct ColorType { + constexpr ColorType() {}; + std::string getName() const { return "color"; } + bool operator==(const ColorType&) const { return true; } +}; + +struct ObjectType { + constexpr ObjectType() {}; + std::string getName() const { return "object"; } + bool operator==(const ObjectType&) const { return true; } +}; + +struct ErrorType { + constexpr ErrorType() {}; + std::string getName() const { return "error"; } + bool operator==(const ErrorType&) const { return true; } +}; + +struct ValueType { + constexpr ValueType() {}; + std::string getName() const { return "value"; } + bool operator==(const ValueType&) const { return true; } +}; + +constexpr NullType Null; +constexpr NumberType Number; +constexpr StringType String; +constexpr BooleanType Boolean; +constexpr ColorType Color; +constexpr ValueType Value; +constexpr ObjectType Object; +constexpr ErrorType Error; + +struct Array; + +using Type = variant< + NullType, + NumberType, + BooleanType, + StringType, + ColorType, + ObjectType, + ValueType, + mapbox::util::recursive_wrapper<Array>, + ErrorType>; + +struct Array { + explicit Array(Type itemType_) : itemType(std::move(itemType_)) {} + Array(Type itemType_, std::size_t N_) : itemType(std::move(itemType_)), N(N_) {} + Array(Type itemType_, optional<std::size_t> N_) : itemType(std::move(itemType_)), N(std::move(N_)) {} + std::string getName() const { + if (N) { + return "array<" + toString(itemType) + ", " + util::toString(*N) + ">"; + } else if (itemType == Value) { + return "array"; + } else { + return "array<" + toString(itemType) + ">"; + } + } + + bool operator==(const Array& rhs) const { return itemType == rhs.itemType && N == rhs.N; } + + Type itemType; + optional<std::size_t> N; +}; + +template <class T> +std::string toString(const T& type) { return type.match([&] (const auto& t) { return t.getName(); }); } + +} // namespace type +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/value.hpp b/include/mbgl/style/expression/value.hpp new file mode 100644 index 0000000000..8baa9d2dba --- /dev/null +++ b/include/mbgl/style/expression/value.hpp @@ -0,0 +1,153 @@ +#pragma once + +#include <mbgl/style/expression/type.hpp> +#include <mbgl/style/position.hpp> +#include <mbgl/style/types.hpp> +#include <mbgl/util/color.hpp> +#include <mbgl/util/enum.hpp> +#include <mbgl/util/feature.hpp> +#include <mbgl/util/variant.hpp> + +#include <array> +#include <vector> + +namespace mbgl { +namespace style { +namespace expression { + +struct Value; + +using ValueBase = variant< + NullValue, + bool, + double, + std::string, + Color, + mapbox::util::recursive_wrapper<std::vector<Value>>, + mapbox::util::recursive_wrapper<std::unordered_map<std::string, Value>>>; +struct Value : ValueBase { + using ValueBase::ValueBase; + + // Javascript's Number.MAX_SAFE_INTEGER + static uint64_t maxSafeInteger() { return 9007199254740991ULL; } + + static bool isSafeInteger(uint64_t x) { return x <= maxSafeInteger(); }; + static bool isSafeInteger(int64_t x) { + return static_cast<uint64_t>(x > 0 ? x : -x) <= maxSafeInteger(); + } + static bool isSafeInteger(double x) { + return static_cast<uint64_t>(x > 0 ? x : -x) <= maxSafeInteger(); + } + +}; + +constexpr NullValue Null = NullValue(); + +type::Type typeOf(const Value& value); +std::string stringify(const Value& value); + +/* + Returns a Type object representing the expression type that corresponds to + the value type T. (Specialized for primitives and specific array types in + the .cpp.) +*/ +template <typename T> +type::Type valueTypeToExpressionType(); + +/* + Conversions between style value types and expression::Value +*/ + +// no-op overloads +Value toExpressionValue(const Value&); + +// T = Value (just wrap in optional) +template <typename T> +std::enable_if_t<std::is_same<T, Value>::value, +optional<T>> fromExpressionValue(const Value& v) +{ + return optional<T>(v); +} + +// T = member type of Value +template <typename T> +std::enable_if_t< std::is_convertible<T, Value>::value && !std::is_same<T, Value>::value, +optional<T>> fromExpressionValue(const Value& v) +{ + return v.template is<T>() ? v.template get<T>() : optional<T>(); +} + +// real conversions +template <typename T, typename Enable = std::enable_if_t< !std::is_convertible<T, Value>::value >> +Value toExpressionValue(const T& value); + +template <typename T> +std::enable_if_t< !std::is_convertible<T, Value>::value, +optional<T>> fromExpressionValue(const Value& v); + + + +template <class T, class Enable = void> +struct ValueConverter { + using ExpressionType = T; + + static Value toExpressionValue(const T& value) { + return Value(value); + } + static optional<T> fromExpressionValue(const Value& value) { + return value.template is<T>() ? value.template get<T>() : optional<T>(); + } +}; + +template <> +struct ValueConverter<float> { + using ExpressionType = double; + static type::Type expressionType() { return type::Number; } + static Value toExpressionValue(const float value); + static optional<float> fromExpressionValue(const Value& value); +}; + +template<> +struct ValueConverter<mbgl::Value> { + static Value toExpressionValue(const mbgl::Value& value); +}; + +template <typename T, std::size_t N> +struct ValueConverter<std::array<T, N>> { + using ExpressionType = std::vector<Value>; + static type::Type expressionType() { + return type::Array(valueTypeToExpressionType<T>(), N); + } + static Value toExpressionValue(const std::array<T, N>& value); + static optional<std::array<T, N>> fromExpressionValue(const Value& value); +}; + +template <typename T> +struct ValueConverter<std::vector<T>> { + using ExpressionType = std::vector<Value>; + static type::Type expressionType() { + return type::Array(valueTypeToExpressionType<T>()); + } + static Value toExpressionValue(const std::vector<T>& value); + static optional<std::vector<T>> fromExpressionValue(const Value& value); +}; + +template <> +struct ValueConverter<Position> { + using ExpressionType = std::vector<Value>; + static type::Type expressionType() { return type::Array(type::Number, 3); } + static Value toExpressionValue(const mbgl::style::Position& value); + static optional<Position> fromExpressionValue(const Value& v); +}; + +template <typename T> +struct ValueConverter<T, std::enable_if_t< std::is_enum<T>::value >> { + using ExpressionType = std::string; + static type::Type expressionType() { return type::String; } + static Value toExpressionValue(const T& value); + static optional<T> fromExpressionValue(const Value& value); +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/camera_function.hpp b/include/mbgl/style/function/camera_function.hpp index 0fd5bcb078..25b38e3616 100644 --- a/include/mbgl/style/function/camera_function.hpp +++ b/include/mbgl/style/function/camera_function.hpp @@ -1,10 +1,18 @@ #pragma once +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/expression/interpolate.hpp> +#include <mbgl/style/expression/step.hpp> +#include <mbgl/style/expression/find_zoom_curve.hpp> +#include <mbgl/style/expression/value.hpp> +#include <mbgl/style/expression/is_constant.hpp> +#include <mbgl/style/function/convert.hpp> #include <mbgl/style/function/exponential_stops.hpp> #include <mbgl/style/function/interval_stops.hpp> #include <mbgl/util/interpolate.hpp> #include <mbgl/util/variant.hpp> + namespace mbgl { namespace style { @@ -12,30 +20,66 @@ template <class T> class CameraFunction { public: using Stops = std::conditional_t< - util::Interpolatable<T>, + util::Interpolatable<T>::value, variant< ExponentialStops<T>, IntervalStops<T>>, variant< IntervalStops<T>>>; + + CameraFunction(std::unique_ptr<expression::Expression> expression_) + : expression(std::move(expression_)), + zoomCurve(expression::findZoomCurveChecked(expression.get())) + { + assert(!expression::isZoomConstant(*expression)); + assert(expression::isFeatureConstant(*expression)); + } CameraFunction(Stops stops_) - : stops(std::move(stops_)) { - } + : stops(std::move(stops_)), + expression(stops.match([&] (const auto& s) { + return expression::Convert::toExpression(s); + })), + zoomCurve(expression::findZoomCurveChecked(expression.get())) + {} T evaluate(float zoom) const { - return stops.match([&] (const auto& s) { - return s.evaluate(zoom).value_or(T()); - }); + const expression::EvaluationResult result = expression->evaluate(expression::EvaluationContext(zoom, nullptr)); + if (result) { + const optional<T> typed = expression::fromExpressionValue<T>(*result); + return typed ? *typed : T(); + } + return T(); + } + + float interpolationFactor(const Range<float>& inputLevels, const float inputValue) const { + return zoomCurve.match( + [&](const expression::InterpolateBase* z) { + return z->interpolationFactor(Range<double> { inputLevels.min, inputLevels.max }, inputValue); + }, + [&](const expression::Step*) { return 0.0f; } + ); + } + + Range<float> getCoveringStops(const float lower, const float upper) const { + return zoomCurve.match( + [&](auto z) { return z->getCoveringStops(lower, upper); } + ); } friend bool operator==(const CameraFunction& lhs, const CameraFunction& rhs) { - return lhs.stops == rhs.stops; + return *lhs.expression == *rhs.expression; } - Stops stops; bool useIntegerZoom = false; + + // retained for compatibility with pre-expression function API + Stops stops; + +private: + std::shared_ptr<expression::Expression> expression; + const variant<const expression::InterpolateBase*, const expression::Step*> zoomCurve; }; } // namespace style diff --git a/include/mbgl/style/function/composite_function.hpp b/include/mbgl/style/function/composite_function.hpp index 43599cd333..b44bf8e6fe 100644 --- a/include/mbgl/style/function/composite_function.hpp +++ b/include/mbgl/style/function/composite_function.hpp @@ -1,5 +1,12 @@ #pragma once +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/expression/interpolate.hpp> +#include <mbgl/style/expression/step.hpp> +#include <mbgl/style/expression/find_zoom_curve.hpp> +#include <mbgl/style/expression/value.hpp> +#include <mbgl/style/expression/is_constant.hpp> +#include <mbgl/style/function/convert.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> @@ -24,7 +31,7 @@ template <class T> class CompositeFunction { public: using InnerStops = std::conditional_t< - util::Interpolatable<T>, + util::Interpolatable<T>::value, variant< ExponentialStops<T>, IntervalStops<T>, @@ -34,7 +41,7 @@ public: CategoricalStops<T>>>; using Stops = std::conditional_t< - util::Interpolatable<T>, + util::Interpolatable<T>::value, variant< CompositeExponentialStops<T>, CompositeIntervalStops<T>, @@ -43,110 +50,71 @@ 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) - } - }; - } - ); + CompositeFunction(std::unique_ptr<expression::Expression> expression_) + : expression(std::move(expression_)), + zoomCurve(expression::findZoomCurveChecked(expression.get())) + { + assert(!expression::isZoomConstant(*expression)); + assert(!expression::isFeatureConstant(*expression)); } - // 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) - }; - } - - // 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); + })), + zoomCurve(expression::findZoomCurveChecked(expression.get())) + {} + + // 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); + const expression::EvaluationResult result = expression->evaluate(expression::EvaluationContext({zoom}, &feature)); + if (result) { + const optional<T> typed = expression::fromExpressionValue<T>(*result); + return typed ? *typed : defaultValue ? *defaultValue : finalDefaultValue; } - return evaluateFinal(coveringRanges(zoom), *value, finalDefaultValue); + return defaultValue ? *defaultValue : finalDefaultValue; + } + + float interpolationFactor(const Range<float>& inputLevels, const float inputValue) const { + return zoomCurve.match( + [&](const expression::InterpolateBase* z) { + return z->interpolationFactor(Range<double> { inputLevels.min, inputLevels.max }, inputValue); + }, + [&](const expression::Step*) { return 0.0f; } + ); + } + + Range<float> getCoveringStops(const float lower, const float upper) const { + return zoomCurve.match( + [&](auto z) { return z->getCoveringStops(lower, upper); } + ); } 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); + return *lhs.expression == *rhs.expression; } std::string property; Stops stops; optional<T> 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)); - } + std::shared_ptr<expression::Expression> expression; + const variant<const expression::InterpolateBase*, const expression::Step*> zoomCurve; }; } // namespace style diff --git a/include/mbgl/style/function/convert.hpp b/include/mbgl/style/function/convert.hpp new file mode 100644 index 0000000000..9f7b7ed1f8 --- /dev/null +++ b/include/mbgl/style/function/convert.hpp @@ -0,0 +1,351 @@ +#pragma once + +#include <mbgl/style/expression/array_assertion.hpp> +#include <mbgl/style/expression/assertion.hpp> +#include <mbgl/style/expression/case.hpp> +#include <mbgl/style/expression/coalesce.hpp> +#include <mbgl/style/expression/compound_expression.hpp> +#include <mbgl/style/expression/coercion.hpp> +#include <mbgl/style/expression/interpolate.hpp> +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/expression/literal.hpp> +#include <mbgl/style/expression/match.hpp> +#include <mbgl/style/expression/step.hpp> + +#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 <mbgl/util/enum.hpp> +#include <mbgl/style/types.hpp> + +#include <string> + + +namespace mbgl { +namespace style { +namespace expression { + +namespace detail { + +class ErrorExpression : public Expression { +public: + ErrorExpression(std::string message_) : Expression(type::Error), message(std::move(message_)) {} + void eachChild(const std::function<void(const Expression&)>&) const override {} + + bool operator==(const Expression& e) const override { + return dynamic_cast<const ErrorExpression*>(&e); + } + + EvaluationResult evaluate(const EvaluationContext&) const override { + return EvaluationError{message}; + } + +private: + std::string message; +}; + +} // namespace detail + + +// Create expressions representing 'classic' (i.e. stop-based) style functions + +struct Convert { + template <typename T> + static std::unique_ptr<Literal> makeLiteral(const T& value) { + return std::make_unique<Literal>(Value(toExpressionValue(value))); + } + + static std::unique_ptr<Expression> makeGet(type::Type type, const std::string& property) { + ParsingContext ctx; + std::vector<std::unique_ptr<Expression>> getArgs; + getArgs.push_back(makeLiteral(property)); + ParseResult get = createCompoundExpression("get", std::move(getArgs), ctx); + assert(get); + assert(ctx.getErrors().size() == 0); + + std::vector<std::unique_ptr<Expression>> assertionArgs; + assertionArgs.push_back(std::move(*get)); + + return std::make_unique<Assertion>(type, std::move(assertionArgs)); + } + + static std::unique_ptr<Expression> makeZoom() { + ParsingContext ctx; + ParseResult zoom = createCompoundExpression("zoom", std::vector<std::unique_ptr<Expression>>(), ctx); + assert(zoom); + assert(ctx.getErrors().size() == 0); + return std::move(*zoom); + } + + static std::unique_ptr<Expression> makeError(std::string message) { + return std::make_unique<detail::ErrorExpression>(message); + } + + template <typename OutputType> + static ParseResult makeInterpolate(type::Type type, + std::unique_ptr<Expression> input, + std::map<double, std::unique_ptr<Expression>> convertedStops, + typename Interpolate<OutputType>::Interpolator interpolator) + { + ParseResult curve = ParseResult(std::make_unique<Interpolate<OutputType>>( + std::move(type), + std::move(interpolator), + std::move(input), + std::move(convertedStops) + )); + assert(curve); + return std::move(*curve); + } + + template <typename Key> + static ParseResult makeMatch(type::Type type, + std::unique_ptr<Expression> input, + std::map<CategoricalValue, std::unique_ptr<Expression>> stops) { + // match expression + typename Match<Key>::Branches branches; + for(auto it = stops.begin(); it != stops.end(); it++) { + assert(it->first.template is<Key>()); + Key key = it->first.template get<Key>(); + branches.emplace( + std::move(key), + std::move(it->second) + ); + } + + return ParseResult(std::make_unique<Match<Key>>(std::move(type), + std::move(input), + std::move(branches), + makeError("No matching label"))); + } + + static ParseResult makeCase(type::Type type, + std::unique_ptr<Expression> input, + std::map<CategoricalValue, std::unique_ptr<Expression>> stops) { + // case expression + std::vector<typename Case::Branch> branches; + + auto it = stops.find(true); + std::unique_ptr<Expression> true_case = it == stops.end() ? + makeError("No matching label") : + std::move(it->second); + + it = stops.find(false); + std::unique_ptr<Expression> false_case = it == stops.end() ? + makeError("No matching label") : + std::move(it->second); + + branches.push_back(std::make_pair(std::move(input), std::move(true_case))); + return ParseResult(std::make_unique<Case>(std::move(type), std::move(branches), std::move(false_case))); + } + + template <typename T> + static ParseResult fromCategoricalStops(std::map<CategoricalValue, T> stops, const std::string& property) { + assert(stops.size() > 0); + + std::map<CategoricalValue, std::unique_ptr<Expression>> convertedStops; + for(const std::pair<CategoricalValue, T>& stop : stops) { + convertedStops.emplace( + stop.first, + makeLiteral(stop.second) + ); + } + + type::Type type = valueTypeToExpressionType<T>(); + + const CategoricalValue& firstKey = stops.begin()->first; + return firstKey.match( + [&](bool) { + return makeCase(type, makeGet(type::Boolean, property), std::move(convertedStops)); + }, + [&](const std::string&) { + return makeMatch<std::string>(type, makeGet(type::String, property), std::move(convertedStops)); + }, + [&](int64_t) { + return makeMatch<int64_t>(type, makeGet(type::Number, property), std::move(convertedStops)); + } + ); + } + + template <typename T> + static std::map<double, std::unique_ptr<Expression>> convertStops(const std::map<float, T>& stops) { + std::map<double, std::unique_ptr<Expression>> convertedStops; + for(const auto& stop : stops) { + convertedStops.emplace( + stop.first, + makeLiteral(stop.second) + ); + } + return convertedStops; + } + + template <typename T> + static std::unique_ptr<Expression> toExpression(const ExponentialStops<T>& stops) + { + ParseResult e = makeInterpolate<typename ValueConverter<T>::ExpressionType>( + valueTypeToExpressionType<T>(), + makeZoom(), + convertStops(stops.stops), + ExponentialInterpolator(stops.base)); + assert(e); + return std::move(*e); + } + + template <typename T> + static std::unique_ptr<Expression> toExpression(const IntervalStops<T>& stops) + { + ParseResult e(std::make_unique<Step>(valueTypeToExpressionType<T>(), + makeZoom(), + convertStops(stops.stops))); + assert(e); + return std::move(*e); + } + + template <typename T> + static std::unique_ptr<Expression> toExpression(const std::string& property, + const ExponentialStops<T>& stops) + { + ParseResult e = makeInterpolate<typename ValueConverter<T>::ExpressionType>(valueTypeToExpressionType<T>(), + makeGet(type::Number, property), + convertStops(stops.stops), + ExponentialInterpolator(stops.base)); + assert(e); + return std::move(*e); + } + + template <typename T> + static std::unique_ptr<Expression> toExpression(const std::string& property, + const IntervalStops<T>& stops) + { + std::unique_ptr<Expression> get = makeGet(type::Number, property); + ParseResult e(std::make_unique<Step>(valueTypeToExpressionType<T>(), + std::move(get), + convertStops(stops.stops))); + assert(e); + return std::move(*e); + } + + template <typename T> + static std::unique_ptr<Expression> toExpression(const std::string& property, + const CategoricalStops<T>& stops) + { + ParseResult expr = fromCategoricalStops(stops.stops, property); + assert(expr); + return std::move(*expr); + } + + // interpolatable zoom curve + template <typename T> + static typename std::enable_if_t<util::Interpolatable<T>::value, + ParseResult> makeZoomCurve(std::map<double, std::unique_ptr<Expression>> stops) { + return makeInterpolate<typename ValueConverter<T>::ExpressionType>(valueTypeToExpressionType<T>(), + makeZoom(), + std::move(stops), + ExponentialInterpolator(1.0)); + } + + // non-interpolatable zoom curve + template <typename T> + static typename std::enable_if_t<!util::Interpolatable<T>::value, + ParseResult> makeZoomCurve(std::map<double, std::unique_ptr<Expression>> stops) { + return ParseResult(std::make_unique<Step>(valueTypeToExpressionType<T>(), makeZoom(), std::move(stops))); + } + + template <typename T> + static std::unique_ptr<Expression> toExpression(const std::string& property, + const CompositeExponentialStops<T>& stops) + { + std::map<double, std::unique_ptr<Expression>> outerStops; + for (const std::pair<float, std::map<float, T>>& stop : stops.stops) { + std::unique_ptr<Expression> get = makeGet(type::Number, property); + ParseResult innerInterpolate = makeInterpolate<typename ValueConverter<T>::ExpressionType>(valueTypeToExpressionType<T>(), + std::move(get), + convertStops(stop.second), + ExponentialInterpolator(stops.base)); + assert(innerInterpolate); + outerStops.emplace(stop.first, std::move(*innerInterpolate)); + } + + ParseResult zoomCurve = makeZoomCurve<T>(std::move(outerStops)); + assert(zoomCurve); + return std::move(*zoomCurve); + } + + template <typename T> + static std::unique_ptr<Expression> toExpression(const std::string& property, + const CompositeIntervalStops<T>& stops) + { + std::map<double, std::unique_ptr<Expression>> outerStops; + for (const std::pair<float, std::map<float, T>>& stop : stops.stops) { + std::unique_ptr<Expression> get = makeGet(type::Number, property); + ParseResult innerInterpolate(std::make_unique<Step>(valueTypeToExpressionType<T>(), + std::move(get), + convertStops(stop.second))); + assert(innerInterpolate); + outerStops.emplace(stop.first, std::move(*innerInterpolate)); + } + + ParseResult zoomCurve = makeZoomCurve<T>(std::move(outerStops)); + assert(zoomCurve); + return std::move(*zoomCurve); + } + + template <typename T> + static std::unique_ptr<Expression> toExpression(const std::string& property, + const CompositeCategoricalStops<T>& stops) + { + std::map<double, std::unique_ptr<Expression>> outerStops; + for (const std::pair<float, std::map<CategoricalValue, T>>& stop : stops.stops) { + ParseResult innerInterpolate = fromCategoricalStops(stop.second, property); + assert(innerInterpolate); + outerStops.emplace(stop.first, std::move(*innerInterpolate)); + } + + ParseResult zoomCurve = makeZoomCurve<T>(std::move(outerStops)); + assert(zoomCurve); + return std::move(*zoomCurve); + } + + + static std::unique_ptr<Expression> fromIdentityFunction(type::Type type, const std::string& property) + { + std::unique_ptr<Expression> input = type.match( + [&] (const type::StringType&) { + return makeGet(type::String, property); + }, + [&] (const type::NumberType&) { + return makeGet(type::Number, property); + }, + [&] (const type::BooleanType&) { + return makeGet(type::Boolean, property); + }, + [&] (const type::ColorType&) { + std::vector<std::unique_ptr<Expression>> args; + args.push_back(makeGet(type::String, property)); + return std::make_unique<Coercion>(type::Color, std::move(args)); + }, + [&] (const type::Array& arr) { + std::vector<std::unique_ptr<Expression>> getArgs; + getArgs.push_back(makeLiteral(property)); + ParsingContext ctx; + ParseResult get = createCompoundExpression("get", std::move(getArgs), ctx); + assert(get); + assert(ctx.getErrors().size() == 0); + return std::make_unique<ArrayAssertion>(arr, std::move(*get)); + }, + [&] (const auto&) -> std::unique_ptr<Expression> { + return makeLiteral(Null); + } + ); + + return input; + } +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/source_function.hpp b/include/mbgl/style/function/source_function.hpp index 2872c63a64..02e4b604e2 100644 --- a/include/mbgl/style/function/source_function.hpp +++ b/include/mbgl/style/function/source_function.hpp @@ -1,5 +1,7 @@ #pragma once +#include <mbgl/style/expression/is_constant.hpp> +#include <mbgl/style/function/convert.hpp> #include <mbgl/style/function/exponential_stops.hpp> #include <mbgl/style/function/interval_stops.hpp> #include <mbgl/style/function/categorical_stops.hpp> @@ -16,7 +18,7 @@ template <class T> class SourceFunction { public: using Stops = std::conditional_t< - util::Interpolatable<T>, + util::Interpolatable<T>::value, variant< ExponentialStops<T>, IntervalStops<T>, @@ -27,33 +29,48 @@ public: CategoricalStops<T>, IdentityStops<T>>>; + SourceFunction(std::unique_ptr<expression::Expression> expression_) + : expression(std::move(expression_)) + { + assert(expression::isZoomConstant(*expression)); + assert(!expression::isFeatureConstant(*expression)); + } + SourceFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {}) : property(std::move(property_)), stops(std::move(stops_)), - defaultValue(std::move(defaultValue_)) { - } + defaultValue(std::move(defaultValue_)), + expression(stops.match([&] (const IdentityStops<T>&) { + return expression::Convert::fromIdentityFunction(expression::valueTypeToExpressionType<T>(), property); + }, [&] (const auto& s) { + return expression::Convert::toExpression(property, s); + })) + {} template <class Feature> T evaluate(const Feature& feature, T finalDefaultValue) const { - optional<Value> v = feature.getValue(property); - if (!v) { - return defaultValue.value_or(finalDefaultValue); + const expression::EvaluationResult result = expression->evaluate(expression::EvaluationContext(&feature)); + if (result) { + const optional<T> typed = expression::fromExpressionValue<T>(*result); + return typed ? *typed : defaultValue ? *defaultValue : finalDefaultValue; } - return stops.match([&] (const auto& s) -> T { - return s.evaluate(*v).value_or(defaultValue.value_or(finalDefaultValue)); - }); + return defaultValue ? *defaultValue : finalDefaultValue; } friend bool operator==(const SourceFunction& lhs, const SourceFunction& rhs) { - return std::tie(lhs.property, lhs.stops, lhs.defaultValue) - == std::tie(rhs.property, rhs.stops, rhs.defaultValue); + return *lhs.expression == *rhs.expression; } + bool useIntegerZoom = false; + + // retained for compatibility with pre-expression function API std::string property; Stops stops; optional<T> defaultValue; - bool useIntegerZoom = false; + +private: + std::shared_ptr<expression::Expression> expression; }; } // namespace style diff --git a/include/mbgl/style/layer.hpp b/include/mbgl/style/layer.hpp index c6a3c0e735..eb2dbf830b 100644 --- a/include/mbgl/style/layer.hpp +++ b/include/mbgl/style/layer.hpp @@ -1,7 +1,7 @@ #pragma once #include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/any.hpp> +#include <mbgl/util/unique_any.hpp> #include <mbgl/util/immutable.hpp> #include <mbgl/style/layer_type.hpp> #include <mbgl/style/types.hpp> @@ -126,7 +126,7 @@ public: // For use in SDK bindings, which store a reference to a platform-native peer // object here, so that separately-obtained references to this object share // identical platform-native peers. - any peer; + util::unique_any peer; }; } // namespace style diff --git a/include/mbgl/style/layers/circle_layer.hpp b/include/mbgl/style/layers/circle_layer.hpp index 275a2e67a5..942dd67503 100644 --- a/include/mbgl/style/layers/circle_layer.hpp +++ b/include/mbgl/style/layers/circle_layer.hpp @@ -78,6 +78,12 @@ public: void setCirclePitchScaleTransition(const TransitionOptions&); TransitionOptions getCirclePitchScaleTransition() const; + static PropertyValue<AlignmentType> getDefaultCirclePitchAlignment(); + PropertyValue<AlignmentType> getCirclePitchAlignment() const; + void setCirclePitchAlignment(PropertyValue<AlignmentType>); + void setCirclePitchAlignmentTransition(const TransitionOptions&); + TransitionOptions getCirclePitchAlignmentTransition() const; + static DataDrivenPropertyValue<float> getDefaultCircleStrokeWidth(); DataDrivenPropertyValue<float> getCircleStrokeWidth() const; void setCircleStrokeWidth(DataDrivenPropertyValue<float>); diff --git a/include/mbgl/style/layers/custom_layer.hpp b/include/mbgl/style/layers/custom_layer.hpp index 79a353b047..bf3387f95b 100644 --- a/include/mbgl/style/layers/custom_layer.hpp +++ b/include/mbgl/style/layers/custom_layer.hpp @@ -39,6 +39,14 @@ struct CustomLayerRenderParameters { using CustomLayerRenderFunction = void (*)(void* context, const CustomLayerRenderParameters&); /** + * Called when the system has destroyed the underlying GL context. The + * `CustomLayerDeinitializeFunction` will not be called in this case, however + * `CustomLayerInitializeFunction` will be called instead to prepare for a new render. + * + */ +using CustomLayerContextLostFunction = void (*)(void* context); + +/** * Destroy any GL state needed by the custom layer, and deallocate context, if necessary. This * method is called once, from the main thread, at a point when the GL context is active. * @@ -51,8 +59,16 @@ public: CustomLayer(const std::string& id, CustomLayerInitializeFunction, CustomLayerRenderFunction, + CustomLayerContextLostFunction, CustomLayerDeinitializeFunction, void* context); + + CustomLayer(const std::string& id, + CustomLayerInitializeFunction, + CustomLayerRenderFunction, + CustomLayerDeinitializeFunction, + void* context); + ~CustomLayer() final; // Visibility diff --git a/include/mbgl/style/layers/line_layer.hpp b/include/mbgl/style/layers/line_layer.hpp index 0b49690fd8..4519296323 100644 --- a/include/mbgl/style/layers/line_layer.hpp +++ b/include/mbgl/style/layers/line_layer.hpp @@ -42,9 +42,9 @@ public: PropertyValue<LineCapType> getLineCap() const; void setLineCap(PropertyValue<LineCapType>); - static PropertyValue<LineJoinType> getDefaultLineJoin(); - PropertyValue<LineJoinType> getLineJoin() const; - void setLineJoin(PropertyValue<LineJoinType>); + static DataDrivenPropertyValue<LineJoinType> getDefaultLineJoin(); + DataDrivenPropertyValue<LineJoinType> getLineJoin() const; + void setLineJoin(DataDrivenPropertyValue<LineJoinType>); static PropertyValue<float> getDefaultLineMiterLimit(); PropertyValue<float> getLineMiterLimit() const; diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp index 8158f267c9..a72baa0b4e 100644 --- a/include/mbgl/style/layers/symbol_layer.hpp +++ b/include/mbgl/style/layers/symbol_layer.hpp @@ -98,6 +98,14 @@ public: DataDrivenPropertyValue<std::array<float, 2>> getIconOffset() const; void setIconOffset(DataDrivenPropertyValue<std::array<float, 2>>); + static DataDrivenPropertyValue<SymbolAnchorType> getDefaultIconAnchor(); + DataDrivenPropertyValue<SymbolAnchorType> getIconAnchor() const; + void setIconAnchor(DataDrivenPropertyValue<SymbolAnchorType>); + + static PropertyValue<AlignmentType> getDefaultIconPitchAlignment(); + PropertyValue<AlignmentType> getIconPitchAlignment() const; + void setIconPitchAlignment(PropertyValue<AlignmentType>); + static PropertyValue<AlignmentType> getDefaultTextPitchAlignment(); PropertyValue<AlignmentType> getTextPitchAlignment() const; void setTextPitchAlignment(PropertyValue<AlignmentType>); @@ -118,25 +126,25 @@ public: DataDrivenPropertyValue<float> getTextSize() const; void setTextSize(DataDrivenPropertyValue<float>); - static PropertyValue<float> getDefaultTextMaxWidth(); - PropertyValue<float> getTextMaxWidth() const; - void setTextMaxWidth(PropertyValue<float>); + static DataDrivenPropertyValue<float> getDefaultTextMaxWidth(); + DataDrivenPropertyValue<float> getTextMaxWidth() const; + void setTextMaxWidth(DataDrivenPropertyValue<float>); static PropertyValue<float> getDefaultTextLineHeight(); PropertyValue<float> getTextLineHeight() const; void setTextLineHeight(PropertyValue<float>); - static PropertyValue<float> getDefaultTextLetterSpacing(); - PropertyValue<float> getTextLetterSpacing() const; - void setTextLetterSpacing(PropertyValue<float>); + static DataDrivenPropertyValue<float> getDefaultTextLetterSpacing(); + DataDrivenPropertyValue<float> getTextLetterSpacing() const; + void setTextLetterSpacing(DataDrivenPropertyValue<float>); - static PropertyValue<TextJustifyType> getDefaultTextJustify(); - PropertyValue<TextJustifyType> getTextJustify() const; - void setTextJustify(PropertyValue<TextJustifyType>); + static DataDrivenPropertyValue<TextJustifyType> getDefaultTextJustify(); + DataDrivenPropertyValue<TextJustifyType> getTextJustify() const; + void setTextJustify(DataDrivenPropertyValue<TextJustifyType>); - static PropertyValue<TextAnchorType> getDefaultTextAnchor(); - PropertyValue<TextAnchorType> getTextAnchor() const; - void setTextAnchor(PropertyValue<TextAnchorType>); + static DataDrivenPropertyValue<SymbolAnchorType> getDefaultTextAnchor(); + DataDrivenPropertyValue<SymbolAnchorType> getTextAnchor() const; + void setTextAnchor(DataDrivenPropertyValue<SymbolAnchorType>); static PropertyValue<float> getDefaultTextMaxAngle(); PropertyValue<float> getTextMaxAngle() const; diff --git a/include/mbgl/style/source.hpp b/include/mbgl/style/source.hpp index cec9619451..0b6a6c72d9 100644 --- a/include/mbgl/style/source.hpp +++ b/include/mbgl/style/source.hpp @@ -2,7 +2,7 @@ #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/optional.hpp> -#include <mbgl/util/any.hpp> +#include <mbgl/util/unique_any.hpp> #include <mbgl/util/immutable.hpp> #include <mbgl/style/types.hpp> @@ -76,7 +76,7 @@ public: // For use in SDK bindings, which store a reference to a platform-native peer // object here, so that separately-obtained references to this object share // identical platform-native peers. - any peer; + util::unique_any peer; }; } // namespace style diff --git a/include/mbgl/style/sources/custom_geometry_source.hpp b/include/mbgl/style/sources/custom_geometry_source.hpp new file mode 100644 index 0000000000..a0b990b44b --- /dev/null +++ b/include/mbgl/style/sources/custom_geometry_source.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include <mbgl/style/source.hpp> +#include <mbgl/util/geo.hpp> +#include <mbgl/util/geojson.hpp> +#include <mbgl/util/range.hpp> +#include <mbgl/util/constants.hpp> + +namespace mbgl { + +class OverscaledTileID; +class CanonicalTileID; +template <class T> +class Actor; + +namespace style { + +using TileFunction = std::function<void(const CanonicalTileID&)>; + +class CustomTileLoader; + +class CustomGeometrySource : public Source { +public: + struct TileOptions { + double tolerance = 0.375; + uint16_t tileSize = util::tileSize; + uint16_t buffer = 128; + }; + + struct Options { + TileFunction fetchTileFunction; + TileFunction cancelTileFunction; + Range<uint8_t> zoomRange = { 0, 18}; + TileOptions tileOptions; + }; +public: + CustomGeometrySource(std::string id, CustomGeometrySource::Options options); + ~CustomGeometrySource() final; + void loadDescription(FileSource&) final; + void setTileData(const CanonicalTileID&, const GeoJSON&); + void invalidateTile(const CanonicalTileID&); + void invalidateRegion(const LatLngBounds&); + // Private implementation + class Impl; + const Impl& impl() const; +private: + std::unique_ptr<Actor<CustomTileLoader>> loader; +}; + +template <> +inline bool Source::is<CustomGeometrySource>() const { + return getType() == SourceType::CustomVector; +} + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp index 2dcfec51aa..372e7c7a78 100644 --- a/include/mbgl/style/sources/geojson_source.hpp +++ b/include/mbgl/style/sources/geojson_source.hpp @@ -3,6 +3,7 @@ #include <mbgl/style/source.hpp> #include <mbgl/util/geojson.hpp> #include <mbgl/util/optional.hpp> +#include <mbgl/util/constants.hpp> namespace mbgl { @@ -12,7 +13,9 @@ namespace style { struct GeoJSONOptions { // GeoJSON-VT options + uint8_t minzoom = 0; uint8_t maxzoom = 18; + uint16_t tileSize = util::tileSize; uint16_t buffer = 128; double tolerance = 0.375; diff --git a/include/mbgl/style/sources/image_source.hpp b/include/mbgl/style/sources/image_source.hpp index d8a2c45bd8..009764291f 100644 --- a/include/mbgl/style/sources/image_source.hpp +++ b/include/mbgl/style/sources/image_source.hpp @@ -18,7 +18,7 @@ public: optional<std::string> getURL() const; void setURL(const std::string& url); - void setImage(UnassociatedImage&&); + void setImage(PremultipliedImage&&); void setCoordinates(const std::array<LatLng, 4>&); std::array<LatLng, 4> getCoordinates() const; diff --git a/include/mbgl/style/style.hpp b/include/mbgl/style/style.hpp index cb84922b4d..d6fdbd8f2c 100644 --- a/include/mbgl/style/style.hpp +++ b/include/mbgl/style/style.hpp @@ -1,6 +1,7 @@ #pragma once #include <mbgl/style/transition_options.hpp> +#include <mbgl/map/camera.hpp> #include <mbgl/util/geo.hpp> #include <string> @@ -32,10 +33,7 @@ public: // Defaults std::string getName() const; - LatLng getDefaultLatLng() const; - double getDefaultZoom() const; - double getDefaultBearing() const; - double getDefaultPitch() const; + CameraOptions getDefaultCamera() const; // TransitionOptions TransitionOptions getTransitionOptions() const; diff --git a/include/mbgl/style/transition_options.hpp b/include/mbgl/style/transition_options.hpp index 1583667025..87a81717a0 100644 --- a/include/mbgl/style/transition_options.hpp +++ b/include/mbgl/style/transition_options.hpp @@ -8,8 +8,13 @@ namespace style { class TransitionOptions { public: - optional<Duration> duration = {}; - optional<Duration> delay = {}; + optional<Duration> duration; + optional<Duration> delay; + + TransitionOptions(optional<Duration> duration_ = {}, + optional<Duration> delay_ = {}) + : duration(std::move(duration_)), + delay(std::move(delay_)) {} TransitionOptions reverseMerge(const TransitionOptions& defaults) const { return { diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp index 44b16f16e7..6fe457e181 100644 --- a/include/mbgl/style/types.hpp +++ b/include/mbgl/style/types.hpp @@ -4,18 +4,19 @@ namespace mbgl { -// TODO: should be in public source.hpp header and style namespace +namespace style { + +// TODO: should be in public source.hpp header enum class SourceType : uint8_t { Vector, Raster, GeoJSON, Video, Annotations, - Image + Image, + CustomVector }; -namespace style { - enum class VisibilityType : bool { Visible, None, @@ -68,7 +69,7 @@ enum class TextJustifyType : uint8_t { Right }; -enum class TextAnchorType : uint8_t { +enum class SymbolAnchorType : uint8_t { Center, Left, Right, diff --git a/include/mbgl/tile/tile_id.hpp b/include/mbgl/tile/tile_id.hpp new file mode 100644 index 0000000000..11fb5ce537 --- /dev/null +++ b/include/mbgl/tile/tile_id.hpp @@ -0,0 +1,263 @@ +#pragma once + +#include <mbgl/util/constants.hpp> + +#include <cstdint> +#include <array> +#include <tuple> +#include <forward_list> +#include <algorithm> +#include <iosfwd> +#include <cassert> + +namespace mbgl { + +class OverscaledTileID; +class CanonicalTileID; +class UnwrappedTileID; + +// Has integer z/x/y coordinates +// All tiles must be derived from 0/0/0 (=no tiles outside of the main tile pyramid) +// Used for requesting data; represents data tiles that exist out there. +// z is never larger than the source's maxzoom +class CanonicalTileID { +public: + CanonicalTileID(uint8_t z, uint32_t x, uint32_t y); + bool operator==(const CanonicalTileID&) const; + bool operator!=(const CanonicalTileID&) const; + bool operator<(const CanonicalTileID&) const; + bool isChildOf(const CanonicalTileID&) const; + CanonicalTileID scaledTo(uint8_t z) const; + std::array<CanonicalTileID, 4> children() const; + + uint8_t z; + uint32_t x; + uint32_t y; +}; + +::std::ostream& operator<<(::std::ostream& os, const CanonicalTileID& rhs); +namespace util { +std::string toString(const CanonicalTileID&); +} // namespace util + +// Has integer z/x/y coordinates +// overscaledZ describes the zoom level this tile is intented to represent, e.g. when parsing data +// z is never larger than the source's maxzoom +// z/x/y describe the +class OverscaledTileID { +public: + OverscaledTileID(uint8_t overscaledZ, int16_t wrap, CanonicalTileID); + OverscaledTileID(uint8_t overscaledZ, int16_t wrap, uint8_t z, uint32_t x, uint32_t y); + OverscaledTileID(uint8_t z, uint32_t x, uint32_t y); + explicit OverscaledTileID(const CanonicalTileID&); + explicit OverscaledTileID(CanonicalTileID&&); + bool operator==(const OverscaledTileID&) const; + bool operator!=(const OverscaledTileID&) const; + bool operator<(const OverscaledTileID&) const; + bool isChildOf(const OverscaledTileID&) const; + uint32_t overscaleFactor() const; + OverscaledTileID scaledTo(uint8_t z) const; + UnwrappedTileID toUnwrapped() const; + + const uint8_t overscaledZ; + const int16_t wrap; + const CanonicalTileID canonical; +}; + +::std::ostream& operator<<(::std::ostream& os, const OverscaledTileID& rhs); +namespace util { +std::string toString(const OverscaledTileID&); +} // namespace util + +// Has integer z/x/y coordinates +// wrap describes tiles that are left/right of the main tile pyramid, e.g. when wrapping the world +// Used for describing what position tiles are getting rendered at (= calc the matrix) +// z is never larger than the source's maxzoom +class UnwrappedTileID { +public: + UnwrappedTileID(uint8_t z, int64_t x, int64_t y); + UnwrappedTileID(int16_t wrap, CanonicalTileID); + bool operator==(const UnwrappedTileID&) const; + bool operator!=(const UnwrappedTileID&) const; + bool operator<(const UnwrappedTileID&) const; + bool isChildOf(const UnwrappedTileID&) const; + std::array<UnwrappedTileID, 4> children() const; + OverscaledTileID overscaleTo(uint8_t z) const; + float pixelsToTileUnits(float pixelValue, float zoom) const; + + const int16_t wrap; + const CanonicalTileID canonical; +}; + +::std::ostream& operator<<(::std::ostream& os, const UnwrappedTileID& rhs); +namespace util { +std::string toString(const UnwrappedTileID&); +} // namespace util + +inline CanonicalTileID::CanonicalTileID(uint8_t z_, uint32_t x_, uint32_t y_) : z(z_), x(x_), y(y_) { + assert(z <= 32); + assert(x < (1ull << z)); + assert(y < (1ull << z)); +} + +inline bool CanonicalTileID::operator==(const CanonicalTileID& rhs) const { + return z == rhs.z && x == rhs.x && y == rhs.y; +} + +inline bool CanonicalTileID::operator!=(const CanonicalTileID& rhs) const { + return z != rhs.z || x != rhs.x || y != rhs.y; +} + +inline bool CanonicalTileID::operator<(const CanonicalTileID& rhs) const { + return std::tie(z, x, y) < std::tie(rhs.z, rhs.x, rhs.y); +} + +inline bool CanonicalTileID::isChildOf(const CanonicalTileID& parent) const { + // We're first testing for z == 0, to avoid a 32 bit shift, which is undefined. + return parent.z == 0 || + (parent.z < z && parent.x == (x >> (z - parent.z)) && parent.y == (y >> (z - parent.z))); +} + +inline CanonicalTileID CanonicalTileID::scaledTo(uint8_t targetZ) const { + if (targetZ <= z) { + return { targetZ, x >> (z - targetZ), y >> (z - targetZ) }; // parent or same + } else { + return { targetZ, x << (targetZ - z), y << (targetZ - z) }; // child + } +} + +inline std::array<CanonicalTileID, 4> CanonicalTileID::children() const { + const uint8_t childZ = z + 1; + const uint32_t childX = x * 2; + const uint32_t childY = y * 2; + return { { + { childZ, childX, childY }, + { childZ, childX, childY + 1 }, + { childZ, childX + 1, childY }, + { childZ, childX + 1, childY + 1 }, + } }; +} + +inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, int16_t wrap_, CanonicalTileID canonical_) + : overscaledZ(overscaledZ_), wrap(wrap_), canonical(std::move(canonical_)) { + assert(overscaledZ >= canonical.z); +} + +inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, int16_t wrap_, uint8_t z, uint32_t x, uint32_t y) + : overscaledZ(overscaledZ_), wrap(wrap_), canonical(z, x, y) { + assert(overscaledZ >= canonical.z); +} + +inline OverscaledTileID::OverscaledTileID(uint8_t z, uint32_t x, uint32_t y) + : overscaledZ(z), wrap(0), canonical(z, x, y) { +} + +inline OverscaledTileID::OverscaledTileID(const CanonicalTileID& canonical_) + : overscaledZ(canonical_.z), wrap(0), canonical(canonical_) { + assert(overscaledZ >= canonical.z); +} + +inline OverscaledTileID::OverscaledTileID(CanonicalTileID&& canonical_) + : overscaledZ(canonical_.z), wrap(0), canonical(std::forward<CanonicalTileID>(canonical_)) { + assert(overscaledZ >= canonical.z); +} + +inline bool OverscaledTileID::operator==(const OverscaledTileID& rhs) const { + return overscaledZ == rhs.overscaledZ && wrap == rhs.wrap &&canonical == rhs.canonical; +} + +inline bool OverscaledTileID::operator!=(const OverscaledTileID& rhs) const { + return overscaledZ != rhs.overscaledZ || wrap != rhs.wrap || canonical != rhs.canonical; +} + +inline bool OverscaledTileID::operator<(const OverscaledTileID& rhs) const { + return std::tie(overscaledZ, wrap, canonical) < std::tie(rhs.overscaledZ, rhs.wrap, rhs.canonical); +} + +inline uint32_t OverscaledTileID::overscaleFactor() const { + return 1u << (overscaledZ - canonical.z); +} + +inline bool OverscaledTileID::isChildOf(const OverscaledTileID& rhs) const { + return overscaledZ > rhs.overscaledZ && + (canonical == rhs.canonical || canonical.isChildOf(rhs.canonical)); +} + +inline OverscaledTileID OverscaledTileID::scaledTo(uint8_t z) const { + return { z, wrap, z >= canonical.z ? canonical : canonical.scaledTo(z) }; +} + +inline UnwrappedTileID OverscaledTileID::toUnwrapped() const { + return { wrap, canonical }; +} + +inline UnwrappedTileID::UnwrappedTileID(uint8_t z_, int64_t x_, int64_t y_) + : wrap((x_ < 0 ? x_ - (1ll << z_) + 1 : x_) / (1ll << z_)), + canonical( + z_, + static_cast<uint32_t>(x_ - wrap * (1ll << z_)), + y_ < 0 ? 0 : std::min(static_cast<uint32_t>(y_), static_cast<uint32_t>(1ull << z_) - 1)) { +} + +inline UnwrappedTileID::UnwrappedTileID(int16_t wrap_, CanonicalTileID canonical_) + : wrap(wrap_), canonical(std::move(canonical_)) { +} + +inline bool UnwrappedTileID::operator==(const UnwrappedTileID& rhs) const { + return wrap == rhs.wrap && canonical == rhs.canonical; +} + +inline bool UnwrappedTileID::operator!=(const UnwrappedTileID& rhs) const { + return wrap != rhs.wrap || canonical != rhs.canonical; +} + +inline bool UnwrappedTileID::operator<(const UnwrappedTileID& rhs) const { + return std::tie(wrap, canonical) < std::tie(rhs.wrap, rhs.canonical); +} + +inline bool UnwrappedTileID::isChildOf(const UnwrappedTileID& parent) const { + return wrap == parent.wrap && canonical.isChildOf(parent.canonical); +} + +inline std::array<UnwrappedTileID, 4> UnwrappedTileID::children() const { + const uint8_t childZ = canonical.z + 1; + const uint32_t childX = canonical.x * 2; + const uint32_t childY = canonical.y * 2; + return { { + { wrap, { childZ, childX, childY } }, + { wrap, { childZ, childX, childY + 1 } }, + { wrap, { childZ, childX + 1, childY } }, + { wrap, { childZ, childX + 1, childY + 1 } }, + } }; +} + +inline OverscaledTileID UnwrappedTileID::overscaleTo(const uint8_t overscaledZ) const { + assert(overscaledZ >= canonical.z); + return { overscaledZ, wrap, canonical }; +} + +inline float UnwrappedTileID::pixelsToTileUnits(const float pixelValue, const float zoom) const { + return pixelValue * (util::EXTENT / (util::tileSize * std::pow(2, zoom - canonical.z))); +} + +} // namespace mbgl + +namespace std { + +template <> +struct hash<mbgl::CanonicalTileID> { + size_t operator()(const mbgl::CanonicalTileID& id) const; +}; + +template <> +struct hash<mbgl::UnwrappedTileID> { + size_t operator()(const mbgl::UnwrappedTileID& id) const; +}; + +template <> +struct hash<mbgl::OverscaledTileID> { + size_t operator()(const mbgl::OverscaledTileID& id) const; +}; + +} // namespace std + diff --git a/include/mbgl/tile/tile_necessity.hpp b/include/mbgl/tile/tile_necessity.hpp new file mode 100644 index 0000000000..e51bf51d10 --- /dev/null +++ b/include/mbgl/tile/tile_necessity.hpp @@ -0,0 +1,15 @@ +#pragma once + +namespace mbgl { + +// Tiles can have two states: optional or required. +// - optional means that only low-cost actions should be taken to obtain the data +// (e.g. load from cache, but accept stale data) +// - required means that every effort should be taken to obtain the data (e.g. load +// from internet and keep the data fresh if it expires) +enum class TileNecessity : bool { + Optional = false, + Required = true, +}; + +} // namespace mbgl diff --git a/include/mbgl/util/any.hpp b/include/mbgl/util/any.hpp deleted file mode 100644 index eea64b188a..0000000000 --- a/include/mbgl/util/any.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include <linb/any.hpp> - -namespace mbgl { - -using linb::any; -using linb::any_cast; - -} // namespace mbgl diff --git a/include/mbgl/util/constants.hpp b/include/mbgl/util/constants.hpp index 14aaa752bc..d5e55065c4 100644 --- a/include/mbgl/util/constants.hpp +++ b/include/mbgl/util/constants.hpp @@ -19,7 +19,8 @@ constexpr float tileSize = 512; * * Positions are stored as signed 16bit integers. * One bit is lost for signedness to support features extending past the left edge of the tile. - * One bit is lost because the line vertex buffer packs 1 bit of other data into the int. + * One bit is lost because the line vertex buffer used to pack 1 bit of other data into the int. + * This is no longer the case but we're reserving this bit anyway. * One bit is lost to support features extending past the extent on the right edge of the tile. * This leaves us with 2^13 = 8192 */ @@ -37,6 +38,9 @@ constexpr double MIN_ZOOM = 0.0; constexpr double MAX_ZOOM = 25.5; constexpr float MIN_ZOOM_F = MIN_ZOOM; constexpr float MAX_ZOOM_F = MAX_ZOOM; +constexpr uint8_t DEFAULT_MAX_ZOOM = 22; + +constexpr uint8_t DEFAULT_PREFETCH_ZOOM_DELTA = 4; constexpr uint64_t DEFAULT_MAX_CACHE_SIZE = 50 * 1024 * 1024; @@ -57,7 +61,6 @@ extern const bool tileParseWarnings; extern const bool styleParseWarnings; extern const bool spriteWarnings; extern const bool renderWarnings; -extern const bool renderTree; extern const bool labelTextMissingWarning; extern const bool missingFontStackWarning; extern const bool missingFontFaceWarning; diff --git a/include/mbgl/util/convert.hpp b/include/mbgl/util/convert.hpp index c2b3d9950d..02ec7feef9 100644 --- a/include/mbgl/util/convert.hpp +++ b/include/mbgl/util/convert.hpp @@ -1,3 +1,5 @@ +#include <mbgl/util/util.hpp> + #include <array> #include <type_traits> #include <utility> @@ -7,8 +9,8 @@ namespace util { template<typename To, typename From, std::size_t Size, typename = std::enable_if_t<std::is_convertible<From, To>::value>> -constexpr std::array<To, Size> convert(const std::array<From, Size>&from) { - std::array<To, Size> to {}; +MBGL_CONSTEXPR std::array<To, Size> convert(const std::array<From, Size>&from) { + std::array<To, Size> to {{}}; std::copy(std::begin(from), std::end(from), std::begin(to)); return to; } diff --git a/include/mbgl/util/enum.hpp b/include/mbgl/util/enum.hpp index 369ca86bfd..608befd3c4 100644 --- a/include/mbgl/util/enum.hpp +++ b/include/mbgl/util/enum.hpp @@ -11,6 +11,7 @@ namespace mbgl { template <typename T> class Enum { public: + using Type = T; static const char * toString(T); static optional<T> toEnum(const std::string&); }; diff --git a/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp index 6d725b102b..60043ee156 100644 --- a/include/mbgl/util/geo.hpp +++ b/include/mbgl/util/geo.hpp @@ -154,18 +154,84 @@ public: sw.longitude() > ne.longitude(); } - bool contains(const LatLng& point) const { - return (point.latitude() >= sw.latitude() && - point.latitude() <= ne.latitude() && - point.longitude() >= sw.longitude() && - point.longitude() <= ne.longitude()); - } - - bool intersects(const LatLngBounds area) const { - return (area.ne.latitude() > sw.latitude() && - area.sw.latitude() < ne.latitude() && - area.ne.longitude() > sw.longitude() && - area.sw.longitude() < ne.longitude()); + bool crossesAntimeridian() const { + return (sw.wrapped().longitude() > ne.wrapped().longitude()); + } + + bool contains(const LatLng& point, LatLng::WrapMode wrap = LatLng::Unwrapped) const { + bool containsLatitude = point.latitude() >= sw.latitude() && + point.latitude() <= ne.latitude(); + if (!containsLatitude) { + return false; + } + + bool containsUnwrappedLongitude = point.longitude() >= sw.longitude() && + point.longitude() <= ne.longitude(); + if (containsUnwrappedLongitude) { + return true; + } else if (wrap == LatLng::Wrapped) { + LatLngBounds wrapped(sw.wrapped(), ne.wrapped()); + auto ptLon = point.wrapped().longitude(); + if (crossesAntimeridian()) { + return (ptLon >= wrapped.sw.longitude() && + ptLon <= util::LONGITUDE_MAX) || + (ptLon <= wrapped.ne.longitude() && + ptLon >= -util::LONGITUDE_MAX); + } else { + return (ptLon >= wrapped.sw.longitude() && + ptLon <= wrapped.ne.longitude()); + } + } + return false; + } + + bool contains(const LatLngBounds& area, LatLng::WrapMode wrap = LatLng::Unwrapped) const { + bool containsLatitude = area.north() <= north() && area.south() >= south(); + if (!containsLatitude) { + return false; + } + + bool containsUnwrapped = area.east() <= east() && area.west() >= west(); + if(containsUnwrapped) { + return true; + } else if (wrap == LatLng::Wrapped) { + LatLngBounds wrapped(sw.wrapped(), ne.wrapped()); + LatLngBounds other(area.sw.wrapped(), area.ne.wrapped()); + if (crossesAntimeridian() & !area.crossesAntimeridian()) { + return (other.east() <= util::LONGITUDE_MAX && other.west() >= wrapped.west()) || + (other.east() <= wrapped.east() && other.west() >= -util::LONGITUDE_MAX); + } else { + return other.east() <= wrapped.east() && other.west() >= wrapped.west(); + } + } + return false; + } + + bool intersects(const LatLngBounds area, LatLng::WrapMode wrap = LatLng::Unwrapped) const { + bool latitudeIntersects = area.north() > south() && area.south() < north(); + if (!latitudeIntersects) { + return false; + } + + bool longitudeIntersects = area.east() > west() && area.west() < east(); + if (longitudeIntersects) { + return true; + } else if (wrap == LatLng::Wrapped) { + LatLngBounds wrapped(sw.wrapped(), ne.wrapped()); + LatLngBounds other(area.sw.wrapped(), area.ne.wrapped()); + if (crossesAntimeridian()) { + return area.crossesAntimeridian() || + other.east() > wrapped.west() || + other.west() < wrapped.east(); + } else if (other.crossesAntimeridian()){ + return other.east() > wrapped.west() || + other.west() < wrapped.east(); + } else { + return other.east() > wrapped.west() && + other.west() < wrapped.east(); + } + } + return false; } private: diff --git a/include/mbgl/util/geometry.hpp b/include/mbgl/util/geometry.hpp index 6dc16bc514..a28c59a47d 100644 --- a/include/mbgl/util/geometry.hpp +++ b/include/mbgl/util/geometry.hpp @@ -2,6 +2,7 @@ #include <mapbox/geometry/geometry.hpp> #include <mapbox/geometry/point_arithmetic.hpp> +#include <mapbox/geometry/for_each_point.hpp> namespace mbgl { @@ -58,4 +59,9 @@ struct ToFeatureType { FeatureType operator()(const mapbox::geometry::geometry_collection<T> &) const { return FeatureType::Unknown; } }; +template <class T, typename F> +auto forEachPoint(const Geometry<T>& g, F f) { + mapbox::geometry::for_each_point(g, f); +} + } // namespace mbgl diff --git a/include/mbgl/util/image.hpp b/include/mbgl/util/image.hpp index 91bf06d727..4887058f79 100644 --- a/include/mbgl/util/image.hpp +++ b/include/mbgl/util/image.hpp @@ -5,6 +5,7 @@ #include <mbgl/util/size.hpp> #include <string> +#include <cstring> #include <memory> #include <algorithm> @@ -66,9 +67,9 @@ public: template <typename T = Image> T clone() const { - T copy(size); - std::copy(data.get(), data.get() + bytes(), copy.data.get()); - return copy; + T copy_(size); + std::copy(data.get(), data.get() + bytes(), copy_.data.get()); + return copy_; } size_t stride() const { return channels * size.width; } @@ -91,6 +92,31 @@ public: operator=(std::move(newImage)); } + // Clears the rect area specified by `pt` and `size` from `dstImage`. + static void clear(Image& dstImg, const Point<uint32_t>& pt, const Size& size) { + if (size.isEmpty()) { + return; + } + + if (!dstImg.valid()) { + throw std::invalid_argument("invalid destination for image clear"); + } + + if (size.width > dstImg.size.width || + size.height > dstImg.size.height || + pt.x > dstImg.size.width - size.width || + pt.y > dstImg.size.height - size.height) { + throw std::out_of_range("out of range destination coordinates for image clear"); + } + + uint8_t* dstData = dstImg.data.get(); + + for (uint32_t y = 0; y < size.height; y++) { + const std::size_t dstOffset = (pt.y + y) * dstImg.stride() + pt.x * channels; + std::memset(dstData + dstOffset, 0, size.width * channels); + } + } + // Copy image data within `rect` from `src` to the rectangle of the same size at `pt` // in `dst`. If the specified bounds exceed the bounds of the source or destination, // throw `std::out_of_range`. Must not be used to move data within a single Image. diff --git a/include/mbgl/util/indexed_tuple.hpp b/include/mbgl/util/indexed_tuple.hpp index a414639530..fd0b931d36 100644 --- a/include/mbgl/util/indexed_tuple.hpp +++ b/include/mbgl/util/indexed_tuple.hpp @@ -31,16 +31,13 @@ public: using std::tuple<Ts...>::tuple; template <class I> - static constexpr std::size_t Index = TypeIndex<I, Is...>::value; - - template <class I> auto& get() { - return std::get<Index<I>>(*this); + return std::get<TypeIndex<I, Is...>::value>(*this); } template <class I> const auto& get() const { - return std::get<Index<I>>(*this); + return std::get<TypeIndex<I, Is...>::value>(*this); } template <class... Js, class... Us> diff --git a/include/mbgl/util/interpolate.hpp b/include/mbgl/util/interpolate.hpp index a2103f18b2..aff730a0a2 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,6 +48,36 @@ public: } }; + +// 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<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<style::expression::Value> result; + for (std::size_t i = 0; i < a.size(); i++) { + assert(a[i].template is<double>()); + assert(b[i].template is<double>()); + style::expression::Value item = interpolate( + a[i].template get<double>(), + b[i].template get<double>(), + t); + result.push_back(item); + } + return result; + } +}; + template <> struct Interpolator<style::Position> { public: @@ -95,7 +126,13 @@ struct Interpolator<std::vector<T>> : Uninterpolated {}; template <class T> -constexpr bool Interpolatable = !std::is_base_of<Uninterpolated, Interpolator<T>>::value; +struct Interpolatable + : std::conditional_t< + !std::is_base_of<Uninterpolated, Interpolator<T>>::value, + std::true_type, + std::false_type> {}; + + } // namespace util } // namespace mbgl diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp index 3cc1146513..1613af3b36 100644 --- a/include/mbgl/util/projection.hpp +++ b/include/mbgl/util/projection.hpp @@ -75,10 +75,7 @@ public: } static Point<double> project(const LatLng& latLng, double scale) { - return Point<double> { - util::LONGITUDE_MAX + latLng.longitude(), - util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latLng.latitude() * M_PI / util::DEGREES_MAX)) - } * worldSize(scale) / util::DEGREES_MAX; + return project_(latLng, worldSize(scale)); } static LatLng unproject(const Point<double>& p, double scale, LatLng::WrapMode wrapMode = LatLng::Unwrapped) { @@ -89,6 +86,23 @@ public: wrapMode }; } + + // Project lat, lon to point in a zoom-dependent world size + static Point<double> project(const LatLng& point, uint8_t zoom, uint16_t tileSize) { + const double t2z = tileSize * std::pow(2, zoom); + Point<double> pt = project_(point, t2z); + // Flip y coordinate + auto x = ::round(std::min(pt.x, t2z)); + auto y = ::round(std::min(t2z - pt.y, t2z)); + return { x, y }; + } +private: + static Point<double> project_(const LatLng& latLng, double worldSize) { + return Point<double> { + util::LONGITUDE_MAX + latLng.longitude(), + util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latLng.latitude() * M_PI / util::DEGREES_MAX)) + } * worldSize / util::DEGREES_MAX; + } }; } // namespace mbgl diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp index 14352ca823..acbea80273 100644 --- a/include/mbgl/util/run_loop.hpp +++ b/include/mbgl/util/run_loop.hpp @@ -62,6 +62,12 @@ public: push(task); return std::make_unique<WorkRequest>(task); } + + void schedule(std::weak_ptr<Mailbox> mailbox) override { + invoke([mailbox] () { + Mailbox::maybeReceive(mailbox); + }); + } class Impl; @@ -72,12 +78,6 @@ private: void push(std::shared_ptr<WorkTask>); - void schedule(std::weak_ptr<Mailbox> mailbox) override { - invoke([mailbox] () { - Mailbox::maybeReceive(mailbox); - }); - } - void withMutex(std::function<void()>&& fn) { std::lock_guard<std::mutex> lock(mutex); fn(); diff --git a/include/mbgl/util/size.hpp b/include/mbgl/util/size.hpp index 45c303969c..12c0ad056b 100644 --- a/include/mbgl/util/size.hpp +++ b/include/mbgl/util/size.hpp @@ -15,6 +15,10 @@ public: constexpr uint32_t area() const { return width * height; } + + constexpr float aspectRatio() const { + return static_cast<float>(width) / static_cast<float>(height); + } constexpr bool isEmpty() const { return width == 0 || height == 0; diff --git a/include/mbgl/util/string.hpp b/include/mbgl/util/string.hpp index de061647b5..13498ccb92 100644 --- a/include/mbgl/util/string.hpp +++ b/include/mbgl/util/string.hpp @@ -1,9 +1,38 @@ #pragma once +#include <sstream> #include <string> #include <cassert> +#include <cstdlib> #include <exception> +// Polyfill needed by Qt when building for Android with GCC +#if defined(__ANDROID__) && defined(__GLIBCXX__) + +namespace std { + +template <typename T> +std::string to_string(T value) +{ + std::ostringstream oss; + oss << value; + + return oss.str(); +} + +inline int stoi(const std::string &str) +{ + return atoi(str.c_str()); +} + +inline float stof(const std::string &str) { + return static_cast<float>(atof(str.c_str())); +} + +} // namespace std + +#endif + namespace mbgl { namespace util { @@ -40,5 +69,9 @@ inline std::string toString(std::exception_ptr error) { } } +inline float stof(const std::string& str) { + return std::stof(str); +} + } // namespace util } // namespace mbgl diff --git a/include/mbgl/util/thread.hpp b/include/mbgl/util/thread.hpp new file mode 100644 index 0000000000..672eebf6db --- /dev/null +++ b/include/mbgl/util/thread.hpp @@ -0,0 +1,161 @@ +#pragma once + +#include <mbgl/actor/actor.hpp> +#include <mbgl/actor/mailbox.hpp> +#include <mbgl/actor/scheduler.hpp> +#include <mbgl/util/platform.hpp> +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/util.hpp> + +#include <cassert> +#include <future> +#include <memory> +#include <mutex> +#include <queue> +#include <string> +#include <thread> +#include <utility> + +namespace mbgl { +namespace util { + +// Manages a thread with `Object`. + +// Upon creation of this object, it launches a thread and creates an object of type `Object` +// in that thread. When the `Thread<>` object is destructed, the destructor waits +// for thread termination. The `Thread<>` constructor blocks until the thread and +// the `Object` are fully created, so after the object creation, it's safe to obtain the +// `Object` stored in this thread. The thread created will always have low priority on +// the platforms that support setting thread priority. +// +// The following properties make this class different from `ThreadPool`: +// +// - Only one thread is created. +// - `Object` will live in a single thread, providing thread affinity. +// - It is safe to use `ThreadLocal` in an `Object` managed by `Thread<>` +// - A `RunLoop` is created for the `Object` thread. +// - `Object` can use `Timer` and do asynchronous I/O, like wait for sockets events. +// +template<class Object> +class Thread : public Scheduler { +public: + template <class... Args> + Thread(const std::string& name, Args&&... args) { + std::promise<void> running; + + thread = std::thread([&] { + platform::setCurrentThreadName(name); + platform::makeThreadLowPriority(); + + util::RunLoop loop_(util::RunLoop::Type::New); + loop = &loop_; + + object = std::make_unique<Actor<Object>>(*this, std::forward<Args>(args)...); + running.set_value(); + + loop->run(); + loop = nullptr; + }); + + running.get_future().get(); + } + + ~Thread() override { + if (paused) { + resume(); + } + + std::promise<void> joinable; + + // Kill the actor, so we don't get more + // messages posted on this scheduler after + // we delete the RunLoop. + loop->invoke([&] { + object.reset(); + joinable.set_value(); + }); + + joinable.get_future().get(); + + loop->stop(); + thread.join(); + } + + // Returns a non-owning reference to `Object` that + // can be used to send messages to `Object`. It is safe + // to the non-owning reference to outlive this object + // and be used after the `Thread<>` gets destroyed. + ActorRef<std::decay_t<Object>> actor() const { + return object->self(); + } + + // Pauses the `Object` thread. It will prevent the object to wake + // up from events such as timers and file descriptor I/O. Messages + // sent to a paused `Object` will be queued and only processed after + // `resume()` is called. + void pause() { + MBGL_VERIFY_THREAD(tid); + + assert(!paused); + + paused = std::make_unique<std::promise<void>>(); + resumed = std::make_unique<std::promise<void>>(); + + auto pausing = paused->get_future(); + + loop->invoke([this] { + auto resuming = resumed->get_future(); + paused->set_value(); + resuming.get(); + }); + + pausing.get(); + } + + // Resumes the `Object` thread previously paused by `pause()`. + void resume() { + MBGL_VERIFY_THREAD(tid); + + assert(paused); + + resumed->set_value(); + + resumed.reset(); + paused.reset(); + } + +private: + MBGL_STORE_THREAD(tid); + + void schedule(std::weak_ptr<Mailbox> mailbox) override { + { + std::lock_guard<std::mutex> lock(mutex); + queue.push(mailbox); + } + + loop->invoke([this] { receive(); }); + } + + void receive() { + std::unique_lock<std::mutex> lock(mutex); + + auto mailbox = queue.front(); + queue.pop(); + lock.unlock(); + + Mailbox::maybeReceive(mailbox); + } + + std::mutex mutex; + std::queue<std::weak_ptr<Mailbox>> queue; + std::thread thread; + std::unique_ptr<Actor<Object>> object; + + std::unique_ptr<std::promise<void>> paused; + std::unique_ptr<std::promise<void>> resumed; + + util::RunLoop* loop = nullptr; +}; + +} // namespace util +} // namespace mbgl diff --git a/include/mbgl/util/tileset.hpp b/include/mbgl/util/tileset.hpp index 1256e9fe96..5a03e1a9da 100644 --- a/include/mbgl/util/tileset.hpp +++ b/include/mbgl/util/tileset.hpp @@ -1,7 +1,9 @@ #pragma once #include <mbgl/util/range.hpp> +#include <mbgl/util/constants.hpp> +#include <tuple> #include <vector> #include <string> #include <cstdint> @@ -13,9 +15,18 @@ public: enum class Scheme : bool { XYZ, TMS }; std::vector<std::string> tiles; - Range<uint8_t> zoomRange { 0, 22 }; + Range<uint8_t> zoomRange; std::string attribution; - Scheme scheme = Scheme::XYZ; + Scheme scheme; + + Tileset(std::vector<std::string> tiles_ = std::vector<std::string>(), + Range<uint8_t> zoomRange_ = { 0, util::DEFAULT_MAX_ZOOM }, + std::string attribution_ = {}, + Scheme scheme_ = Scheme::XYZ) + : tiles(std::move(tiles_)), + zoomRange(std::move(zoomRange_)), + attribution(std::move(attribution_)), + scheme(scheme_) {} // TileJSON also includes center, zoom, and bounds, but they are not used by mbgl. diff --git a/include/mbgl/util/unique_any.hpp b/include/mbgl/util/unique_any.hpp new file mode 100644 index 0000000000..d488930a03 --- /dev/null +++ b/include/mbgl/util/unique_any.hpp @@ -0,0 +1,275 @@ +#pragma once + +#include <typeinfo> +#include <type_traits> +#include <stdexcept> +namespace mbgl { +namespace util { + +class bad_any_cast : public std::bad_cast { +public: + const char* what() const noexcept override { + return "bad any_cast<>()"; + } +}; +/** + * A variant of `std::any` for non-copyable types. + * + * Use `unique_any` for non-copyable types (e.g. `std::unique_ptr<T>`) + * or to ensure that no copies are made of copyable types that are + * moved in. + * + * `uniqe_any` differs from `std::any` in that it does not support copy construction + * or copy assignment. It also does not require the contained type to be copy + * constructible. + * + * The `any_cast<T>()` methods work similar to `std::any_cast<T>()` except that + * non-copyable types may only be cast to references. + * + * Example usage: + * unique_any u1(3); + * auto u2 = unique_any(std::move(u1)); // u1 is moved from + * int i = any_cast<int>(u2); + * + * unique_any u2; + * u2 = std::unique_ptr<int>(new int); + * std::unique_ptr<int> iPtr = any_cast<std::unique_ptr<int>>(std::move(u2)); + * + * Inspired by linb::any (https://github.com/thelink2012/any) and the + * libc++ implementation (https://github.com/llvm-mirror/libcxx). + */ +class unique_any final +{ +public: + unique_any() = default; + + //Copy constructor (deleted) + unique_any(const unique_any& rhs) = delete; + + unique_any(unique_any&& rhs) : vtable(rhs.vtable) { + if (vtable) { + vtable->move(std::move(rhs.storage), storage); + } + rhs.vtable = nullptr; + } + + // Constructs with a direct-initilizated object of type ValueType + template <typename ValueType, + typename _Vt = std::decay_t<ValueType>, + typename = std::enable_if_t<!std::is_same<_Vt, unique_any>::value> > + unique_any(ValueType&& value) { + create(std::forward<ValueType>(value)); + } + + ~unique_any() { + reset(); + } + + unique_any& operator=(unique_any&& rhs) { + unique_any(std::move(rhs)).swap(*this); + return *this; + } + + template <class ValueType, + typename = std::enable_if_t<!std::is_same<std::decay_t<ValueType>, unique_any>::value> > + unique_any& operator=(ValueType&& rhs) { + unique_any(std::forward<ValueType>(rhs)).swap(*this); + return *this; + } + + void reset() { + if (vtable) { + vtable->destroy(storage); + vtable = nullptr; + } + } + + void swap(unique_any& rhs) { + if (this == &rhs) { + return; + } else { + unique_any tmp(std::move(rhs)); + rhs.vtable = vtable; + if (rhs.vtable) { + rhs.vtable->move(std::move(storage), rhs.storage); + } + vtable = tmp.vtable; + if (vtable) { + vtable->move(std::move(tmp.storage), storage); + } + } + } + + const std::type_info& type() const { + return !has_value()? typeid(void) : vtable->type(); + } + + bool has_value() const { + return vtable != nullptr; + } + +private: + + union Storage { + using StackStorage = std::aligned_storage_t<3*sizeof(void*), std::alignment_of<void*>::value>; + Storage() = default; + + void * dynamic { nullptr }; + StackStorage stack; + }; + + template<typename T> + struct AllocateOnStack : std::integral_constant<bool, + sizeof(T) <= sizeof(Storage::stack) + && std::alignment_of<T>::value <= std::alignment_of<Storage::StackStorage>::value + && std::is_nothrow_move_constructible<T>::value> { + }; + + struct VTable { + virtual ~VTable() = default; + virtual void move(Storage&& src, Storage& dest) = 0; + virtual void destroy(Storage&) = 0; + virtual const std::type_info& type() = 0; + }; + + template <typename ValueType> + struct VTableHeap : public VTable { + void move(Storage&& src, Storage& dest) override { + destroy(dest); + dest.dynamic = src.dynamic; + } + + void destroy(Storage& s) override { + if (s.dynamic) { + delete reinterpret_cast<ValueType*>(s.dynamic); + } + s.dynamic = nullptr; + } + + const std::type_info& type() override { + return typeid(ValueType); + } + }; + + template <typename ValueType> + struct VTableStack : public VTable { + void move(Storage&& src, Storage& dest) override { + auto srcValue = reinterpret_cast<ValueType&&>(src.stack); + new (static_cast<void*>(&dest.stack)) ValueType(std::move(srcValue)); + srcValue.~ValueType(); + } + + void destroy(Storage& s) override { + reinterpret_cast<ValueType&>(s.stack).~ValueType(); + } + + const std::type_info& type() override { + return typeid(ValueType); + } + }; + + template <typename ValueType> + static VTable* vtableForType() { + using VTableType = std::conditional_t<AllocateOnStack<ValueType>::value, VTableStack<ValueType>, VTableHeap<ValueType> >; + static VTableType vtable; + return &vtable; + } + + template <typename ValueType, typename _Vt> + std::enable_if_t<AllocateOnStack<_Vt>::value> + createStorage(ValueType&& value) { + new (static_cast<void*>(&storage.stack)) _Vt(std::forward<ValueType>(value)); + } + + template <typename ValueType, typename _Vt> + std::enable_if_t<!AllocateOnStack<_Vt>::value> + createStorage(ValueType&& value) { + storage.dynamic = static_cast<void*>(new _Vt(std::forward<ValueType>(value))); + } + + template <typename ValueType> + void create(ValueType&& value) { + using _Vt = std::decay_t<ValueType>; + vtable = vtableForType<_Vt>(); + createStorage<ValueType, _Vt>(std::forward<ValueType>(value)); + } + + VTable* vtable { nullptr }; + Storage storage; + +protected: + template<class ValueType> + friend const ValueType* any_cast(const unique_any* operand) ; + + template<class ValueType> + friend ValueType* any_cast(unique_any* operand) ; + + template<typename ValueType, typename _Vt = std::decay_t<ValueType> > + ValueType* cast() + { + return reinterpret_cast<ValueType *>( + AllocateOnStack<_Vt>::value ? &storage.stack : storage.dynamic); + } +}; + +template<typename ValueType> +inline const ValueType* any_cast(const unique_any* any) +{ + return any_cast<ValueType>(const_cast<unique_any *>(any)); +} + +template<typename ValueType> +inline ValueType* any_cast(unique_any* any) +{ + if(any == nullptr || any->type() != typeid(ValueType)) + return nullptr; + else + return any->cast<ValueType>(); +} + +template<typename ValueType, typename _Vt = std::decay_t<ValueType> > +inline ValueType any_cast(const unique_any& any) +{ + static_assert(std::is_constructible<ValueType, const _Vt&>::value, + "any_cast type can't construct copy of contained object"); + auto temp = any_cast<_Vt>(&any); + if (temp == nullptr) { + throw bad_any_cast(); + } + return static_cast<ValueType>(*temp); +} + +template<typename ValueType, typename _Vt = std::decay_t<ValueType> > +inline ValueType any_cast(unique_any& any) +{ + static_assert(std::is_constructible<ValueType, const _Vt&>::value, + "any_cast type can't construct copy of contained object"); + auto temp = any_cast<_Vt>(&any); + if (temp == nullptr) { + throw bad_any_cast(); + } + return static_cast<ValueType>(*temp); +} + +template<typename ValueType, typename _Vt = std::remove_cv_t<ValueType> > +inline ValueType any_cast(unique_any&& any) +{ + auto temp = any_cast<_Vt>(&any); + if (temp == nullptr) { + throw bad_any_cast(); + } + auto retValue = static_cast<ValueType>(std::move(*temp)); + any.reset(); + return std::move(retValue); +} + +} // namespace util +} // namespace mbgl + +namespace std { + +inline void swap(mbgl::util::unique_any& lhs, mbgl::util::unique_any& rhs) { + lhs.swap(rhs); +} + +} // namespace std diff --git a/include/mbgl/util/unitbezier.hpp b/include/mbgl/util/unitbezier.hpp index 3a4994917b..92f23d6718 100644 --- a/include/mbgl/util/unitbezier.hpp +++ b/include/mbgl/util/unitbezier.hpp @@ -26,6 +26,7 @@ #pragma once #include <cmath> +#include <tuple> namespace mbgl { namespace util { @@ -34,11 +35,11 @@ struct UnitBezier { // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1). constexpr UnitBezier(double p1x, double p1y, double p2x, double p2y) : cx(3.0 * p1x) - , bx(3.0 * (p2x - p1x) - cx) - , ax(1.0 - cx - bx) + , bx(3.0 * (p2x - p1x) - (3.0 * p1x)) + , ax(1.0 - (3.0 * p1x) - (3.0 * (p2x - p1x) - (3.0 * p1x))) , cy(3.0 * p1y) - , by(3.0 * (p2y - p1y) - cy) - , ay(1.0 - cy - by) { + , by(3.0 * (p2y - p1y) - (3.0 * p1y)) + , ay(1.0 - (3.0 * p1y) - (3.0 * (p2y - p1y) - (3.0 * p1y))) { } double sampleCurveX(double t) const { @@ -102,6 +103,11 @@ struct UnitBezier { double solve(double x, double epsilon) const { return sampleCurveY(solveCurveX(x, epsilon)); } + + bool operator==(const UnitBezier& rhs) const { + return std::tie(cx, bx, ax, cy, by, ay) == + std::tie(rhs.cx, rhs.bx, rhs.ax, rhs.cy, rhs.by, rhs.ay); + } private: const double cx; diff --git a/include/mbgl/util/util.hpp b/include/mbgl/util/util.hpp index c5a7cb3780..7960b40299 100644 --- a/include/mbgl/util/util.hpp +++ b/include/mbgl/util/util.hpp @@ -12,3 +12,10 @@ #define MBGL_VERIFY_THREAD(tid) #endif + +// GCC 4.9 compatibility +#if !defined(__GNUC__) || __GNUC__ >= 5 +#define MBGL_CONSTEXPR constexpr +#else +#define MBGL_CONSTEXPR inline +#endif |