From 9a55c282fecfdd76b1acdf64cef0ce2ed99472ef Mon Sep 17 00:00:00 2001 From: Gali Nelle Date: Wed, 25 Mar 2020 17:35:44 +0200 Subject: [PATCH] Add LocationIndicatorLayer New key is "G" in mbgl-glfw, cycling between no puck, centered in the viewport and positioned in Tokyo. --- CMakeLists.txt | 14 +- .../location_indicator_layer_factory.hpp | 15 + include/mbgl/style/conversion/constant.hpp | 5 + .../mbgl/style/conversion/property_value.hpp | 4 + include/mbgl/style/layers/layer.hpp.ejs | 2 +- .../style/layers/location_indicator_layer.hpp | 115 +++ metrics/cache-metrics.db | Bin 397312 -> 401408 bytes .../location_indicator/dateline/metrics.json | 47 ++ .../location_indicator/default/metrics.json | 47 ++ .../no_radius_border/metrics.json | 47 ++ .../no_radius_fill/metrics.json | 47 ++ .../no_textures/metrics.json | 47 ++ .../one_texture/metrics.json | 47 ++ .../location_indicator/rotated/metrics.json | 47 ++ .../location_indicator/tilted/metrics.json | 47 ++ .../tilted_texture_shift/metrics.json | 47 ++ .../metrics.json | 47 ++ .../metrics.json | 47 ++ .../metrics.json | 47 ++ .../metrics.json | 47 ++ .../two_textures/metrics.json | 47 ++ .../location_indicator/dateline/expected.png | Bin 0 -> 9307 bytes .../location_indicator/dateline/style.json | 68 ++ .../location_indicator/default/expected.png | Bin 0 -> 13669 bytes .../location_indicator/default/style.json | 100 +++ .../no_radius_border/expected.png | Bin 0 -> 9301 bytes .../no_radius_border/style.json | 68 ++ .../no_radius_fill/expected.png | Bin 0 -> 7625 bytes .../no_radius_fill/style.json | 68 ++ .../no_textures/expected.png | Bin 0 -> 2392 bytes .../location_indicator/no_textures/style.json | 65 ++ .../one_texture/expected.png | Bin 0 -> 3500 bytes .../location_indicator/one_texture/style.json | 66 ++ .../location_indicator/rotated/expected.png | Bin 0 -> 9452 bytes .../location_indicator/rotated/style.json | 68 ++ .../location_indicator/tilted/expected.png | Bin 0 -> 5958 bytes .../location_indicator/tilted/style.json | 68 ++ .../tilted_texture_shift/expected.png | Bin 0 -> 6502 bytes .../tilted_texture_shift/style.json | 68 ++ .../expected.png | Bin 0 -> 6396 bytes .../style.json | 69 ++ .../expected.png | Bin 0 -> 5996 bytes .../style.json | 68 ++ .../expected.png | Bin 0 -> 3327 bytes .../tilted_texture_shift_top_left/style.json | 68 ++ .../expected.png | Bin 0 -> 3238 bytes .../tilted_texture_shift_top_right/style.json | 68 ++ .../two_textures/expected.png | Bin 0 -> 8945 bytes .../two_textures/style.json | 67 ++ .../src/mbgl/layermanager/layer_manager.cpp | 7 + platform/glfw/CMakeLists.txt | 9 + platform/glfw/assets/puck.png | Bin 0 -> 5663 bytes platform/glfw/assets/puck_hat.png | Bin 0 -> 6843 bytes platform/glfw/assets/puck_shadow.png | Bin 0 -> 28077 bytes platform/glfw/glfw_view.cpp | 112 ++- platform/glfw/glfw_view.hpp | 10 + scripts/generate-style-code.js | 4 +- scripts/style-spec.js | 183 +++- src/mbgl/gl/defines.hpp | 6 +- src/mbgl/layermanager/layer_manager.cpp | 9 +- .../location_indicator_layer_factory.cpp | 25 + .../render_location_indicator_layer.cpp | 786 ++++++++++++++++++ .../render_location_indicator_layer.hpp | 30 + src/mbgl/style/conversion/constant.cpp | 22 + src/mbgl/style/conversion/property_value.cpp | 19 + src/mbgl/style/conversion/stringify.hpp | 9 + src/mbgl/style/expression/value.cpp | 4 + src/mbgl/style/layers/layer.cpp.ejs | 7 +- .../style/layers/layer_properties.cpp.ejs | 4 +- .../style/layers/layer_properties.hpp.ejs | 2 +- .../style/layers/location_indicator_layer.cpp | 626 ++++++++++++++ .../layers/location_indicator_layer_impl.cpp | 12 + .../layers/location_indicator_layer_impl.hpp | 34 + .../location_indicator_layer_properties.cpp | 35 + .../location_indicator_layer_properties.hpp | 114 +++ 75 files changed, 3843 insertions(+), 18 deletions(-) create mode 100644 include/mbgl/layermanager/location_indicator_layer_factory.hpp create mode 100644 include/mbgl/style/layers/location_indicator_layer.hpp create mode 100644 metrics/linux-gcc8-release/location_indicator/dateline/metrics.json create mode 100644 metrics/linux-gcc8-release/location_indicator/default/metrics.json create mode 100644 metrics/linux-gcc8-release/location_indicator/no_radius_border/metrics.json create mode 100644 metrics/linux-gcc8-release/location_indicator/no_radius_fill/metrics.json create mode 100644 metrics/linux-gcc8-release/location_indicator/no_textures/metrics.json create mode 100644 metrics/linux-gcc8-release/location_indicator/one_texture/metrics.json create mode 100644 metrics/linux-gcc8-release/location_indicator/rotated/metrics.json create mode 100644 metrics/linux-gcc8-release/location_indicator/tilted/metrics.json create mode 100644 metrics/linux-gcc8-release/location_indicator/tilted_texture_shift/metrics.json create mode 100644 metrics/linux-gcc8-release/location_indicator/tilted_texture_shift_bottom_left/metrics.json create mode 100644 metrics/linux-gcc8-release/location_indicator/tilted_texture_shift_bottom_right/metrics.json create mode 100644 metrics/linux-gcc8-release/location_indicator/tilted_texture_shift_top_left/metrics.json create mode 100644 metrics/linux-gcc8-release/location_indicator/tilted_texture_shift_top_right/metrics.json create mode 100644 metrics/linux-gcc8-release/location_indicator/two_textures/metrics.json create mode 100644 metrics/tests/location_indicator/dateline/expected.png create mode 100644 metrics/tests/location_indicator/dateline/style.json create mode 100644 metrics/tests/location_indicator/default/expected.png create mode 100644 metrics/tests/location_indicator/default/style.json create mode 100644 metrics/tests/location_indicator/no_radius_border/expected.png create mode 100644 metrics/tests/location_indicator/no_radius_border/style.json create mode 100644 metrics/tests/location_indicator/no_radius_fill/expected.png create mode 100644 metrics/tests/location_indicator/no_radius_fill/style.json create mode 100644 metrics/tests/location_indicator/no_textures/expected.png create mode 100644 metrics/tests/location_indicator/no_textures/style.json create mode 100644 metrics/tests/location_indicator/one_texture/expected.png create mode 100644 metrics/tests/location_indicator/one_texture/style.json create mode 100644 metrics/tests/location_indicator/rotated/expected.png create mode 100644 metrics/tests/location_indicator/rotated/style.json create mode 100644 metrics/tests/location_indicator/tilted/expected.png create mode 100644 metrics/tests/location_indicator/tilted/style.json create mode 100644 metrics/tests/location_indicator/tilted_texture_shift/expected.png create mode 100644 metrics/tests/location_indicator/tilted_texture_shift/style.json create mode 100644 metrics/tests/location_indicator/tilted_texture_shift_bottom_left/expected.png create mode 100644 metrics/tests/location_indicator/tilted_texture_shift_bottom_left/style.json create mode 100644 metrics/tests/location_indicator/tilted_texture_shift_bottom_right/expected.png create mode 100644 metrics/tests/location_indicator/tilted_texture_shift_bottom_right/style.json create mode 100644 metrics/tests/location_indicator/tilted_texture_shift_top_left/expected.png create mode 100644 metrics/tests/location_indicator/tilted_texture_shift_top_left/style.json create mode 100644 metrics/tests/location_indicator/tilted_texture_shift_top_right/expected.png create mode 100644 metrics/tests/location_indicator/tilted_texture_shift_top_right/style.json create mode 100644 metrics/tests/location_indicator/two_textures/expected.png create mode 100644 metrics/tests/location_indicator/two_textures/style.json create mode 100644 platform/glfw/assets/puck.png create mode 100644 platform/glfw/assets/puck_hat.png create mode 100644 platform/glfw/assets/puck_shadow.png create mode 100644 src/mbgl/layermanager/location_indicator_layer_factory.cpp create mode 100644 src/mbgl/renderer/layers/render_location_indicator_layer.cpp create mode 100644 src/mbgl/renderer/layers/render_location_indicator_layer.hpp create mode 100644 src/mbgl/style/layers/location_indicator_layer.cpp create mode 100644 src/mbgl/style/layers/location_indicator_layer_impl.cpp create mode 100644 src/mbgl/style/layers/location_indicator_layer_impl.hpp create mode 100644 src/mbgl/style/layers/location_indicator_layer_properties.cpp create mode 100644 src/mbgl/style/layers/location_indicator_layer_properties.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 89096358d..fcce30396 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -825,7 +825,9 @@ if(MBGL_WITH_OPENGL) ${PROJECT_SOURCE_DIR}/include/mbgl/gl/custom_layer_factory.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gl/renderable_resource.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gl/renderer_backend.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/location_indicator_layer_factory.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/platform/gl_functions.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/location_indicator_layer.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/attribute.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/attribute.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/command_encoder.cpp @@ -851,12 +853,12 @@ if(MBGL_WITH_OPENGL) ${PROJECT_SOURCE_DIR}/src/mbgl/gl/offscreen_texture.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/offscreen_texture.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/render_custom_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/render_custom_layer.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/render_pass.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/render_pass.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/renderbuffer_resource.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/renderer_backend.cpp - ${PROJECT_SOURCE_DIR}/src/mbgl/gl/render_custom_layer.cpp - ${PROJECT_SOURCE_DIR}/src/mbgl/gl/render_custom_layer.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/state.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/texture.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/texture.hpp @@ -874,6 +876,7 @@ if(MBGL_WITH_OPENGL) ${PROJECT_SOURCE_DIR}/src/mbgl/gl/vertex_array_extension.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/vertex_buffer_resource.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/gl/vertex_buffer_resource.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/location_indicator_layer_factory.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/platform/gl_functions.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/background.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/background_pattern.cpp @@ -906,6 +909,13 @@ if(MBGL_WITH_OPENGL) ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/symbol_sdf_icon.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/symbol_sdf_text.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/symbol_text_and_icon.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_location_indicator_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_location_indicator_layer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/location_indicator_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/location_indicator_layer_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/location_indicator_layer_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/location_indicator_layer_properties.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/location_indicator_layer_properties.hpp ) endif() diff --git a/include/mbgl/layermanager/location_indicator_layer_factory.hpp b/include/mbgl/layermanager/location_indicator_layer_factory.hpp new file mode 100644 index 000000000..8d8e3b9a4 --- /dev/null +++ b/include/mbgl/layermanager/location_indicator_layer_factory.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace mbgl { + +class LocationIndicatorLayerFactory : public LayerFactory { +protected: + const style::LayerTypeInfo* getTypeInfo() const noexcept final; + std::unique_ptr createLayer(const std::string& id, + const style::conversion::Convertible& value) noexcept final; + std::unique_ptr createRenderLayer(Immutable) noexcept final; +}; + +} // namespace mbgl diff --git a/include/mbgl/style/conversion/constant.hpp b/include/mbgl/style/conversion/constant.hpp index 3a5833be6..4acddfb01 100644 --- a/include/mbgl/style/conversion/constant.hpp +++ b/include/mbgl/style/conversion/constant.hpp @@ -54,6 +54,11 @@ struct Converter> { optional> operator()(const Convertible& value, Error& error) const; }; +template +struct Converter> { + optional> operator()(const Convertible& value, Error& error) const; +}; + template <> struct Converter> { optional> operator()(const Convertible& value, Error& error) const; diff --git a/include/mbgl/style/conversion/property_value.hpp b/include/mbgl/style/conversion/property_value.hpp index 61360b744..925f97af1 100644 --- a/include/mbgl/style/conversion/property_value.hpp +++ b/include/mbgl/style/conversion/property_value.hpp @@ -47,6 +47,10 @@ struct Converter> { } }; +template <> +struct Converter>, void> { + optional>> operator()(const Convertible& value, Error& error, bool, bool) const; +}; } // namespace conversion } // namespace style } // namespace mbgl diff --git a/include/mbgl/style/layers/layer.hpp.ejs b/include/mbgl/style/layers/layer.hpp.ejs index 9cdd74737..a083829f6 100644 --- a/include/mbgl/style/layers/layer.hpp.ejs +++ b/include/mbgl/style/layers/layer.hpp.ejs @@ -29,7 +29,7 @@ class TransitionOptions; class <%- camelize(type) %>Layer : public Layer { public: -<% if (type === 'background') { -%> +<% if ((type === 'background') || (type === 'location-indicator')) { -%> <%- camelize(type) %>Layer(const std::string& layerID); <% } else { -%> <%- camelize(type) %>Layer(const std::string& layerID, const std::string& sourceID); diff --git a/include/mbgl/style/layers/location_indicator_layer.hpp b/include/mbgl/style/layers/location_indicator_layer.hpp new file mode 100644 index 000000000..e9e67c1a2 --- /dev/null +++ b/include/mbgl/style/layers/location_indicator_layer.hpp @@ -0,0 +1,115 @@ +// clang-format off + +// This file is generated. Do not edit. + +#pragma once + +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +class TransitionOptions; + +class LocationIndicatorLayer : public Layer { +public: + LocationIndicatorLayer(const std::string& layerID); + ~LocationIndicatorLayer() final; + + // Layout properties + + static PropertyValue getDefaultBearing(); + const PropertyValue& getBearing() const; + void setBearing(const PropertyValue&); + + static PropertyValue getDefaultBearingImage(); + const PropertyValue& getBearingImage() const; + void setBearingImage(const PropertyValue&); + + static PropertyValue getDefaultImageTiltDisplacement(); + const PropertyValue& getImageTiltDisplacement() const; + void setImageTiltDisplacement(const PropertyValue&); + + static PropertyValue getDefaultPerspectiveCompensation(); + const PropertyValue& getPerspectiveCompensation() const; + void setPerspectiveCompensation(const PropertyValue&); + + static PropertyValue getDefaultShadowImage(); + const PropertyValue& getShadowImage() const; + void setShadowImage(const PropertyValue&); + + static PropertyValue getDefaultTopImage(); + const PropertyValue& getTopImage() const; + void setTopImage(const PropertyValue&); + + // Paint properties + + static PropertyValue getDefaultAccuracyRadius(); + const PropertyValue& getAccuracyRadius() const; + void setAccuracyRadius(const PropertyValue&); + void setAccuracyRadiusTransition(const TransitionOptions&); + TransitionOptions getAccuracyRadiusTransition() const; + + static PropertyValue getDefaultAccuracyRadiusBorderColor(); + const PropertyValue& getAccuracyRadiusBorderColor() const; + void setAccuracyRadiusBorderColor(const PropertyValue&); + void setAccuracyRadiusBorderColorTransition(const TransitionOptions&); + TransitionOptions getAccuracyRadiusBorderColorTransition() const; + + static PropertyValue getDefaultAccuracyRadiusColor(); + const PropertyValue& getAccuracyRadiusColor() const; + void setAccuracyRadiusColor(const PropertyValue&); + void setAccuracyRadiusColorTransition(const TransitionOptions&); + TransitionOptions getAccuracyRadiusColorTransition() const; + + static PropertyValue getDefaultBearingImageSize(); + const PropertyValue& getBearingImageSize() const; + void setBearingImageSize(const PropertyValue&); + void setBearingImageSizeTransition(const TransitionOptions&); + TransitionOptions getBearingImageSizeTransition() const; + + static PropertyValue> getDefaultLocation(); + const PropertyValue>& getLocation() const; + void setLocation(const PropertyValue>&); + void setLocationTransition(const TransitionOptions&); + TransitionOptions getLocationTransition() const; + + static PropertyValue getDefaultShadowImageSize(); + const PropertyValue& getShadowImageSize() const; + void setShadowImageSize(const PropertyValue&); + void setShadowImageSizeTransition(const TransitionOptions&); + TransitionOptions getShadowImageSizeTransition() const; + + static PropertyValue getDefaultTopImageSize(); + const PropertyValue& getTopImageSize() const; + void setTopImageSize(const PropertyValue&); + void setTopImageSizeTransition(const TransitionOptions&); + TransitionOptions getTopImageSizeTransition() const; + + // Private implementation + + class Impl; + const Impl& impl() const; + + Mutable mutableImpl() const; + LocationIndicatorLayer(Immutable); + std::unique_ptr cloneRef(const std::string& id) const final; + +protected: + // Dynamic properties + optional setPropertyInternal(const std::string& name, const conversion::Convertible& value) final; + + StyleProperty getProperty(const std::string& name) const final; + Value serialize() const final; + + Mutable mutableBaseImpl() const final; +}; + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/metrics/cache-metrics.db b/metrics/cache-metrics.db index b52622fb7f9e771e867e84d77ee3a7d31a960e40..53ddff9bf6af2ad2735592803c16a49aec086bc3 100644 GIT binary patch delta 40849 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

2!Uq7! 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 ST{IlHql+uPv;4zLF{BJ-S zL->F5f8+ni|7PRFLgvk_$`L%%@76Q&Pky5>A;8Ah%fN5T|A_B7UoU?@|4yLZO1{m# zc141WY?}gD3Iy2rPc!iU;D68mjQ=kG75>ve1>5;2Z#d7|eBpfih4YL+%ml>D+b^7F zFzkIf3>@0dX`C#{h9G5XS*=JP;=UG02kH8T&p`+L#zAuI1e(Ni#}i$x_!=!rT@~LLp_FhqP#;B+53GTOvz0 zg~UvYB!!lV5;J7qhOy7~e2@G7em>v7;p>OTabD-V&N;7rk>u*UQ$=x!A^Ix`lQeAwx`_Ney8l*<+vb>$-Q(> z_fp)dpRJFUf4lfw*6A1d_?>mD@2q=r&@gIGRbGDiAF223E-$dJHrNv~H{Hm|8Vc)c ztgdQkY5YAyP(5XD>ban&Tilzqc5Uuzcr4j!%i0lLH~^|T#QDFsslzX(Y%@Q#hKRuL zd-eZM0hrtA+9b36+}H}f0;tCTc)P>%hOX)i^+h)T_Xv>i%PoHB+TP-|19lv(YbOz)atLU3v=d(hGzuAjXe3Ej16))l0(yDMtOkO~ zLt39wER+sh24GklPVcc3cM>@OTYeURT?zIZzB)%G`oVcUi2VeeqZ{X{0y5;`#Dd4d z---nYI2aGWDJ$R-8EeKp<9s5`pA1seD2xMcaH|HKN9uF6E#P`05q!DO?Xqi$ z9XJG~{^&M=;VF``%vM>r-gK8(vkKxT$syRf6?2-=8g_u93>R%3$Zaj8>j07rf=$AM zqnAT~JOtQ4Kr&McC?o*drWA3KL>cZTAXqqal6%SJl*U)-kF(1AkQ8-!kZ03dh`W6I z9amR|7yqdS*C7ZtTfcEm*Y@>J`7DSe3BFc*$V-@e>PwR^F(0L&WT0+wyGZ&lyITWU z>V}XJb#O6L*#ob#f7cYb_#(~4RR()+1IWH47ipEl?AcevskpEGt1?Ei9l&&npo=p8 z1%GSLNJzvV#}r*Vn-YH%-!fwoAVYf#Q5z-|OJ`n`@mGpA32UBjvwZI{e`p?r=t4+j z)z1)v2#Mtx-*;aHzjt@WCjTdo+;9oFgNu;nUHY#$xU(k1c(vy$WnE0-2;40l&3 zQ?Ag8AEsbNz?O)D6DjOL#+Nueznfe6I=$QP9l>5OSo!w(#FM3ZO2u{+9K_F^+!i>G z9twLG`6QCDYP_(UTRzxQ&+=_|>`G5GYiekHZtr)X;obAOJAFf2&YbSoxN54Qy?$xR zVvEh@-7fMEXCt7QJ$^r6Kv5b_FLsDu?Ed z>O|OK#qtz0L>}0pDCpE2A7oj~skM2FesxpryGk@`oO<04c-l*^E$wO9^~?Rl&YHUO z3sIjH0rlA0b*+|5d8wG-cQ*yw0BoUh`~~+jvT_*Q58UNrsgGh8^S&C#?tM~KFMY0A zz9Uf-bw>BP!Xr|}8lI|((4P>~Cr2?xpqQfSKt9t>hW@y_gvj<3%uLP2T1%ph21GX` zPFfb%bQv0g{S2p^^jwQh2OAT@^8)5}dNfaWL6AFoej9Fzh%#=@(~J!zJ4nw2cUd#S z^%n4rrwae6HtTf!hZU?TnOd`

Jv3HTUF|#cfRuEyWFe%PlJgt|Qh~JP$&+H}fgU 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 literal 0 HcmV?d00001 diff --git a/metrics/tests/location_indicator/dateline/style.json b/metrics/tests/location_indicator/dateline/style.json new file mode 100644 index 000000000..410fc5c53 --- /dev/null +++ b/metrics/tests/location_indicator/dateline/style.json @@ -0,0 +1,68 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 512, + "height": 256, + "operations": [ + [ + "addImage", + "puck_hat", + "puck_hat.png" + ], + [ + "addImage", + "puck", + "puck.png" + ], + [ + "addImage", + "puck_shadow", + "puck_shadow.png" + ] + ] + } + }, + "center": [ 139.766707, 35.693055 ], + "zoom": 16, + "pitch" : 0, + "bearing" : 42, + "sources": {}, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "white" + } + }, + { + "id": "puck123", + "type": "location-indicator", + "layout" : { "bearing" : 45, + "bearing-image" : "puck", + "top-image" : "puck_hat", + "shadow-image" : "puck_shadow", + "perspective-compensation" : 1, + "image-tilt-displacement" : 5 + }, + "paint" : { + "accuracy-radius-transition": { "duration": 0, "delay": 0 }, + "bearing-image-size-transition": { "duration": 0, "delay": 0 }, + "top-image-size-transition": { "duration": 0, "delay": 0 }, + "shadow-image-size-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-color-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-border-color-transition": { "duration": 0, "delay": 0 }, + + "location" : [ 35.693055, 139.766707, 0], + "accuracy-radius": 90.0, + "bearing-image-size" : 48, + "top-image-size" : 16, + "shadow-image-size" : 112, + + "accuracy-radius-color": "rgba(255,0,0,0.2)", + "accuracy-radius-border-color": "rgba(255,0,255,0.6)" + } + } + ] +} diff --git a/metrics/tests/location_indicator/default/expected.png b/metrics/tests/location_indicator/default/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..5823cce89e4e3c9610ab59073367d73e5a9ab698 GIT binary patch literal 13669 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�H&`V97OdV@@eA2W>=UWDwAYcQS}sLTK_nf&U6g^* zxei{|lf+#I^A%fN5U-lwNeL�GGt}zZU9!4#c!yvH|s{PoTB`gzo#S9ea1Ahv+b_ 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 literal 0 HcmV?d00001 diff --git a/metrics/tests/location_indicator/default/style.json b/metrics/tests/location_indicator/default/style.json new file mode 100644 index 000000000..b0f58b405 --- /dev/null +++ b/metrics/tests/location_indicator/default/style.json @@ -0,0 +1,100 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 512, + "height": 256, + "operations": [ + [ + "addImage", + "puck_hat", + "puck_hat.png" + ], + [ + "addImage", + "puck", + "puck.png" + ], + [ + "addImage", + "puck_shadow", + "puck_shadow.png" + ] + ] + } + }, + "sources": { + "geojson": { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + }, + "geometry": { + "type": "Point", + "coordinates": [ + 139.766707, + 35.693055 + + ] + } + } + ] + } + } + }, + "center": [ 139.766707, 35.693055 ], + "zoom": 16, + "pitch" : 0, + "bearing" : 42, + "sources": {}, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "lightseagreen" + } + }, + { + "id": "puck123", + "type": "circle", + "source": "geojson", + "paint": { + "circle-radius": 40, + "circle-color" : "rgba(255,0,0,0.1)" + } + }, + { + "id": "puck124", + "type": "location-indicator", + "layout" : { "bearing" : 45, + "bearing-image" : "puck", + "top-image" : "puck_hat", + "shadow-image" : "puck_shadow", + "perspective-compensation" : 1, + "image-tilt-displacement" : 5 + }, + "paint" : { + "accuracy-radius-transition": { "duration": 0, "delay": 0 }, + "bearing-image-size-transition": { "duration": 0, "delay": 0 }, + "top-image-size-transition": { "duration": 0, "delay": 0 }, + "shadow-image-size-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-color-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-border-color-transition": { "duration": 0, "delay": 0 }, + + "location" : [ 35.693055, 139.766707, 0], + "accuracy-radius": 90.0, + "bearing-image-size" : 48, + "top-image-size" : 16, + "shadow-image-size" : 112, + + "accuracy-radius-color": "rgba(255,0,0,0.2)", + "accuracy-radius-border-color": "rgba(255,0,255,0.6)" + } + } + ] +} diff --git a/metrics/tests/location_indicator/no_radius_border/expected.png b/metrics/tests/location_indicator/no_radius_border/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..436a27ed08d927c41984d60fee0df110efeadacd GIT binary patch literal 9301 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 literal 0 HcmV?d00001 diff --git a/metrics/tests/location_indicator/no_radius_border/style.json b/metrics/tests/location_indicator/no_radius_border/style.json new file mode 100644 index 000000000..922cd0c0f --- /dev/null +++ b/metrics/tests/location_indicator/no_radius_border/style.json @@ -0,0 +1,68 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 512, + "height": 256, + "operations": [ + [ + "addImage", + "puck_hat", + "puck_hat.png" + ], + [ + "addImage", + "puck", + "puck.png" + ], + [ + "addImage", + "puck_shadow", + "puck_shadow.png" + ] + ] + } + }, + "center": [ 139.766707, 35.693055 ], + "zoom": 16, + "pitch" : 0, + "bearing" : 42, + "sources": {}, + "sprite" : {}, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "white" + } + }, + { + "id": "puck123", + "type": "location-indicator", + "layout" : { "bearing" : 45, + "bearing-image" : "puck", + "top-image" : "puck_hat", + "shadow-image" : "puck_shadow", + "perspective-compensation" : 1, + "image-tilt-displacement" : 5 + }, + "paint" : { + "accuracy-radius-transition": { "duration": 0, "delay": 0 }, + "bearing-image-size-transition": { "duration": 0, "delay": 0 }, + "top-image-size-transition": { "duration": 0, "delay": 0 }, + "shadow-image-size-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-color-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-border-color-transition": { "duration": 0, "delay": 0 }, + + "location" : [ 35.693055, 139.766707, 0], + "accuracy-radius": 90.0, + "bearing-image-size" : 48, + "top-image-size" : 16, + "shadow-image-size" : 112, + + "accuracy-radius-color": "rgba(255,0,0,0.2)" + } + } + ] +} diff --git a/metrics/tests/location_indicator/no_radius_fill/expected.png b/metrics/tests/location_indicator/no_radius_fill/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..447546c880e6c9d184795908e21773965c97fe8e GIT binary patch literal 7625 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 literal 0 HcmV?d00001 diff --git a/metrics/tests/location_indicator/no_radius_fill/style.json b/metrics/tests/location_indicator/no_radius_fill/style.json new file mode 100644 index 000000000..a01ee0c57 --- /dev/null +++ b/metrics/tests/location_indicator/no_radius_fill/style.json @@ -0,0 +1,68 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 512, + "height": 256, + "operations": [ + [ + "addImage", + "puck_hat", + "puck_hat.png" + ], + [ + "addImage", + "puck", + "puck.png" + ], + [ + "addImage", + "puck_shadow", + "puck_shadow.png" + ] + ] + } + }, + "center": [ 139.766707, 35.693055 ], + "zoom": 16, + "pitch" : 0, + "bearing" : 42, + "sources": {}, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "white" + } + }, + { + "id": "puck123", + "type": "location-indicator", + "layout" : { "bearing" : 45, + "bearing-image" : "puck", + "top-image" : "puck_hat", + "shadow-image" : "puck_shadow", + "perspective-compensation" : 1, + "image-tilt-displacement" : 5 + }, + "paint" : { + "accuracy-radius-transition": { "duration": 0, "delay": 0 }, + "bearing-image-size-transition": { "duration": 0, "delay": 0 }, + "top-image-size-transition": { "duration": 0, "delay": 0 }, + "shadow-image-size-transition": { "duration": 0, "delay": 0 }, + + "accuracy-radius-color-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-border-color-transition": { "duration": 0, "delay": 0 }, + + "location" : [ 35.693055, 139.766707, 0], + "accuracy-radius": 90.0, + "bearing-image-size" : 48, + "top-image-size" : 16, + "shadow-image-size" : 112, + + "accuracy-radius-border-color": "rgba(255,0,255,0.6)" + } + } + ] +} diff --git a/metrics/tests/location_indicator/no_textures/expected.png b/metrics/tests/location_indicator/no_textures/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..8ac31bf4dfd6cff2590811e31e6186626f6bc91c GIT binary patch literal 2392 zcmcguYfw{X8h*ZygCrDz5Cw54Bq$&iSGH6ZhFhwk;s~Up-36*boOXxZ6@djWq@X!P zTWEC#W@RdhOK_ZNe^g7Rw5E=<9G5cMiY@-ao7&!%db^jnEq~G@w)AS zC-3B63cC0=TXuVG{5`(t^($$+-u(5@7iK*E;c<87@pI|>kJ~8*7XE1K07{by@SGUP z%>ab0EOuZ3psxq`cQ_zsJ)o99W0T)Ke)0$H^78VRjzYSHL#Fk6xzlQX_%uHR4@Jm$ zx~NZk(9*HRha;!ukoBJQDDW35C<69|E*6G;U*}6@)!^GQJH}6uHCYhuoG)24e9arV zQg4Fornd$=B3AI*M5tJAI3~wM4$0Lg3Pg$of~R96K{WOx_{vhtb7ajc5RN9!zXPB3 z82L@{vbhGOaRlF%SlOS4~7n_o(0632fm!o0V^Vb`p5!_#}5ml@nFfkk3) zoU^WlE&!H5mE(CSvHXIdRWmnxUPUdD1ZRg9lcMN|A4PtG{kr)mv7}KnC)%H8azY73 zh+`>|DxhyHSe$JZsS}Bief&|0QA%PWVY)e5a+aY8n7T$wN_utTPwSx$8n#b=sS&G# zi4f|mT`CIa(>Ww1*rz{crgBW0ErtV8G*^M!y`v?Do;sSxVE1Onbdr=bNO7oaelR7H zZivL>*rVZ@EgfDh4QQ$B9G*#$DC9&Dq;lG77go8TUrk3!Svdb9EQLO|0e_Mb_$GgQ*^|JY-FS9a$GxCV#vsMLY{Hio&glh2anTHe!^+wIRpBInIqZK1*i= zC&k#Dy`OM`fb5B3#MjZcn zZy~4{a}^H5Rk0h1aG;5W4#1cM1Mx&5BZ|Ob=as{$9{#VOJ{`F~a@fxyk45KU`PT8y#t*b#+fsJ}LSk*(uFN zeHKjjt}VNeu@FrOgl#HCDl2t{N1=!aA^9ZcSh3S59F1KY`3U2*j*;3{;i+fg<4&{5YQ+Z$2JO2N^gLWG+?}u) z*<_t-r@41o&a7WIvkS^Rtuqt2JQPI zEY_m(>kvnS1Wz7E3o|Jy=c)i43Lr;XnNtKN$9MDJy}_IqjHFXZ<@38Uaa+ZiHZhcq z+s0Wkb$?;TNGbokl0SCXSNnlKrdZClM0=hR`coo(-Nf~@QzFh=ON>$pOfwP z^PPvqa znM<3U8}?u##hH(Obwr~sxNdN06rRuclJNguSe*F%r+H5{nq?ApW5SN~Y~?3g)z$w1 Dm*ifV literal 0 HcmV?d00001 diff --git a/metrics/tests/location_indicator/no_textures/style.json b/metrics/tests/location_indicator/no_textures/style.json new file mode 100644 index 000000000..1cae891f7 --- /dev/null +++ b/metrics/tests/location_indicator/no_textures/style.json @@ -0,0 +1,65 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 512, + "height": 256, + "operations": [ + [ + "addImage", + "puck_hat", + "puck_hat.png" + ], + [ + "addImage", + "puck", + "puck.png" + ], + [ + "addImage", + "puck_shadow", + "puck_shadow.png" + ] + ] + } + }, + "center": [ 139.766707, 35.693055 ], + "zoom": 16, + "pitch" : 0, + "bearing" : 42, + "sources": {}, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "white" + } + }, + { + "id": "puck123", + "type": "location-indicator", + "layout" : { "bearing" : 45, + "perspective-compensation" : 1, + "image-tilt-displacement" : 5 + }, + "paint" : { + "accuracy-radius-transition": { "duration": 0, "delay": 0 }, + "bearing-image-size-transition": { "duration": 0, "delay": 0 }, + "top-image-size-transition": { "duration": 0, "delay": 0 }, + "shadow-image-size-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-color-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-border-color-transition": { "duration": 0, "delay": 0 }, + + "location" : [ 35.693055, 139.766707, 0], + "accuracy-radius": 90.0, + "bearing-image-size" : 48, + "top-image-size" : 16, + "shadow-image-size" : 112, + + "accuracy-radius-color": "rgba(255,0,0,0.2)", + "accuracy-radius-border-color": "rgba(255,0,255,0.6)" + } + } + ] +} diff --git a/metrics/tests/location_indicator/one_texture/expected.png b/metrics/tests/location_indicator/one_texture/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..c0fb9ce58093894bb43897fa893bba7bcdf290b3 GIT binary patch literal 3500 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 literal 0 HcmV?d00001 diff --git a/metrics/tests/location_indicator/one_texture/style.json b/metrics/tests/location_indicator/one_texture/style.json new file mode 100644 index 000000000..f44b698c0 --- /dev/null +++ b/metrics/tests/location_indicator/one_texture/style.json @@ -0,0 +1,66 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 512, + "height": 256, + "operations": [ + [ + "addImage", + "puck_hat", + "puck_hat.png" + ], + [ + "addImage", + "puck", + "puck.png" + ], + [ + "addImage", + "puck_shadow", + "puck_shadow.png" + ] + ] + } + }, + "center": [ 139.766707, 35.693055 ], + "zoom": 16, + "pitch" : 0, + "bearing" : 42, + "sources": {}, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "white" + } + }, + { + "id": "puck123", + "type": "location-indicator", + "layout" : { "bearing" : 45, + "bearing-image" : "puck", + "perspective-compensation" : 1, + "image-tilt-displacement" : 5 + }, + "paint" : { + "accuracy-radius-transition": { "duration": 0, "delay": 0 }, + "bearing-image-size-transition": { "duration": 0, "delay": 0 }, + "top-image-size-transition": { "duration": 0, "delay": 0 }, + "shadow-image-size-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-color-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-border-color-transition": { "duration": 0, "delay": 0 }, + + "location" : [ 35.693055, 139.766707, 0], + "accuracy-radius": 90.0, + "bearing-image-size" : 48, + "top-image-size" : 16, + "shadow-image-size" : 112, + + "accuracy-radius-color": "rgba(255,0,0,0.2)", + "accuracy-radius-border-color": "rgba(255,0,255,0.6)" + } + } + ] +} diff --git a/metrics/tests/location_indicator/rotated/expected.png b/metrics/tests/location_indicator/rotated/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..4b481b12d581aaffff4b3041b1d2dc139bbaac1d GIT binary patch literal 9452 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 Bd^7+6 literal 0 HcmV?d00001 diff --git a/metrics/tests/location_indicator/rotated/style.json b/metrics/tests/location_indicator/rotated/style.json new file mode 100644 index 000000000..5a75f56e2 --- /dev/null +++ b/metrics/tests/location_indicator/rotated/style.json @@ -0,0 +1,68 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 512, + "height": 256, + "operations": [ + [ + "addImage", + "puck_hat", + "puck_hat.png" + ], + [ + "addImage", + "puck", + "puck.png" + ], + [ + "addImage", + "puck_shadow", + "puck_shadow.png" + ] + ] + } + }, + "center": [ 139.766707, 35.693055 ], + "zoom": 16, + "pitch" : 0, + "bearing" : 42, + "sources": {}, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "white" + } + }, + { + "id": "puck123", + "type": "location-indicator", + "layout" : { "bearing" : 0, + "bearing-image" : "puck", + "top-image" : "puck_hat", + "shadow-image" : "puck_shadow", + "perspective-compensation" : 1, + "image-tilt-displacement" : 5 + }, + "paint" : { + "accuracy-radius-transition": { "duration": 0, "delay": 0 }, + "bearing-image-size-transition": { "duration": 0, "delay": 0 }, + "top-image-size-transition": { "duration": 0, "delay": 0 }, + "shadow-image-size-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-color-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-border-color-transition": { "duration": 0, "delay": 0 }, + + "location" : [ 35.693055, 139.766707, 0], + "accuracy-radius": 90.0, + "bearing-image-size" : 48, + "top-image-size" : 16, + "shadow-image-size" : 112, + + "accuracy-radius-color": "rgba(255,0,0,0.2)", + "accuracy-radius-border-color": "rgba(255,0,255,0.6)" + } + } + ] +} diff --git a/metrics/tests/location_indicator/tilted/expected.png b/metrics/tests/location_indicator/tilted/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..624be49df3caac4255d4fd58279cb501c667433b GIT binary patch literal 5958 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 MpWEKzJpot#3lhehO8@`> literal 0 HcmV?d00001 diff --git a/metrics/tests/location_indicator/tilted/style.json b/metrics/tests/location_indicator/tilted/style.json new file mode 100644 index 000000000..9dfe1033f --- /dev/null +++ b/metrics/tests/location_indicator/tilted/style.json @@ -0,0 +1,68 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 512, + "height": 256, + "operations": [ + [ + "addImage", + "puck_hat", + "puck_hat.png" + ], + [ + "addImage", + "puck", + "puck.png" + ], + [ + "addImage", + "puck_shadow", + "puck_shadow.png" + ] + ] + } + }, + "center": [ 139.766707, 35.693055 ], + "zoom": 16, + "pitch" : 60, + "bearing" : 42, + "sources": {}, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "white" + } + }, + { + "id": "puck123", + "type": "location-indicator", + "layout" : { "bearing" : 45, + "bearing-image" : "puck", + "top-image" : "puck_hat", + "shadow-image" : "puck_shadow", + "perspective-compensation" : 1, + "image-tilt-displacement" : 0 + }, + "paint" : { + "accuracy-radius-transition": { "duration": 0, "delay": 0 }, + "bearing-image-size-transition": { "duration": 0, "delay": 0 }, + "top-image-size-transition": { "duration": 0, "delay": 0 }, + "shadow-image-size-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-color-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-border-color-transition": { "duration": 0, "delay": 0 }, + + "location" : [ 35.693055, 139.766707, 0], + "accuracy-radius": 90.0, + "bearing-image-size" : 48, + "top-image-size" : 16, + "shadow-image-size" : 112, + + "accuracy-radius-color": "rgba(255,0,0,0.2)", + "accuracy-radius-border-color": "rgba(255,0,255,0.6)" + } + } + ] +} diff --git a/metrics/tests/location_indicator/tilted_texture_shift/expected.png b/metrics/tests/location_indicator/tilted_texture_shift/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..0a4baa1b584813195809c8b546927dc5e6480708 GIT binary patch literal 6502 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 RU-?D>4(xT_Q)*9-|1WpIusi?& literal 0 HcmV?d00001 diff --git a/metrics/tests/location_indicator/tilted_texture_shift/style.json b/metrics/tests/location_indicator/tilted_texture_shift/style.json new file mode 100644 index 000000000..6b6372ae1 --- /dev/null +++ b/metrics/tests/location_indicator/tilted_texture_shift/style.json @@ -0,0 +1,68 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 512, + "height": 256, + "operations": [ + [ + "addImage", + "puck_hat", + "puck_hat.png" + ], + [ + "addImage", + "puck", + "puck.png" + ], + [ + "addImage", + "puck_shadow", + "puck_shadow.png" + ] + ] + } + }, + "center": [ 139.766707, 35.693055 ], + "zoom": 16, + "pitch" : 55, + "bearing" : 42, + "sources": {}, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "white" + } + }, + { + "id": "puck123", + "type": "location-indicator", + "layout" : { "bearing" : 45, + "bearing-image" : "puck", + "top-image" : "puck_hat", + "shadow-image" : "puck_shadow", + "perspective-compensation" : 1, + "image-tilt-displacement" : 5 + }, + "paint" : { + "accuracy-radius-transition": { "duration": 0, "delay": 0 }, + "bearing-image-size-transition": { "duration": 0, "delay": 0 }, + "top-image-size-transition": { "duration": 0, "delay": 0 }, + "shadow-image-size-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-color-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-border-color-transition": { "duration": 0, "delay": 0 }, + + "location" : [ 35.693055, 139.766707, 0], + "accuracy-radius": 90.0, + "bearing-image-size" : 48, + "top-image-size" : 16, + "shadow-image-size" : 112, + + "accuracy-radius-color": "rgba(255,0,0,0.2)", + "accuracy-radius-border-color": "rgba(255,0,255,0.6)" + } + } + ] +} diff --git a/metrics/tests/location_indicator/tilted_texture_shift_bottom_left/expected.png b/metrics/tests/location_indicator/tilted_texture_shift_bottom_left/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..a651914b0901f1d68a5c64bcfcf67c5213ab1654 GIT binary patch literal 6396 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 literal 0 HcmV?d00001 diff --git a/metrics/tests/location_indicator/tilted_texture_shift_bottom_left/style.json b/metrics/tests/location_indicator/tilted_texture_shift_bottom_left/style.json new file mode 100644 index 000000000..1d5bb8ae6 --- /dev/null +++ b/metrics/tests/location_indicator/tilted_texture_shift_bottom_left/style.json @@ -0,0 +1,69 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 512, + "height": 256, + "operations": [ + [ + "addImage", + "puck_hat", + "puck_hat.png" + ], + [ + "addImage", + "puck", + "puck.png" + ], + [ + "addImage", + "puck_shadow", + "puck_shadow.png" + ] + ] + } + }, + "center": [ 139.768885, 35.693125 ], + "zoom": 16, + "pitch" : 60, + "bearing" : 42, + "sources": {}, + + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "white" + } + }, + { + "id": "puck123", + "type": "location-indicator", + "layout" : { "bearing" : 45, + "bearing-image" : "puck", + "top-image" : "puck_hat", + "shadow-image" : "puck_shadow", + "perspective-compensation" : 1, + "image-tilt-displacement" : 5 + }, + "paint" : { + "accuracy-radius-transition": { "duration": 0, "delay": 0 }, + "bearing-image-size-transition": { "duration": 0, "delay": 0 }, + "top-image-size-transition": { "duration": 0, "delay": 0 }, + "shadow-image-size-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-color-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-border-color-transition": { "duration": 0, "delay": 0 }, + + "location" : [ 35.693055, 139.766707, 0], + "accuracy-radius": 90.0, + "bearing-image-size" : 48, + "top-image-size" : 16, + "shadow-image-size" : 112, + + "accuracy-radius-color": "rgba(255,0,0,0.2)", + "accuracy-radius-border-color": "rgba(255,0,255,0.6)" + } + } + ] +} diff --git a/metrics/tests/location_indicator/tilted_texture_shift_bottom_right/expected.png b/metrics/tests/location_indicator/tilted_texture_shift_bottom_right/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..d0839ea66f4a90ef663f7e1e49111900145fa4c8 GIT binary patch literal 5996 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 literal 0 HcmV?d00001 diff --git a/metrics/tests/location_indicator/tilted_texture_shift_bottom_right/style.json b/metrics/tests/location_indicator/tilted_texture_shift_bottom_right/style.json new file mode 100644 index 000000000..611bea2a9 --- /dev/null +++ b/metrics/tests/location_indicator/tilted_texture_shift_bottom_right/style.json @@ -0,0 +1,68 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 512, + "height": 256, + "operations": [ + [ + "addImage", + "puck_hat", + "puck_hat.png" + ], + [ + "addImage", + "puck", + "puck.png" + ], + [ + "addImage", + "puck_shadow", + "puck_shadow.png" + ] + ] + } + }, + "center": [ 139.766487, 35.694867 ], + "zoom": 16, + "pitch" : 60, + "bearing" : 42, + "sources": {}, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "white" + } + }, + { + "id": "puck123", + "type": "location-indicator", + "layout" : { "bearing" : 45, + "bearing-image" : "puck", + "top-image" : "puck_hat", + "shadow-image" : "puck_shadow", + "perspective-compensation" : 1, + "image-tilt-displacement" : 5 + }, + "paint" : { + "accuracy-radius-transition": { "duration": 0, "delay": 0 }, + "bearing-image-size-transition": { "duration": 0, "delay": 0 }, + "top-image-size-transition": { "duration": 0, "delay": 0 }, + "shadow-image-size-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-color-transition": { "duration": 0, "delay": 0 }, + "accuracy-radius-border-color-transition": { "duration": 0, "delay": 0 }, + + "location" : [ 35.693055, 139.766707, 0], + "accuracy-radius": 90.0, + "bearing-image-size" : 48, + "top-image-size" : 16, + "shadow-image-size" : 112, + + "accuracy-radius-color": "rgba(255,0,0,0.2)", + "accuracy-radius-border-color": "rgba(255,0,255,0.6)" + } + } + ] +} diff --git a/metrics/tests/location_indicator/tilted_texture_shift_top_left/expected.png b/metrics/tests/location_indicator/tilted_texture_shift_top_left/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..48747eb194289437d86a9c789b2bc3f402b7680e GIT binary patch literal 3327 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ÐQO!fCp%_P5S@s>s3dOPMM8=Ir>v)rbVY)69QlX~sg*O=!-I599 z*qLL@P}OmhHXQr&2@Ow^?kvx=S$SN5Wb~pb2L%X_`l4J#p&0t1N#Kl~;At@Mmx@2Q z{T#zX9qIIf?0TyzhY@!Tdi}OSGo!_nx}!a3pPM0TO5K!G!Em^Zww~IdtY*YNhn~}@ zw|q5+R1B85XW}WMnv2GLO=NwMkSrJQNQxmX6D%+{1?0ylfc5mN#5;CNCjsDj7Ew6a|5$XZP73_Al5U=KONb5AS)-=l#C#`<(Cf zAnZXa^Y!Kc0IS1?_MHF#VmPh-#K*%7; zM+9E^C`I!Qr?koLvUWMFOqt3;5u8^5-tg<*JWl8bwE76^r07k76?0D<%!z}N(ls9A zq-z3PnJm?mx!-FU1qC4GaRoJt{YiyT>&|ih=!)|o+$g$ttyCwkgB35>4*hMw+Cm$Y zx*CE3p#BlQaYnebT$U_~R3Wt82(=kve($wD>BTuZ;qo1QCbR42jyhD5*11aCV%Ob2 z%>Ay4t&f=Ph36eFaNhxw-MVFDpu4r5IL5uRR!187P(RaTL1quU&r?Mca^whUycxXe zXbI8uD|5Si#mGhfE36U9ENxEiq5wW%}m zg3(xx*BfS(?Oj)E03^`AtkwCbX!Gx5+NDfwgaw8D365Cy4HgFozwPV(wWU;aj#lYQ z)u~)dCdoq=iU=DjOan+Yg%2FA#CLyvO;_7m{qOus z_WRobZGwJ1K#9kN_}M1@qS!;rnxTI~z0~B&zuU$-!(-=ZE4hwGgp1Pa$^kh4F`TRl zNf(lOtT<%JG;{hborVq9NYATZSwzO_b+gL3#p}sa{ZBm69(Pjm%L2J+v2NPswq|86 zntm9qR^Zixig(i}O7Rg&`X%4MNe#V2wX25$Guc!FB1 zIM^3VEAH6tUP4y38_;A%(-P$|R+jh_i%FI*x`l-XhEv8)s7F;(@7UbS8p%OS875EkXd_0I`(GmHCVZ&-4paoQ8 zkN^y9Z@iI>+X@$$T;OY)hV_M=X$!Iq9N7=bbKWSS3D1bHQzTPmPg-F-TJl4SY~OZ; zy`aVC8c)_BcgiqL@)W+l_de_>NwHmkmiZPEY8c@TFYeP8C;RR@_x5PGK6K7Z*NODl zIU*~O>>P+Y_IUNu;^vNLrCTFTkj!@(V@O#TTKebmu#;b4jvHXM)iR#r!amFpxmK%g zi*&Ks#IN)xu4gY1VLE);Rlm@lVR#CdOJ3oA95VVnEZYroE7|$BD2Vnf(@qo}^R7cW zocnS82#TD!_@TM;l3;)vosyr zcKz#)BP@4RA$H>99$J3POMBc}V@8~1zaVGsdo|k#0{}BDK6z~B%vS6+c&tfdX@Vh`*^LP?7j+FOndvg@m~X2vubhf$05n{rLv=Sb&R&B z@v&obr%!VlcP(nF^||8Ux~kdCn#f6AzNpyMXAN9pY3cpl7cHJOeg(nZfaTb|spYJvyy^9v{02T@rrgjGcXw z{#ir5$M5pO_CKVME`E3AqorN&LXB9)rWSmhEOLbA5I^iRHjKH?NF&LZXjwy6kM}y{ z*md9X_uDxWr!dcHBmSP%;-A<^${DQ}8{N8RTaD>$<5h&RP1#0fth!B>FwwC019`^y zr+<3x^e)D7mx&L`XxJnsbNa&|Gq;Y(z%izuJZa@Rb)qvi>U%1PB|E|E1N;4Hx{KKW zj3z{Osw^&~n4F@A1QIgSV<&2P^U=ZOuX#lIn$~C^qXg??cpDQb7UOq0v7Ri&DUvRa zIk6T+>j;qYFuPt(x<3FjjmrA%_3C1Kqx`K2snetN!LSBQz2|w-DB1e1iHhFshRIqN zfwnZg6ohqd@Yr>Jh=M-#@Zlb@eA}{1=Q#RzW8Mt_kUu80^M+2J&iF+Fn?Qb_mOMLR zW^#{YgWu+uIa+yTa#M;*7*zdox%L1?Qx!1Z-&*hG^<7w$`ws~D+s%kL_hX(ELd-9r zGoAM-n&<#V2ElbB0MN*HI2%bD?+r2)e@Q7aw;bGLyAPAr#AzcQkMPS1LLWR^n*hapR4ix zdv3W!F5m59YNdhylUcycE4x+blo!;ib2e@3VXvZ_QPP75?&$*n@SwxqQSNj780MLJ zb)^}}@|5GSt(n%px*)OTdY+nWlHlpv)xiD6P()`u!lWehtk(|M=bqM+fk@?sim)rG zPrbVnlMZ>|Nf*`bNRXQ3i?_wQSeq8i{PtNJ^eM&WbH77g$|=X6nJ?P5t^{C_Fod|}P6M6^$fB*mW p4}XUIsmp)O+|H+HRy7QAjRZuDQ(YZbcW7E~J=;v}m_XB+6oJ zwaO$Vmn3FtwX!KnmJ%aT?h|3g+@9~T`}uzVhp!(VXU=)f`}Vxf`@ApdK3=Z#wH9gt z0Oq^9x$Fc0g+m08HQ>LeyqDnsm_2ZJaq^A(Z7hE%Bjl~0XjTzuwr;BvouRw1A%M}a zK1GJfFkQV|7Z<+nMdCMAJbE%c%eb#Hw7uuAuDFMhv5k?{T{Cf!k~JBzg_4sSC2tbS zN*)So$0rr;u72I2p;fgO8-=G{)=1SmNON2#);w2h!-43-8TbU>kmp45p_Ad0l|+Y6 zKuVr_{(rc@)0X7Q25J>lMFHT=Cj-`jDf8J0S?@J(9YEix3$V_6O=?@3D(op(3fwe^ zfWB_0n~aRGCg}jI=4YwPv!qVi2yj7AaChZS_P{a%aEDehS+}ulN@_-^pn%IL3ZjiJ zJ$xtrWgZX}VQ`Ugdu9QGxj6x#gVLC0KzAb;^|g0E=Dt6kVW6zmBL@J-y*4Ha-J9nKRnnX3=N_sL|?Pi-p9UC#q601-_7 z-0wYVZlslhvH+c(E3=rH&<6EI=mcHhmiNRTf9^GzhEYoZ z4H37bOX%PD!!uDiw?NaN0;T@|NoVZ{z<*ga*0o3cS8K=23eBhIfFE%Ch6r9SYiq|7 z0~C7hp$S^dEI%N06&?9`%r~hqu%*f-TG|~y(v2~@t*0G z5+CZOm278wb@Qg&I^vu)pbZz>1*uxHlnQ^viL+$^a{F{WE z5yp4*llmB#C14Ptm3WVbxjl(^MZdWANlrhzagF=ra81LV+0LJzCF@gy7nA)+=Snpo z^%O{bKFtuTp$WIEodt>c%C7c0MxwiH^3jFR!YT6tdFOGq_Gw23wT1BeD+{!3AE7)O zKrJBZt~>P&;6YW=>B!I8^QVXW62p%(d4q+`y563n=jbc0@R)5c^_yQCxQsB?DF@HW;H?k3#~fB$!Jqg%Lj>cC%;N_8r}5=&@bes@KW zZKa>0z-8NJ-9AZ;e8WCE;r7&dc8Te3^0Bj= zEgH?O9LXV;8#O553`EaiyuDGA-bPb100NU zZxCBGurzp7!e~}_OTJPwUhUae_HjS|J>Gg*e*C1ktgp+Z?1{NDZ`Y5{(;i2f>+ak- zSV2DR*IP77zk45X9K$FSBhMuUm(I@Ne~ywD#DD6lDdQdL*Aj`lIzL9pNM!ek*9jD* z@*2iW;myuXiI3hL%ShsNyq^*aZxFXU$MW@ZBPg;1tYVsOgl6Yabl=8hoVUVoB8Y)r zYJ>X@@M%nu>NS&cTBtd zgzVDT%zDO@e0JLYPJ7Go+a4wk|I9wReCE8*URI^Xo^7mOM8yYIaoo@?*%*(=5NkoK z?}S*-Y9P_$I$PpQR;s9Xl#xyB`LfD%vH89nhu(LW4nD5cl2Z5#gFajrlr2FV)m_7CMOkZ4GU!*cY=(+w5NF5nAk0A^(R1C zcNN>yI1(B;lKR^Q^XT5sk4D8z6nuLIuud~pfi}6is7{@q&0R4oP$}&CW80>rdHrlA zn`*9<*PME^vvI(F)ndop=b4+nPna!amZ&+C!EP1Y3~r8s(R?d9na);>-&AHYaHmX`Rzj9Wo#a$ zs+Q<$-kY>vb1ITLZwW00MjX&vH@H4QZ9RtYy)DDS^|KvIRf(aK!Xkxoy3u}iq=9=c zY^?S{@5lGAKDJZw*c)~@-Z}^TB6EopS?Ccl5m!PF-d-}R7)o9utppW zqj$a3`BF=qTIW>f!0G!H+l&~6`;z$c!2);}_qi0-7^;C-e zZs6(tk^tzb)gGH?Yp>%MGmcJMatHqW*j}jq_;fe`mv5WC&#igekkeBemM|uM>0it} zefwnovnNllb(dgnqN`Es-OM?Ex?w%bZ>FNA8ZXI%c_f}CbaBzPbf9aPK7~u$6xHh$ zo{QwQrB$-Xl5C-ux~Rd%9e{%u9M_tf8E24kb@9kQg5Kh?PCR&m8OENsY^X3>E zB7-9;!K5%uRh_^U{`gOmc&sKT8($kbQ|6p044tkeCjMc7t(pEfB%cVZE8$AmE&goz z-_y%qaVP3V+M`ZS@17nx{y>e}vUj7nXAsIci-ypCAM}e*}MDNyBsO|W-cvriS`B50867+MsELwfU>`YwW zZ$`r4X3GLbj5;RR_|bCt$EeWTYNf+xfl!gr*3LZ5t(4DjFN`!EnVy!E`b*T$YlbuG zPu`Pv3su+Ub@pnNCbUQF2bU*xpEtJA?$Yj#leYehMqT}ij4!0{6sWue=mn!&(cwmh z0A9c@8qiZNr~%Ev>Ov!R8_JKz3*WsK+7G&3n91%htDar!$gM6DTi|Op?=QSl=rL*9 zKJi%7qD0CJyydkd_?Ex-ZQtS%>9Kpeg&hY}s*mz%E7uEt%+t8bGNSnAPXmon;*S-E z=hkdP_oRyS+q5DC;L~+hjgt?RIBY38+#7|NkneYofLssabG+mm~yre&nwF=<(R5$^z&PsxoS$%J43*iU+Y@jf!#>IWRP zTdxpqGIK%~VH-gj8RBH=E`*#fFHXhB^CpEoUu|x1>$<(AsuqR%bnwi2EBxLmRn5`% zGxkHmIQ1iUSz*FV(Ddx*!q~*A@sI1LJHqPYzVEI%E_fLIX~(X`M(YVF+ljsotZH=1 z7>TFfb+qivpYPM?#R4Jvq5?|qzU5mY;hoPp0#6}zBMfgjio#%3$I@{1$S!DnO4_5r=;e6*#eOp%u)Lf{oZ-T$%(}+vk`XQ$!8-ge54S#PY_w z67cvIZmnEO>F`pH^ouVkLtl4yM*OWfQOVT4v%Rp#FUGb%wCHD&?5M5PR-9`W zUS7ZoELq|!Am2G@2Z`P^=e`y|@|#>X10x!?KOFBGk$-eHgZ*MRa+iR0sSO;hLClwoCF&6}Hl%VuqJvfM z8TjuLP)aC}*5Jcu4cMv$iN- zUNaBXh|)Y3iM0lSkE>zD04Bg`shK2EHK+i$Yj?)8KzuPiQdXNSck0#9D%=$R#vlCU zsE_T_;a{L2JSUa~Qm$|7gA9^cEkV!|iGGA())JZl)@s07fQ4@^7+wVYwD|!b-V*Ev z$8d4}`_Eo-2XcttknuH-OY?K%sdCtmC)2H#_0HlYTmIRF(&w6`@hV3K!5Lt_nNBZy3nf#1L;aknGS7)T4o<2W7rjD`$E-yt`DDglE_SqVC<3sMBa3#RK0&cmp#y(Z@> zkR?5cP;dd3^B5(vOr7J zP$`0TD}n*uCdNuorPaET{LG?qGr}{#$XFfeu7pOfoev${;hqOa=#f}QS(sm5#~sh# zG&U>+i5mR38l4v4z#_g0>3xlE#NYGak-cj)XRQztzZm_(8bc#&(5o@?qdyyQj?#Kd zgz}EX@9syuKlRWmdF%!&eA*og+vx({9ZYI>VpXAEI1z?(K@0zX9%bZJzn^G4w`*nv z#~er>fk6Q_?TNiGVJ&{~y~xoJDU}}u?~l|;Pn zjufAL;Qi`nk)+-?{d-xDBQdBgthvya#OQ+T+lNSeZ!j*`j)5YSV!J{ADsW$qeoX(% zjr-w(?qY94yLp|XI9K_=KFWq*SB?B_49exW%Q#BDgiB=Fys1d+J{ja7$VvnFNgs!1 zKODu+^pt^LEn?9Kgtw0M*`BjjGy34)CfFS`4Gew-SEI9}2M=o3=*ZrVAFfOs*dDH6 z51P@9!Mtw5-J!A#;~|=(_*lFK-x~KAxSm+heH?xt7nuD|=)xP8fp~%8K@*K{jEJZE zPl=11MArKWQ_V<-)_Zb<4aq)OD0y-IydztCr6#`NV)T~P$MmC5>G7G!-B6Vv4^%6K zp8oh5L*!Pw&=>T`3dbtefZq+VSOc872&FsN~(i^B4bYagfDsqp${P^izOWuNBvb z2Rjq6a#Cv;>Ae5fXlz_aH?QugoyreEURIb>2v#&2O7S&yO&jO=WuT zq&7|L<#=AZ&|7V%?6{1-m=eB6=qlU-LE_S3a4c;=CahvD zemFAR=@U!1eZ^ALP{m^M!v)B)i%3pqKK+e&q=azCv$tQAE%#P5Fn2-@YKu&vp>Do% zx*wl!W8>V0Qxi!1Fu)+;` z-*}g4{^LqW)zNskZ#VLg#tBCmFNbHrF5^JImK)97zql!{vvw0vlZK+b( zP!K*&y$IuxkUf?l;ZLk+F;Yq#Ok%Zb-OUF3HA5)Pq|@KLHq>b%{gzlD}3D0f|4N2b@*y;x<6KiR?vC_ zy5mpv`Yl6ZqOfeln)sQ(*3MR#Pq6(Qxk0lYV)6KFPCGYGnNWL8($74l5HEttv!L>u zcN$O$O>+oc z%72tzDF+V>z*H@=P1;ece*0P&ItwK&#;O0Mt*k*?*5 zLjCXkRWYqCm%Y^|gu-#f$Qs<@uxb%WM{OMbo$_ER@|U`|_w z#vMUQk6V(kJhuCEm{gj#Q~0i@7^br6+{P)TvmPEfX?i}sTDkXM?EG6rN+I&S(b2cl z2{hZ)RF1i5REJze>|gkU4I1wgy~~euC?sHMVVkCWu)$^^F;6MECG0p;)Gxk2*%Ci3 znX?ouZcSwREaL2=_@K6d6x(p(rjVf?V}39i!{Hs)c00)N7+cu7#dd+3%%)sSvv%w+ z)`Q-tZaAt~6*(!z|7dOChEGeln}bT#3qYC?y#34x2w8`yLFDhE*me;j%{f1AX!T(2 zn*SwBw%FJwUHRz}uczLnwy%t7YANk<$9wvjwiAT|Z-m4F?tcXCtrM-JwH6#+KORM@ zpm8jbm_XiUD>?&pNIaX^=&zyN9+n!`Ju;cN2o}gZ5}Hed`7<5G$}9c&>*ZX0&9im0 zwVY|{0vOA@XZVwHJyaYosu)l00}$q+;zA zZ61CA@0wMT)Xe(O>5&*={uDLzi{5WCQ}37VdoIf6zWOtWqr-MI>=q3crrIOo*V4aR z#P33PYcR$YE{Wdhj~#Cr-E_64+J9gM4lNn7vQk@=*cSz;cAZi?qP!P>i%9}*nhBJ| z;_*5WpZM#;SW?Ob?eRSA+uBVbbCw0=DR*a6A4|())P27}4_f=!F>Z;MgNHIk5 zzh!w^DcWp_IIq`yw;k&?=UAa;1X_l#+uN{y{?Lb!49{}ID{&@)6C*oe& zZL00}o;9Vxo|eo#5NMc-QnInuXnvhD{msI`XBhilby#C zx0|}j+cIHQGI|5tr~@~`GWxZlU{Wc0Q^1+H_=vkUCp7E#Nhi9DR!TGev{LU+XU_J| zp`HPAl_6f7K8I~)yTsdLM0oi6(6M@`N$LA-k3jO%y0%A=_ z@JkMQQeWsPJ5ec)EoZN8Dr{#mV69~6qPsqj>sN;|S z+emWV=7*`$SY`8m-KG~zVQbRlKFJbK`Nwb5#oRkL8zjk)5cGBXGr!m87$CXJQ72=c z!aS?TUDAaa76p>yPYd@>#y%c4rNN@=cTIP-Yz5?b?xNmi(WL`Ug9q=cgYIiJmviei z=XCrY>X_490=~M59@*In=*?4gYc9{;_g4PD#T^2WYK`7st~{Bwxyh7%7;eaebEou^ z&gh=0Q@?$#u2~%CBTt>{@FnoDh#%iFi=F2~RbU`=-TPB1_RSvH>dSJb!YSAp>~})^ zFr%$->dXy@?`Lz}%_0_M!y)6Ff1R~ZuWmA(gth$BD{kxCS_cl8vLNMOVZd&qbqCc= z)rAr-(*@6{3~WOheb#v&gn(%UnzmK*2KSjRq(r394fGk@XRtjudDb7?OXE*b=h~q? zbfh6Mxu0qby@MK#!{GSJ+}M`NYF;#@DgtxOR|b~5)=fRve7!`Vv36)@GQWYL_ueF; zuCtTaR0~D0Nfw&s!&4U7!q@c}SgX@yD(VFIA)QOy9zc1D=)h;4FZ#C*+zA_-81a(f z9l7&l>J!-9HgRKZeC*G}VB4G(O%sK%dYmRTBjCbHH&%gDlPMoYbh;?znU$Lw=+Oa4 zur=f*+Ky_@HKlEdN0A{7RU+mDoA5y_^tG113haV=_np1WQo_xgeQ@C<-&{9}Cqq8L z_I9Us8f(+z5>t*hB&9$o^a%n3di}fXn;I)EW(lHt761k$v*-=!g?>!YmbqW^%~*Y! zZt#2b@>xVTC4in3n0%K8(oN?g7VJ-SL2kj6yAq^A5Jz`D+O?FhZX#<6(OC=&Pgl~& zmzEPU=axMP2uM5oelJF2vV0-))xUQUZ)qiY$ruUPAmp=?PFhO;Kwywiz%!HhX6=j) z{E?4uQb_@J9WKZ8<+BD;9HUZtsGuAm0Du+n!ver(>B1)ltquRQi2vU&%yQ434GMNG SF`JlE$bGAq%bhKp #include #include +#include #include #include +#include #include #include @@ -65,6 +67,9 @@ LayerManagerDefault::LayerManagerDefault() { #if !defined(MBGL_LAYER_CUSTOM_DISABLE_ALL) addLayerType(std::make_unique()); #endif +#if !defined(MBGL_LAYER_LOCATION_INDICATOR_DISABLE_ALL) + addLayerType(std::make_unique()); +#endif #endif } @@ -72,6 +77,8 @@ void LayerManagerDefault::addLayerType(std::unique_ptr factory) { std::string type{factory->getTypeInfo()->type}; if (!type.empty()) { typeToFactory.emplace(std::make_pair(std::move(type), factory.get())); + } else { + Log::Warning(Event::Setup, "Failure adding layer factory. getTypeInfo() returned an empty type string."); } factories.emplace_back(std::move(factory)); } diff --git a/platform/glfw/CMakeLists.txt b/platform/glfw/CMakeLists.txt index 2a1524e11..986d3b013 100644 --- a/platform/glfw/CMakeLists.txt +++ b/platform/glfw/CMakeLists.txt @@ -13,11 +13,20 @@ add_executable( ${PROJECT_SOURCE_DIR}/platform/default/src/mbgl/map/map_snapshotter.cpp ) +set_property( + SOURCE ${PROJECT_SOURCE_DIR}/platform/glfw/glfw_view.cpp + PROPERTY COMPILE_DEFINITIONS MAPBOX_PUCK_ASSETS_PATH=\"${PROJECT_SOURCE_DIR}/platform/glfw/assets/\" +) + if(MBGL_WITH_OPENGL) target_sources( mbgl-glfw PRIVATE ${PROJECT_SOURCE_DIR}/platform/glfw/glfw_gl_backend.cpp ) + target_compile_definitions( + mbgl-glfw + PRIVATE MBGL_RENDER_BACKEND_OPENGL=1 + ) endif() target_include_directories( diff --git a/platform/glfw/assets/puck.png b/platform/glfw/assets/puck.png new file mode 100644 index 0000000000000000000000000000000000000000..09d93ebec86381752ff497d8b19a2253197dd85a GIT binary patch literal 5663 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 R5cW?1ZFPOMIu(bQ{{b;IRc!zO literal 0 HcmV?d00001 diff --git a/platform/glfw/assets/puck_hat.png b/platform/glfw/assets/puck_hat.png new file mode 100644 index 0000000000000000000000000000000000000000..e9411e0d022be60e6a9725bae7e1f603f1cc54a5 GIT binary patch literal 6843 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 literal 0 HcmV?d00001 diff --git a/platform/glfw/assets/puck_shadow.png b/platform/glfw/assets/puck_shadow.png new file mode 100644 index 0000000000000000000000000000000000000000..baf848b597df1344f1fa1b10ecc9a1595e72226c GIT binary patch literal 28077 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 literal 0 HcmV?d00001 diff --git a/platform/glfw/glfw_view.cpp b/platform/glfw/glfw_view.cpp index f59d44bd8..e538d934a 100644 --- a/platform/glfw/glfw_view.cpp +++ b/platform/glfw/glfw_view.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,25 @@ #include #include +#if defined(MBGL_RENDER_BACKEND_OPENGL) && !defined(MBGL_LAYER_LOCATION_INDICATOR_DISABLE_ALL) +#include + +namespace { +const std::string mbglPuckAssetsPath{MAPBOX_PUCK_ASSETS_PATH}; + +mbgl::Color premultiply(mbgl::Color c) { + c.r *= c.a; + c.g *= c.a; + c.b *= c.a; + return c; +} + +std::array toArray(const mbgl::LatLng &crd) { + return {crd.latitude(), crd.longitude(), 0}; +} +} // namespace +#endif + class SnapshotObserver final : public mbgl::MapSnapshotterObserver { public: ~SnapshotObserver() override = default; @@ -459,6 +479,9 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action, // Snapshot with overlay view->makeSnapshot(true); } break; + case GLFW_KEY_G: { + view->toggleLocationIndicatorLayer(); + } break; } } @@ -689,6 +712,12 @@ void GLFWView::onScroll(GLFWwindow *window, double /*xOffset*/, double yOffset) } view->map->scaleBy(scale, mbgl::ScreenCoordinate { view->lastX, view->lastY }); +#if defined(MBGL_RENDER_BACKEND_OPENGL) && !defined(MBGL_LAYER_CUSTOM_DISABLE_ALL) + if (view->puck && view->puckFollowsCameraCenter) { + mbgl::LatLng mapCenter = view->map->getCameraOptions().center.value(); + view->puck->setLocation(toArray(mapCenter)); + } +#endif } void GLFWView::onWindowResize(GLFWwindow *window, int width, int height) { @@ -755,7 +784,12 @@ void GLFWView::onMouseMove(GLFWwindow *window, double x, double y) { } view->lastX = x; view->lastY = y; - +#if defined(MBGL_RENDER_BACKEND_OPENGL) && !defined(MBGL_LAYER_CUSTOM_DISABLE_ALL) + if (view->puck && view->puckFollowsCameraCenter) { + mbgl::LatLng mapCenter = view->map->getCameraOptions().center.value(); + view->puck->setLocation(toArray(mapCenter)); + } +#endif auto &style = view->map->getStyle(); if (style.getLayer("state-fills")) { auto screenCoordinate = mbgl::ScreenCoordinate{view->lastX, view->lastY}; @@ -879,6 +913,10 @@ void GLFWView::setWindowTitle(const std::string& title) { } void GLFWView::onDidFinishLoadingStyle() { +#if defined(MBGL_RENDER_BACKEND_OPENGL) && !defined(MBGL_LAYER_CUSTOM_DISABLE_ALL) + puck = nullptr; +#endif + if (show3DExtrusions) { toggle3DExtrusions(show3DExtrusions); } @@ -933,3 +971,75 @@ void GLFWView::toggleCustomSource() { mbgl::style::VisibilityType::None : mbgl::style::VisibilityType::Visible); } } + +void GLFWView::toggleLocationIndicatorLayer() { +#if defined(MBGL_RENDER_BACKEND_OPENGL) && !defined(MBGL_LAYER_LOCATION_INDICATOR_DISABLE_ALL) + puck = static_cast(map->getStyle().getLayer("puck")); + static const mbgl::LatLng puckLocation{35.683389, 139.76525}; // A location on the crossing of 4 tiles + if (puck == nullptr) { + auto puckLayer = std::make_unique("puck"); + + puckLayer->setLocationTransition(mbgl::style::TransitionOptions( + mbgl::Duration::zero(), mbgl::Duration::zero())); // Note: This is used here for demo purpose. + // SDKs should not use this, or else the location + // will "jump" to positions. + puckLayer->setLocation(toArray(puckLocation)); + puckLayer->setAccuracyRadius(50); + puckLayer->setAccuracyRadiusColor( + premultiply(mbgl::Color{0.0, 1.0, 0.0, 0.2})); // Note: these must be fed premultiplied + + puckLayer->setBearing(0); + puckLayer->setAccuracyRadiusBorderColor(premultiply(mbgl::Color{0.0, 1.0, 0.2, 0.4})); + puckLayer->setTopImageSize(24); + puckLayer->setBearingImageSize(72); + puckLayer->setShadowImageSize(96); + puckLayer->setImageTiltDisplacement(8.0f); // set to 0 for a "flat" puck + puckLayer->setPerspectiveCompensation(0.9); + + map->getStyle().addImage(std::make_unique( + "puck.png", mbgl::decodeImage(mbgl::util::read_file(mbglPuckAssetsPath + "puck.png")), 1.0)); + + map->getStyle().addImage(std::make_unique( + "puck_shadow.png", mbgl::decodeImage(mbgl::util::read_file(mbglPuckAssetsPath + "puck_shadow.png")), 1.0)); + + map->getStyle().addImage(std::make_unique( + "puck_hat.png", mbgl::decodeImage(mbgl::util::read_file(mbglPuckAssetsPath + "puck_hat.png")), 1.0)); + + puckLayer->setBearingImage(mbgl::style::expression::Image("puck.png")); + puckLayer->setShadowImage(mbgl::style::expression::Image("puck_shadow.png")); + puckLayer->setTopImage(mbgl::style::expression::Image("puck_hat.png")); + + puck = puckLayer.get(); + map->getStyle().addLayer(std::move(puckLayer)); + } else { + bool visible = puck->getVisibility() == mbgl::style::VisibilityType::Visible; + if (visible) { + if (!puckFollowsCameraCenter) { + mbgl::LatLng mapCenter = map->getCameraOptions().center.value(); + puck->setLocation(toArray(mapCenter)); + puckFollowsCameraCenter = true; + } else { + puckFollowsCameraCenter = false; + puck->setVisibility(mbgl::style::VisibilityType(mbgl::style::VisibilityType::None)); + } + } else { + puck->setLocation(toArray(puckLocation)); + puck->setVisibility(mbgl::style::VisibilityType(mbgl::style::VisibilityType::Visible)); + puckFollowsCameraCenter = false; + } + } +#endif +} + +using Nanoseconds = std::chrono::nanoseconds; + +void GLFWView::onWillStartRenderingFrame() { +#if defined(MBGL_RENDER_BACKEND_OPENGL) && !defined(MBGL_LAYER_LOCATION_INDICATOR_DISABLE_ALL) + puck = static_cast(map->getStyle().getLayer("puck")); + if (puck) { + uint64_t ns = mbgl::Clock::now().time_since_epoch().count(); + const float bearing = float(ns % 2000000000) / 2000000000.0 * 360.0; + puck->setBearing(bearing); + } +#endif +} diff --git a/platform/glfw/glfw_view.hpp b/platform/glfw/glfw_view.hpp index 21b54f87a..bee8896fa 100644 --- a/platform/glfw/glfw_view.hpp +++ b/platform/glfw/glfw_view.hpp @@ -6,6 +6,9 @@ #include #include #include +#if defined(MBGL_RENDER_BACKEND_OPENGL) && !defined(MBGL_LAYER_CUSTOM_DISABLE_ALL) +#include +#endif struct GLFWwindow; class GLFWBackend; @@ -61,6 +64,7 @@ public: // mbgl::MapObserver implementation void onDidFinishLoadingStyle() override; + void onWillStartRenderingFrame() override; protected: // mbgl::Backend implementation @@ -91,6 +95,7 @@ private: void addAnimatedAnnotation(); void updateAnimatedAnnotations(); void toggleCustomSource(); + void toggleLocationIndicatorLayer(); void cycleDebugOptions(); void clearAnnotations(); @@ -148,4 +153,9 @@ private: std::unique_ptr snapshotter; std::unique_ptr snapshotterObserver; mbgl::ResourceOptions mapResourceOptions; + +#if defined(MBGL_RENDER_BACKEND_OPENGL) && !defined(MBGL_LAYER_CUSTOM_DISABLE_ALL) + bool puckFollowsCameraCenter = false; + mbgl::style::LocationIndicatorLayer *puck = nullptr; +#endif }; diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.js index 804d4a243..08fd1fba1 100755 --- a/scripts/generate-style-code.js +++ b/scripts/generate-style-code.js @@ -64,7 +64,7 @@ global.evaluatedType = function (property) { case 'boolean': return 'bool'; case 'number': - return 'float'; + return /location$/.test(property.name) ? 'double' : 'float'; case 'resolvedImage': return 'expression::Image'; case 'formatted': @@ -77,7 +77,7 @@ global.evaluatedType = function (property) { return `Color`; case 'array': if (property.length) { - return `std::array<${evaluatedType({type: property.value})}, ${property.length}>`; + return `std::array<${evaluatedType({type: property.value, name: property.name})}, ${property.length}>`; } else { return `std::vector<${evaluatedType({type: property.value, name: property.name})}>`; } diff --git a/scripts/style-spec.js b/scripts/style-spec.js index 8a9c9d414..c0acbebd9 100644 --- a/scripts/style-spec.js +++ b/scripts/style-spec.js @@ -1 +1,182 @@ -var spec = module.exports = require('../mapbox-gl-js/src/style-spec/reference/v8'); +const referenceSpec = require('../mapbox-gl-js/src/style-spec/reference/v8'); + +referenceSpec.layer.type.values["location-indicator"] = {}; +referenceSpec["layout_location-indicator"] = { + "top-image": { + "type": "resolvedImage", + "property-type": "data-constant", + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "doc": "Name of image in sprite to use as the top of the location indicator." + }, + "bearing-image": { + "type": "resolvedImage", + "property-type": "data-constant", + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "doc": "Name of image in sprite to use as the middle of the location indicator." + }, + "shadow-image": { + "type": "resolvedImage", + "property-type": "data-constant", + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + }, + "doc": "Name of image in sprite to use as the background of the location indicator." + }, + "perspective-compensation": { + "type": "number", + "default": "0.85", + "property-type": "data-constant", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "doc": "The amount of the perspective compensation, between 0 and 1. A value of 1 produces a location indicator of constant width across the screen. A value of 0 makes it scale naturally according to the viewing projection." + }, + "bearing": { + "type": "number", + "default": "0", + "default": 0, + "period": 360, + "units": "degrees", + "property-type": "data-constant", + "expression": { + "interpolated": false, + "parameters": [ ] + }, + "transition": false, + "doc": "The bearing of the location indicator." + }, + "image-tilt-displacement": { + "type": "number", + "property-type": "data-constant", + "default": "0", + "units": "pixels", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "doc": "The displacement off the center of the top image and the shadow image when the pitch of the map is greater than 0. This helps producing a three-dimensional appearence." + } +}; + +referenceSpec["paint_location-indicator"] = { + "location": { + "type": "array", + "default": [ + 0.0, + 0.0, + 0.0 + ], + "length": 3, + "value": "number", + "property-type": "data-constant", + "expression": { + "interpolated": true, + "parameters": [] + }, + "transition": true, + "doc": "An array of [latitude, longitude, altitude] position of the location indicator." + }, + "accuracy-radius": { + "type": "number", + "units": "meters", + "default": 0, + "property-type": "data-constant", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "transition": true, + "doc": "The accuracy, in meters, of the position source used to retrieve the position of the location indicator." + }, + "top-image-size": { + "type": "number", + "units": "pixels", + "property-type": "data-constant", + "default": 0, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "transition": true, + "doc": "The size of the top image, in pixels." + }, + "bearing-image-size": { + "type": "number", + "units": "pixels", + "property-type": "data-constant", + "default": 0, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "transition": true, + "doc": "The size of the bearing image, in pixels." + }, + "shadow-image-size": { + "type": "number", + "units": "pixels", + "property-type": "data-constant", + "default": 0, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "transition": true, + "doc": "The size of the shadow image, in pixels." + }, + "accuracy-radius-color": { + "type": "color", + "property-type": "data-constant", + "default": "#ffffff", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "transition": true, + "doc": "The color for drawing the accuracy radius, as a circle. To adjust transparency, set the alpha component of the color accordingly." + + }, + "accuracy-radius-border-color": { + "type": "color", + "property-type": "data-constant", + "default": "#ffffff", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "transition": true, + "doc": "The color for drawing the accuracy radius border. To adjust transparency, set the alpha component of the color accordingly." + } +}; + +var spec = module.exports = referenceSpec diff --git a/src/mbgl/gl/defines.hpp b/src/mbgl/gl/defines.hpp index 75325dfb7..1d9177d06 100644 --- a/src/mbgl/gl/defines.hpp +++ b/src/mbgl/gl/defines.hpp @@ -93,6 +93,7 @@ #define GL_LESS 0x0201 #define GL_LINEAR 0x2601 #define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 #define GL_LINE_LOOP 0x0002 #define GL_LINES 0x0001 #define GL_LINE_STRIP 0x0003 @@ -102,6 +103,7 @@ #define GL_MAX_VERTEX_ATTRIBS 0x8869 #define GL_NEAREST 0x2600 #define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 #define GL_NEVER 0x0200 #define GL_NO_ERROR 0 #define GL_NOTEQUAL 0x0205 @@ -161,7 +163,7 @@ #define GL_VERTEX_SHADER 0x8B31 #define GL_VIEWPORT 0x0BA2 #define GL_ZERO 0 - +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE #ifdef MBGL_USE_GLES2 #define GL_HALF_FLOAT 0x8D61 #else @@ -176,4 +178,4 @@ #define GL_RGBA8 0x8058 #define GL_ZOOM_X 0x0D16 #define GL_ZOOM_Y 0x0D17 -#endif \ No newline at end of file +#endif diff --git a/src/mbgl/layermanager/layer_manager.cpp b/src/mbgl/layermanager/layer_manager.cpp index 1e2074cd7..377d3acce 100644 --- a/src/mbgl/layermanager/layer_manager.cpp +++ b/src/mbgl/layermanager/layer_manager.cpp @@ -14,14 +14,17 @@ namespace mbgl { std::unique_ptr LayerManager::createLayer( const std::string& type, const std::string& id, const style::conversion::Convertible& value, style::conversion::Error& error) noexcept { - if (LayerFactory* factory = getFactory(type)) { + LayerFactory* factory = getFactory(type); + if (factory) { auto layer = factory->createLayer(id, value); if (!layer) { - error.message = "Error parsing a layer of type: " + type; + error.message = "Error parsing layer " + id + " of type: " + type; } return layer; + } else { + error.message = "Null factory for type: " + type; } - error.message = "Unsupported layer type: " + type; + error.message = "Unsupported layer type! " + error.message; return nullptr; } diff --git a/src/mbgl/layermanager/location_indicator_layer_factory.cpp b/src/mbgl/layermanager/location_indicator_layer_factory.cpp new file mode 100644 index 000000000..67f7280e9 --- /dev/null +++ b/src/mbgl/layermanager/location_indicator_layer_factory.cpp @@ -0,0 +1,25 @@ +#include + +#include +#include +#include + +namespace mbgl { + +const style::LayerTypeInfo* LocationIndicatorLayerFactory::getTypeInfo() const noexcept { + return style::LocationIndicatorLayer::Impl::staticTypeInfo(); +} + +std::unique_ptr LocationIndicatorLayerFactory::createLayer( + const std::string& id, const style::conversion::Convertible&) noexcept { + return std::unique_ptr(new style::LocationIndicatorLayer(id)); +} + +std::unique_ptr LocationIndicatorLayerFactory::createRenderLayer( + Immutable impl) noexcept { + assert(impl->getTypeInfo() == getTypeInfo()); + return std::make_unique( + staticImmutableCast(impl)); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_location_indicator_layer.cpp b/src/mbgl/renderer/layers/render_location_indicator_layer.cpp new file mode 100644 index 000000000..20c3bb8cf --- /dev/null +++ b/src/mbgl/renderer/layers/render_location_indicator_layer.cpp @@ -0,0 +1,786 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace mbgl::platform; +namespace mbgl { + +struct LocationIndicatorRenderParameters { + LocationIndicatorRenderParameters() = default; + explicit LocationIndicatorRenderParameters(const TransformParameters& tp) : state(&tp.state) {} + LocationIndicatorRenderParameters(const LocationIndicatorRenderParameters& o) = default; + LocationIndicatorRenderParameters& operator=(const LocationIndicatorRenderParameters& o) = default; + + double width = 0.0; + double height = 0.0; + double latitude = 0.0; + double longitude = 0.0; + double zoom = 0.0; + double bearing = 0.0; + double pitch = 0.0; + std::array projectionMatrix; + const TransformState* state = nullptr; + ImageManager* imageManager = nullptr; + // some testing defaults, for before it gets updated via props + double puckBearing = 0.0; + LatLng puckPosition = {0, 0}; + double errorRadiusMeters; + mbgl::Color errorRadiusColor{0, 0, 0, 0}; + mbgl::Color errorRadiusBorderColor{0, 0, 0, 0}; + int puckSizePx = 0; + int puckHatSizePx = 0; + int puckShadowSizePx = 0; + float puckLayersDisplacement = 0; + float perspectiveCompensation = 0; + std::string puckImagePath; + std::string puckShadowImagePath; + std::string puckHatImagePath; +}; + +class RenderLocationIndicatorImpl { +protected: + struct vec2 { + GLfloat x = 0.0f; + GLfloat y = 0.0f; + + vec2(GLfloat x_, GLfloat y_) : x(x_), y(y_) {} + vec2() = default; + explicit vec2(const Point& p) : x(p.x), y(p.y) {} + vec2(const vec2& o) = default; + vec2(vec2&& o) = default; + vec2& operator=(vec2&& o) = default; + vec2& operator=(const vec2& o) = default; + float length() const { return std::sqrt(x * x + y * y); } + vec2 normalized() const { + const float size = length(); + return {x / size, y / size}; + } + void normalize() { *this = normalized(); } + vec2 mirrored(const vec2& mirror) const { + float k = dot(mirror) / mirror.length(); + return 2.0 * k * mirror - (*this); + } + float dot(const vec2& v2) const { return x * v2.x + y * v2.y; } + vec2 rotated(float degrees) const { + const float cs = std::cos(degrees * util::DEG2RAD); + const float sn = std::sin(degrees * util::DEG2RAD); + return vec2{x * cs + y * sn, x * sn + y * cs}.normalized(); + } + float bearing() const { + const vec2 norm = normalized(); + + // From theta to bearing + return util::wrap(M_PI_2 - std::atan2(-norm.y, norm.x), 0, M_PI * 2.0) * util::RAD2DEG; + } + Point toPoint() const { return {x, y}; } + + friend vec2 operator-(const vec2& v) { return {-v.x, -v.y}; } + friend vec2 operator*(double a, const vec2& v) { return {GLfloat(v.x * a), GLfloat(v.y * a)}; } + friend vec2 operator+(const vec2& v1, const vec2& v2) { return {v1.x + v2.x, v1.y + v2.y}; } + friend vec2 operator-(const vec2& v1, const vec2& v2) { return {v1.x - v2.x, v1.y - v2.y}; } + }; + + struct Shader { + virtual ~Shader() { release(); } + void release() { + if (!program) return; + MBGL_CHECK_ERROR(glDetachShader(program, vertexShader)); + MBGL_CHECK_ERROR(glDetachShader(program, fragmentShader)); + MBGL_CHECK_ERROR(glDeleteShader(vertexShader)); + MBGL_CHECK_ERROR(glDeleteShader(fragmentShader)); + MBGL_CHECK_ERROR(glDeleteProgram(program)); + program = vertexShader = fragmentShader = 0; + } + void initialize(const GLchar* const& vsSource, const GLchar* const& fsSource) { + if (program) return; + program = MBGL_CHECK_ERROR(glCreateProgram()); + vertexShader = MBGL_CHECK_ERROR(glCreateShader(GL_VERTEX_SHADER)); + fragmentShader = MBGL_CHECK_ERROR(glCreateShader(GL_FRAGMENT_SHADER)); + MBGL_CHECK_ERROR(glShaderSource(vertexShader, 1, &vsSource, nullptr)); + MBGL_CHECK_ERROR(glCompileShader(vertexShader)); + MBGL_CHECK_ERROR(glAttachShader(program, vertexShader)); + MBGL_CHECK_ERROR(glShaderSource(fragmentShader, 1, &fsSource, nullptr)); + MBGL_CHECK_ERROR(glCompileShader(fragmentShader)); + MBGL_CHECK_ERROR(glAttachShader(program, fragmentShader)); + MBGL_CHECK_ERROR(glLinkProgram(program)); + pullLocations(); + } + virtual void bind() { MBGL_CHECK_ERROR(glUseProgram(program)); } + void detach() { MBGL_CHECK_ERROR(glUseProgram(0)); } + virtual void pullLocations(){}; + + GLuint program = 0; + GLuint vertexShader = 0; + GLuint fragmentShader = 0; + }; + + struct SimpleShader : public Shader { + // Note that custom layers need to draw geometry with a z value of 1 to take advantage of + // depth-based fragment culling. + const GLchar* vertexShaderSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#endif + +attribute vec2 a_pos; +uniform mat4 u_matrix; +void main() { + gl_Position = u_matrix * vec4(a_pos, 0, 1); +} +)MBGL_SHADER"; + + const GLchar* fragmentShaderSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#endif + +uniform vec4 u_color; +void main() { + gl_FragColor = u_color; +} +)MBGL_SHADER"; + + void initialize() { Shader::initialize(SimpleShader::vertexShaderSource, SimpleShader::fragmentShaderSource); } + + void pullLocations() override { + a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); + u_color = MBGL_CHECK_ERROR(glGetUniformLocation(program, "u_color")); + u_matrix = MBGL_CHECK_ERROR(glGetUniformLocation(program, "u_matrix")); + } + void bind() override { + SimpleShader::initialize(); + Shader::bind(); + } + + GLuint a_pos = 0; + GLuint u_color = 0; + GLuint u_matrix = 0; + }; + + struct TexturedShader : public Shader { + const GLchar* vertexShaderSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#endif + +attribute vec2 a_pos; +attribute vec2 a_texCoord; +uniform mat4 u_matrix; +varying vec2 v_texCoord; +void main() { + gl_Position = u_matrix * vec4(a_pos, 0, 1); + v_texCoord = a_texCoord; +} +)MBGL_SHADER"; + + const GLchar* fragmentShaderSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#endif + +uniform sampler2D u_image; +varying vec2 v_texCoord; +void main() { + vec4 color = texture2D(u_image, v_texCoord); + gl_FragColor = color; +} +)MBGL_SHADER"; + + void initialize() { Shader::initialize(vertexShaderSource, fragmentShaderSource); } + + void pullLocations() override { + a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); + a_texCoord = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_texCoord")); + u_image = MBGL_CHECK_ERROR(glGetUniformLocation(program, "u_image")); + u_matrix = MBGL_CHECK_ERROR(glGetUniformLocation(program, "u_matrix")); + } + void bind() override { + TexturedShader::initialize(); + Shader::bind(); + } + + GLuint a_pos = 0; + GLuint a_texCoord = 0; + GLuint u_image = 0; + GLuint u_matrix = 0; + }; + + struct Buffer { + virtual ~Buffer() { release(); } + void release() { + if (!bufferId) return; + MBGL_CHECK_ERROR(glDeleteBuffers(1, &bufferId)); + bufferId = 0; + } + void initialize() { + if (!bufferId) MBGL_CHECK_ERROR(glGenBuffers(1, &bufferId)); + } + void bind(const GLenum target = GL_ARRAY_BUFFER) { + initialize(); + MBGL_CHECK_ERROR(glBindBuffer(target, bufferId)); + } + void detach(const GLenum target = GL_ARRAY_BUFFER) { MBGL_CHECK_ERROR(glBindBuffer(target, 0)); } + template + void upload(const std::array& data) { + bind(); + MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, N * sizeof(T), data.data(), GL_STATIC_DRAW)); + size = static_cast(N * sizeof(T)); + elements = N; + } + template + void upload(const std::vector& data) { + bind(); + MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(T), data.data(), GL_STATIC_DRAW)); + size = data.size() * sizeof(T); + elements = data.size(); + } + + GLuint bufferId = 0; + unsigned int size = 0; + unsigned int elements = 0; + }; + +public: + struct Texture { + ~Texture() { release(); } + void release() { + MBGL_CHECK_ERROR(glDeleteTextures(1, &texId)); + texId = 0; + image = nullptr; + } + /* + Assign can be called any time. Conversely, upload must be called with a bound gl context. + */ + void assign(const Immutable* img) { + if ((img && &img->get()->image == image) || (!img && !image)) return; + imageDirty = true; + image = (img) ? &img->get()->image : nullptr; + if (img) + sharedImage = *img; // keep reference until uploaded + else + sharedImage = nullopt; + } + + void upload() { + if (!imageDirty) return; + imageDirty = false; + initialize(); + + MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texId)); + if (!image || !image->valid()) { + MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); + } else { + MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, + image->size.width, + image->size.height, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + image->data.get())); + MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)); + MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + MBGL_CHECK_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); + if (RenderLocationIndicatorImpl::anisotropicFilteringAvailable) + MBGL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16)); + } + detach(); + sharedImage = nullopt; + } + void initialize() { + if (texId != 0) return; + MBGL_CHECK_ERROR(glGenTextures(1, &texId)); + } + void bind(int textureUnit = -1) { + initialize(); + if (!image && !imageDirty) return; + + upload(); + if (textureUnit >= 0) MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0 + textureUnit)); + MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texId)); + } + void detach() { MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, 0)); } + bool isValid() { return imageDirty || image; } + GLuint texId = 0; + const mbgl::PremultipliedImage* image = nullptr; + optional> sharedImage; + bool imageDirty = false; + }; + + RenderLocationIndicatorImpl() : ruler(0, mapbox::cheap_ruler::CheapRuler::Meters) {} + + static bool hasExtension(const std::string& ext) { + if (const auto* extensions = reinterpret_cast(MBGL_CHECK_ERROR(glGetString(GL_EXTENSIONS)))) { + if (strstr(extensions, ext.c_str()) != nullptr) return true; + } + return false; + } + void initialize() { + // Check if anisotropic filtering is available + if (initialized) return; + initialized = true; + if (hasExtension("GL_EXT_texture_filter_anisotropic")) anisotropicFilteringAvailable = true; + simpleShader.initialize(); + texturedShader.initialize(); + texCoords = {{{0.0f, 1.0f}, + {0.0f, 0.0f}, + {1.0f, 0.0f}, + {1.0f, 1.0f}}}; // Quads will be drawn as triangle fans. so bl, tl, tr, br + texCoordsBuffer.upload(texCoords); + } + + void render(const mbgl::LocationIndicatorRenderParameters& params) { + initialize(); + drawRadius(params); + drawShadow(); + drawPuck(); + drawHat(); + } + + void release() { + if (!simpleShader.program) return; + textures.clear(); + buffer.release(); + circleBuffer.release(); + puckBuffer.release(); + hatBuffer.release(); + texCoordsBuffer.release(); + simpleShader.release(); + texturedShader.release(); + } + + void updatePuckGeometry(const mbgl::LocationIndicatorRenderParameters& params) { + if (params.projectionMatrix != oldParams.projectionMatrix) positionChanged = true; + if (params.puckPosition != oldParams.puckPosition) { + positionChanged = true; + ruler = mapbox::cheap_ruler::CheapRuler(params.puckPosition.latitude(), + mapbox::cheap_ruler::CheapRuler::Meters); + } else if (params.puckBearing != oldParams.puckBearing || + params.puckLayersDisplacement != oldParams.puckLayersDisplacement || + params.perspectiveCompensation != oldParams.perspectiveCompensation || + params.puckSizePx != oldParams.puckSizePx || params.puckHatSizePx != oldParams.puckHatSizePx || + params.puckShadowSizePx != oldParams.puckShadowSizePx) + bearingChanged = true; // changes puck geometry but not necessarily the location + if (params.errorRadiusMeters != oldParams.errorRadiusMeters) radiusChanged = true; + if (params.puckImagePath != oldParams.puckImagePath) + setTextureFromImageID(params.puckImagePath, texPuck, params); + if (params.puckShadowImagePath != oldParams.puckShadowImagePath) + setTextureFromImageID(params.puckShadowImagePath, texShadow, params); + if (params.puckHatImagePath != oldParams.puckHatImagePath) + setTextureFromImageID(params.puckHatImagePath, texPuckHat, params); + + projectionCircle = params.projectionMatrix; + const Point positionMercator = project(params.puckPosition, *params.state); + mat4 translation; + matrix::identity(translation); + matrix::translate(translation, translation, positionMercator.x, positionMercator.y, 0.0); + matrix::multiply(projectionCircle, projectionCircle, translation); + + if (positionChanged) { + updateRadius(params); + updatePuck(params); + positionChanged = false; + } else { + if (radiusChanged) { + updateRadius(params); + } + if (bearingChanged) { + updatePuck(params); + } + } + oldParams = params; + } + +protected: + static ScreenCoordinate latLngToScreenCoordinate(const LatLng& p, const TransformState& s) { + LatLng unwrappedLatLng = p.wrapped(); + unwrappedLatLng.unwrapForShortestPath(s.getLatLng(LatLng::Wrapped)); + ScreenCoordinate point = s.latLngToScreenCoordinate(unwrappedLatLng); + point.y = s.getSize().height - point.y; + return point; + } + + static Point project(const LatLng& c, const TransformState& s) { + LatLng unwrappedLatLng = c.wrapped(); + unwrappedLatLng.unwrapForShortestPath(s.getLatLng(LatLng::Wrapped)); + return Projection::project(unwrappedLatLng, s.getScale()); + } + + static Point unproject(const LatLng& c, const TransformState& s) { + LatLng unwrappedLatLng = c.wrapped(); + unwrappedLatLng.unwrapForShortestPath(s.getLatLng(LatLng::Wrapped)); + return Projection::project(unwrappedLatLng, s.getScale()); + } + + void updateRadius(const mbgl::LocationIndicatorRenderParameters& params) { + const TransformState& s = *params.state; + const unsigned long numVtxCircumference = circle.size() - 1; + const float bearingStep = 360.0f / float(numVtxCircumference - 1); // first and last points are the same + const mapbox::cheap_ruler::point centerPoint(params.puckPosition.longitude(), params.puckPosition.latitude()); + Point center = project(params.puckPosition, s); + circle[0] = {0, 0}; + + double mapBearing = util::wrap(util::RAD2DEG * params.bearing, 0.0, util::DEGREES_MAX); + for (unsigned long i = 1; i <= numVtxCircumference; ++i) { + const float bearing_ = float(i - 1) * bearingStep - mapBearing; + Point poc = ruler.destination(centerPoint, params.errorRadiusMeters, bearing_); + circle[i] = vec2(project(LatLng(poc.y, poc.x), s) - center); + } + radiusChanged = false; + } + + // Size in "map pixels" for a screen pixel + static float pixelSizeToWorldSizeH(const LatLng& pos, const TransformState& s) { + ScreenCoordinate posScreen = latLngToScreenCoordinate(pos, s); + ScreenCoordinate posScreenLeftPx = posScreen; + posScreenLeftPx.x -= 1; + LatLng posLeftPx = screenCoordinateToLatLng(posScreenLeftPx, s); + Point posMerc = project(pos, s); + Point posLeftPxMerc = project(posLeftPx, s); + return vec2(posMerc - posLeftPxMerc).length(); + } + + static vec2 verticalDirectionMercator(const LatLng& pos, const TransformState& s) { + ScreenCoordinate posScreen = latLngToScreenCoordinate(pos, s); + Point posMerc = project(pos, s); + return verticalDirectionMercator(posScreen, posMerc, s); + } + + static vec2 verticalDirectionMercator(const ScreenCoordinate& pos, Point posMerc, const TransformState& s) { + ScreenCoordinate screenDy = pos; + screenDy.y -= 1; + LatLng posDy = screenCoordinateToLatLng(screenDy, s); + Point posMercDy = project(posDy, s); + return verticalDirectionMercator(posMerc, posMercDy); + } + + static vec2 verticalDirectionMercator(const Point& posMerc, const Point& posMercDy) { + Point verticalShiftMercator = posMercDy - posMerc; + vec2 res(verticalShiftMercator); + return res.normalized(); + } + + static Point hatShadowShiftVector(const LatLng& position, + const mbgl::LocationIndicatorRenderParameters& params) { + const TransformState& s = *params.state; + ScreenCoordinate posScreen = latLngToScreenCoordinate(position, s); + posScreen.y = params.height - 1; // moving it to bottom + Point posMerc = project(screenCoordinateToLatLng(posScreen, s), s); + vec2 verticalShiftAtPos = verticalDirectionMercator(posScreen, posMerc, s); + return {verticalShiftAtPos.x, verticalShiftAtPos.y}; + } + + static vec2 directionAtPositionScreen(const LatLng& position, float bearing, const TransformState& s) { + const double scale = s.getScale(); + const vec2 rot = vec2(0.0, -1.0).rotated(-bearing); + Point posMerc = project(position, s); + Point posMercDelta = posMerc + rot.toPoint(); + ScreenCoordinate posScreen = latLngToScreenCoordinate(position, s); + ScreenCoordinate posScreenDelta = latLngToScreenCoordinate(Projection::unproject(posMercDelta, scale), s); + return vec2(posScreenDelta - posScreen).normalized(); + } + + void updatePuck(const mbgl::LocationIndicatorRenderParameters& params) { return updatePuckPerspective(params); } + + void updatePuckPerspective(const mbgl::LocationIndicatorRenderParameters& params) { + const TransformState& s = *params.state; + projectionPuck = projectionCircle; // Duplicated as it might change, depending on what puck style is chosen. + const mapbox::cheap_ruler::point centerPoint(params.puckPosition.longitude(), params.puckPosition.latitude()); + static constexpr float bearings[]{ + 225.0f, 315.0f, 45.0f, 135.0f}; // Quads will be drawn as triangle fans. so bl, tl, tr, br +#ifndef M_SQRT2 + static constexpr const float M_SQRT2 = std::sqrt(2.0f); +#endif + // The puck has to stay square at all zoom levels. CheapRuler::destination does not guarantee this at low zoom + // levels, so the extent has to be produced in mercator space + const double tilt = s.getPitch(); + + // Point verticalShiftAtCenter { float(std::sin(util::DEG2RAD * util::wrap(-t.getBearing() * + // util::RAD2DEG, 0.0f, 360.0f) )), + // -float(std::cos(util::DEG2RAD * util::wrap(-t.getBearing() * + // util::RAD2DEG, 0.0f, 360.0f))) }; + // would be correct only in the vertical center of the map. As soon as position goes away from that line, + // the shift direction is skewed by the perspective projection. + // So the way to have a shift aligned to the screen vertical axis is to find this direction in screen space, and + // convert it back to map space. This would yield an always straight up shift. However, going further (= the + // opposite direction of where the lines are converging in the projection) might produce an even more realistic + // effect. But in this case, it empirically seems that the largest shift that look acceptable is what is + // obtained at the bottom of the window, avoiding the wider converging lines that pass by the edge of the screen + // going toward the top. + + Point verticalShift = hatShadowShiftVector(params.puckPosition, params); + const float horizontalScaleFactor = + (1.0f - params.perspectiveCompensation) + + util::clamp(pixelSizeToWorldSizeH(params.puckPosition, s), 0.8f, 100.1f) * + params.perspectiveCompensation; // Compensation factor for the perspective deformation + // ^ clamping this to 0.8 to avoid growing the puck too much close to the camera. + const double shadowRadius = + params.puckShadowSizePx * M_SQRT2 * 0.5 * + horizontalScaleFactor; // Technically it's not the radius, but the half diagonal of the quad. + const double puckRadius = params.puckSizePx * M_SQRT2 * 0.5 * horizontalScaleFactor; + const double hatRadius = params.puckHatSizePx * M_SQRT2 * 0.5 * horizontalScaleFactor; + + for (unsigned long i = 0; i < 4; ++i) { + const auto b = util::wrap(params.puckBearing + bearings[i], 0.0f, 360.0f); + + const Point cornerDirection{float(std::sin(util::DEG2RAD * b)), + -float(std::cos(util::DEG2RAD * b))}; + + Point shadowOffset = cornerDirection * shadowRadius; + Point puckOffset = cornerDirection * puckRadius; + Point hatOffset = cornerDirection * hatRadius; + + shadowGeometry[i] = + vec2(shadowOffset + (verticalShift * (tilt * -params.puckLayersDisplacement * horizontalScaleFactor))); + puckGeometry[i] = vec2(puckOffset); + hatGeometry[i] = + vec2(hatOffset + (verticalShift * (tilt * params.puckLayersDisplacement * horizontalScaleFactor))); + } + + bearingChanged = false; + } + + void drawRadius(const mbgl::LocationIndicatorRenderParameters& params) { + if (!(params.errorRadiusMeters > 0.0) || + (params.errorRadiusColor.a == 0.0 && params.errorRadiusBorderColor.a == 0.0)) + return; + + simpleShader.bind(); + mbgl::gl::bindUniform(simpleShader.u_color, params.errorRadiusColor); + mbgl::gl::bindUniform(simpleShader.u_matrix, projectionCircle); + + circleBuffer.upload(circle); + MBGL_CHECK_ERROR(glEnableVertexAttribArray(simpleShader.a_pos)); + MBGL_CHECK_ERROR(glVertexAttribPointer(simpleShader.a_pos, 2, GL_FLOAT, GL_FALSE, 0, nullptr)); + + MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_FAN, 0, circle.size())); + if (params.errorRadiusBorderColor.a > 0.0f) { + mbgl::gl::bindUniform(simpleShader.u_color, params.errorRadiusBorderColor); + MBGL_CHECK_ERROR(glLineWidth(1.0f)); + MBGL_CHECK_ERROR(glDrawArrays(GL_LINE_STRIP, 1, circle.size() - 1)); + } + MBGL_CHECK_ERROR(glDisableVertexAttribArray(simpleShader.a_pos)); + circleBuffer.detach(); + simpleShader.detach(); + } + + void drawQuad(Buffer& buf, std::array& data, std::shared_ptr& texture) { + if (!texture || !texture->isValid()) return; + texturedShader.bind(); + texture->bind(0); + glUniform1i(texturedShader.u_image, 0); + mbgl::gl::bindUniform(texturedShader.u_matrix, projectionPuck); + + buf.bind(); + buf.upload(data); + MBGL_CHECK_ERROR(glEnableVertexAttribArray(texturedShader.a_pos)); + MBGL_CHECK_ERROR(glVertexAttribPointer(texturedShader.a_pos, 2, GL_FLOAT, GL_FALSE, 0, nullptr)); + + texCoordsBuffer.bind(); + MBGL_CHECK_ERROR(glEnableVertexAttribArray(texturedShader.a_texCoord)); + MBGL_CHECK_ERROR(glVertexAttribPointer(texturedShader.a_texCoord, 2, GL_FLOAT, GL_FALSE, 0, nullptr)); + + MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_FAN, 0, 4)); + texture->detach(); + texCoordsBuffer.detach(); + texturedShader.detach(); + } + + void drawShadow() { drawQuad(shadowBuffer, shadowGeometry, texShadow); } + + void drawPuck() { drawQuad(puckBuffer, puckGeometry, texPuck); } + + void drawHat() { drawQuad(hatBuffer, hatGeometry, texPuckHat); } + + static LatLng screenCoordinateToLatLng(const ScreenCoordinate& p, + const TransformState& s, + LatLng::WrapMode wrapMode = LatLng::Wrapped) { + ScreenCoordinate flippedPoint = p; + flippedPoint.y = s.getSize().height - flippedPoint.y; + return s.screenCoordinateToLatLng(flippedPoint, wrapMode); + } + + void setTextureFromImageID(const std::string& imagePath, + std::shared_ptr& tex, + const mbgl::LocationIndicatorRenderParameters& params) { + if (textures.find(imagePath) == textures.end()) { + std::shared_ptr tx = std::make_shared(); + if (!imagePath.empty() && params.imageManager) + tx->assign(params.imageManager->getSharedImage(imagePath)); + else + tx->assign(nullptr); + textures[imagePath] = tx; + } + tex = textures.at(imagePath); + } + + std::map> textures; + mapbox::cheap_ruler::CheapRuler ruler; + SimpleShader simpleShader; + TexturedShader texturedShader; + Buffer buffer; + Buffer circleBuffer; + Buffer shadowBuffer; + Buffer puckBuffer; + Buffer hatBuffer; + Buffer texCoordsBuffer; + std::shared_ptr texShadow; + std::shared_ptr texPuck; + std::shared_ptr texPuckHat; + + std::array circle; // 72 points + position + std::array shadowGeometry; + std::array puckGeometry; + std::array hatGeometry; + std::array texCoords; + mbgl::mat4 projectionCircle; + mbgl::mat4 projectionPuck; + + bool positionChanged = false; + bool radiusChanged = false; + bool bearingChanged = false; + mbgl::LocationIndicatorRenderParameters oldParams; + bool initialized = false; + +public: + mbgl::LocationIndicatorRenderParameters parameters; + static bool anisotropicFilteringAvailable; +}; + +bool RenderLocationIndicatorImpl::anisotropicFilteringAvailable = false; + +using namespace style; +namespace { + +inline const LocationIndicatorLayer::Impl& impl(const Immutable& impl) { + assert(impl->getTypeInfo() == LocationIndicatorLayer::Impl::staticTypeInfo()); + return static_cast(*impl); +} +} // namespace + +RenderLocationIndicatorLayer::RenderLocationIndicatorLayer(Immutable _impl) + : RenderLayer(makeMutable(std::move(_impl))), + renderImpl(new RenderLocationIndicatorImpl()), + unevaluated(impl(baseImpl).paint.untransitioned()) { + assert(gfx::BackendScope::exists()); +} + +RenderLocationIndicatorLayer::~RenderLocationIndicatorLayer() { + assert(gfx::BackendScope::exists()); + if (!contextDestroyed) MBGL_CHECK_ERROR(renderImpl->release()); +} + +void RenderLocationIndicatorLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl(baseImpl).paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderLocationIndicatorLayer::evaluate(const PropertyEvaluationParameters& parameters) { + passes = RenderPass::Translucent; + auto properties = makeMutable( + staticImmutableCast(baseImpl), unevaluated.evaluate(parameters)); + const auto& evaluated = properties->evaluated; + auto& layout = impl(baseImpl).layout; + + properties->renderPasses = mbgl::underlying_type(passes); + + // paint + renderImpl->parameters.errorRadiusColor = evaluated.get(); + renderImpl->parameters.errorRadiusBorderColor = evaluated.get(); + renderImpl->parameters.errorRadiusMeters = evaluated.get(); + renderImpl->parameters.puckSizePx = evaluated.get(); + renderImpl->parameters.puckHatSizePx = evaluated.get(); + renderImpl->parameters.puckShadowSizePx = evaluated.get(); + + const std::array pos = evaluated.get(); + renderImpl->parameters.puckPosition = LatLng{pos[0], pos[1]}; + + // layout + if (!layout.get().isUndefined()) + renderImpl->parameters.puckBearing = layout.get().asConstant(); + if (!layout.get().isUndefined()) + renderImpl->parameters.puckImagePath = layout.get().asConstant().id(); + if (!layout.get().isUndefined()) + renderImpl->parameters.puckShadowImagePath = layout.get().asConstant().id(); + if (!layout.get().isUndefined()) + renderImpl->parameters.puckHatImagePath = layout.get().asConstant().id(); + if (!layout.get().isUndefined()) + renderImpl->parameters.puckLayersDisplacement = layout.get().asConstant(); + if (!layout.get().isUndefined()) + renderImpl->parameters.perspectiveCompensation = layout.get().asConstant(); + + evaluatedProperties = std::move(properties); +} + +bool RenderLocationIndicatorLayer::hasTransition() const { + return unevaluated.hasTransition(); +} +bool RenderLocationIndicatorLayer::hasCrossfade() const { + return false; +} + +void RenderLocationIndicatorLayer::markContextDestroyed() { + contextDestroyed = true; +} + +void RenderLocationIndicatorLayer::prepare(const LayerPrepareParameters& p) { + renderImpl->parameters.imageManager = &p.imageManager; + const TransformState& state = p.state; + renderImpl->parameters.state = &state; + + renderImpl->parameters.width = state.getSize().width; + renderImpl->parameters.height = state.getSize().height; + renderImpl->parameters.latitude = state.getLatLng().latitude(); + renderImpl->parameters.longitude = state.getLatLng().longitude(); + renderImpl->parameters.zoom = state.getZoom(); + renderImpl->parameters.bearing = -state.getBearing() * util::RAD2DEG; + renderImpl->parameters.pitch = state.getPitch(); + mat4 projMatrix; + state.getProjMatrix(projMatrix); + renderImpl->parameters.projectionMatrix = projMatrix; + + renderImpl->updatePuckGeometry(renderImpl->parameters); +} + +void RenderLocationIndicatorLayer::render(PaintParameters& paintParameters) { + auto& glContext = static_cast(paintParameters.context); + + // Reset GL state to a known state so the CustomLayer always has a clean slate. + glContext.bindVertexArray = 0; + glContext.setDepthMode(paintParameters.depthModeForSublayer(0, gfx::DepthMaskType::ReadOnly)); + glContext.setStencilMode(gfx::StencilMode::disabled()); + glContext.setColorMode(paintParameters.colorModeForRenderPass()); // this is gfx::ColorMode::alphaBlended() + glContext.setCullFaceMode(gfx::CullFaceMode::disabled()); + + MBGL_CHECK_ERROR(renderImpl->render(renderImpl->parameters)); + + // Reset the view back to our original one, just in case the CustomLayer changed + // the viewport or Framebuffer. + paintParameters.backend.getDefaultRenderable().getResource().bind(); + glContext.setDirtyState(); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_location_indicator_layer.hpp b/src/mbgl/renderer/layers/render_location_indicator_layer.hpp new file mode 100644 index 000000000..739e81479 --- /dev/null +++ b/src/mbgl/renderer/layers/render_location_indicator_layer.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +class RenderLocationIndicatorImpl; +class RenderLocationIndicatorLayer final : public RenderLayer { +public: + explicit RenderLocationIndicatorLayer(Immutable); + ~RenderLocationIndicatorLayer() override; + +private: + void transition(const TransitionParameters &) override; + void evaluate(const PropertyEvaluationParameters &) override; + bool hasTransition() const override; + bool hasCrossfade() const override; + void markContextDestroyed() override; + void prepare(const LayerPrepareParameters &) override; + + void render(PaintParameters &) override; + + bool contextDestroyed = false; + RenderLocationIndicatorImpl *renderImpl = nullptr; + style::LocationIndicatorPaintProperties::Unevaluated unevaluated; +}; + +} // namespace mbgl diff --git a/src/mbgl/style/conversion/constant.cpp b/src/mbgl/style/conversion/constant.cpp index ffdb17858..0e4e29f68 100644 --- a/src/mbgl/style/conversion/constant.cpp +++ b/src/mbgl/style/conversion/constant.cpp @@ -127,6 +127,28 @@ template optional> Converter>::operato template optional> Converter>::operator()(const Convertible&, Error&) const; template optional> Converter>::operator()(const Convertible&, Error&) const; +template +optional> Converter>::operator()(const Convertible& value, + Error& error) const { + if (!isArray(value) || arrayLength(value) != N) { + error.message = "value must be an array of " + util::toString(N) + " numbers"; + return nullopt; + } + + std::array result; + for (size_t i = 0; i < N; i++) { + optional n = toDouble(arrayMember(value, i)); + if (!n) { + error.message = "value must be an array of " + util::toString(N) + " numbers"; + return nullopt; + } + result[i] = *n; + } + return result; +} + +template optional> Converter>::operator()(const Convertible&, Error&) const; + optional> Converter>::operator()(const Convertible& value, Error& error) const { if (!isArray(value)) { error.message = "value must be an array"; diff --git a/src/mbgl/style/conversion/property_value.cpp b/src/mbgl/style/conversion/property_value.cpp index 5e03189e6..1a0e68685 100644 --- a/src/mbgl/style/conversion/property_value.cpp +++ b/src/mbgl/style/conversion/property_value.cpp @@ -88,6 +88,25 @@ Converter>::operator()(conversion: bool, bool) const; +optional>> +mbgl::style::conversion::Converter>, void>::operator()(const Convertible& value, + Error& error, + bool, + bool) const { + optional> a = convert>(value, error); + + if (!a) { + return nullopt; + } + std::array res; + res[0] = (*a)[0]; + res[1] = (*a)[1]; + res[2] = (*a)[2]; + + PropertyValue> r(res); + return r; +} + } // namespace conversion } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp index 381618245..d7908153f 100644 --- a/src/mbgl/style/conversion/stringify.hpp +++ b/src/mbgl/style/conversion/stringify.hpp @@ -76,6 +76,15 @@ void stringify(Writer& writer, const std::array& v) { writer.EndArray(); } +template +void stringify(Writer& writer, const std::array& v) { + writer.StartArray(); + writer.Double(v[0]); + writer.Double(v[1]); + writer.Double(v[2]); + writer.EndArray(); +} + template void stringify(Writer&, const Value&); diff --git a/src/mbgl/style/expression/value.cpp b/src/mbgl/style/expression/value.cpp index 110844f42..9f40d17b0 100644 --- a/src/mbgl/style/expression/value.cpp +++ b/src/mbgl/style/expression/value.cpp @@ -310,6 +310,10 @@ template <> type::Type valueTypeToExpressionType() { return typ template type::Type valueTypeToExpressionType>(); template struct ValueConverter>; +// for LocationIndicator position +template type::Type valueTypeToExpressionType>(); +template struct ValueConverter>; + // layout/paint property types template type::Type valueTypeToExpressionType(); template type::Type valueTypeToExpressionType(); diff --git a/src/mbgl/style/layers/layer.cpp.ejs b/src/mbgl/style/layers/layer.cpp.ejs index ab44a9278..fd5bf619f 100644 --- a/src/mbgl/style/layers/layer.cpp.ejs +++ b/src/mbgl/style/layers/layer.cpp.ejs @@ -8,8 +8,8 @@ // This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`. -#include _layer.hpp> -#include _layer_impl.hpp> +#include _layer.hpp> +#include _layer_impl.hpp> #include #include #include @@ -80,6 +80,7 @@ layerCapabilities['heatmap'] = defaults.require('Source') .set('TileKind', 'Geometry') .finalize(); layerCapabilities['raster'] = defaults.require('Source').set('TileKind', 'Raster').finalize(); +layerCapabilities['location-indicator'] = defaults.finalize(); // Splits lines that are over 120 characters at the firts occurance of '='. const split120Line = line => { @@ -99,7 +100,7 @@ const LayerTypeInfo* <%- camelize(type) %>Layer::Impl::staticTypeInfo() noexcept } -<% if (type === 'background') { -%> +<% if ((type === 'background') || (type === 'location-indicator')) { -%> <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(const std::string& layerID) : Layer(makeMutable(layerID, std::string())) { } diff --git a/src/mbgl/style/layers/layer_properties.cpp.ejs b/src/mbgl/style/layers/layer_properties.cpp.ejs index 18b07efea..09f4c4837 100644 --- a/src/mbgl/style/layers/layer_properties.cpp.ejs +++ b/src/mbgl/style/layers/layer_properties.cpp.ejs @@ -7,9 +7,9 @@ // This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`. -#include _layer_properties.hpp> +#include _layer_properties.hpp> -#include _layer_impl.hpp> +#include _layer_impl.hpp> namespace mbgl { namespace style { diff --git a/src/mbgl/style/layers/layer_properties.hpp.ejs b/src/mbgl/style/layers/layer_properties.hpp.ejs index af67f14b9..66cff8c3c 100644 --- a/src/mbgl/style/layers/layer_properties.hpp.ejs +++ b/src/mbgl/style/layers/layer_properties.hpp.ejs @@ -11,7 +11,7 @@ #include #include -#include _layer.hpp> +#include _layer.hpp> #include #include #include diff --git a/src/mbgl/style/layers/location_indicator_layer.cpp b/src/mbgl/style/layers/location_indicator_layer.cpp new file mode 100644 index 000000000..03b826cc1 --- /dev/null +++ b/src/mbgl/style/layers/location_indicator_layer.cpp @@ -0,0 +1,626 @@ +// clang-format off + +// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace mbgl { +namespace style { + + +// static +const LayerTypeInfo* LocationIndicatorLayer::Impl::staticTypeInfo() noexcept { + const static LayerTypeInfo typeInfo{"location-indicator", + LayerTypeInfo::Source::NotRequired, + LayerTypeInfo::Pass3D::NotRequired, + LayerTypeInfo::Layout::NotRequired, + LayerTypeInfo::FadingTiles::NotRequired, + LayerTypeInfo::CrossTileIndex::NotRequired, + LayerTypeInfo::TileKind::NotRequired}; + return &typeInfo; +} + + +LocationIndicatorLayer::LocationIndicatorLayer(const std::string& layerID) + : Layer(makeMutable(layerID, std::string())) { +} + +LocationIndicatorLayer::LocationIndicatorLayer(Immutable impl_) + : Layer(std::move(impl_)) { +} + +LocationIndicatorLayer::~LocationIndicatorLayer() = default; + +const LocationIndicatorLayer::Impl& LocationIndicatorLayer::impl() const { + return static_cast(*baseImpl); +} + +Mutable LocationIndicatorLayer::mutableImpl() const { + return makeMutable(impl()); +} + +std::unique_ptr LocationIndicatorLayer::cloneRef(const std::string& id_) const { + auto impl_ = mutableImpl(); + impl_->id = id_; + impl_->paint = LocationIndicatorPaintProperties::Transitionable(); + return std::make_unique(std::move(impl_)); +} + +void LocationIndicatorLayer::Impl::stringifyLayout(rapidjson::Writer& writer) const { + layout.stringify(writer); +} + +// Layout properties + +PropertyValue LocationIndicatorLayer::getDefaultBearing() { + return Bearing::defaultValue(); +} + +const PropertyValue& LocationIndicatorLayer::getBearing() const { + return impl().layout.get(); +} + +void LocationIndicatorLayer::setBearing(const PropertyValue& value) { + if (value == getBearing()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +PropertyValue LocationIndicatorLayer::getDefaultBearingImage() { + return BearingImage::defaultValue(); +} + +const PropertyValue& LocationIndicatorLayer::getBearingImage() const { + return impl().layout.get(); +} + +void LocationIndicatorLayer::setBearingImage(const PropertyValue& value) { + if (value == getBearingImage()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +PropertyValue LocationIndicatorLayer::getDefaultImageTiltDisplacement() { + return ImageTiltDisplacement::defaultValue(); +} + +const PropertyValue& LocationIndicatorLayer::getImageTiltDisplacement() const { + return impl().layout.get(); +} + +void LocationIndicatorLayer::setImageTiltDisplacement(const PropertyValue& value) { + if (value == getImageTiltDisplacement()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +PropertyValue LocationIndicatorLayer::getDefaultPerspectiveCompensation() { + return PerspectiveCompensation::defaultValue(); +} + +const PropertyValue& LocationIndicatorLayer::getPerspectiveCompensation() const { + return impl().layout.get(); +} + +void LocationIndicatorLayer::setPerspectiveCompensation(const PropertyValue& value) { + if (value == getPerspectiveCompensation()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +PropertyValue LocationIndicatorLayer::getDefaultShadowImage() { + return ShadowImage::defaultValue(); +} + +const PropertyValue& LocationIndicatorLayer::getShadowImage() const { + return impl().layout.get(); +} + +void LocationIndicatorLayer::setShadowImage(const PropertyValue& value) { + if (value == getShadowImage()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +PropertyValue LocationIndicatorLayer::getDefaultTopImage() { + return TopImage::defaultValue(); +} + +const PropertyValue& LocationIndicatorLayer::getTopImage() const { + return impl().layout.get(); +} + +void LocationIndicatorLayer::setTopImage(const PropertyValue& value) { + if (value == getTopImage()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +// Paint properties + +PropertyValue LocationIndicatorLayer::getDefaultAccuracyRadius() { + return {0}; +} + +const PropertyValue& LocationIndicatorLayer::getAccuracyRadius() const { + return impl().paint.template get().value; +} + +void LocationIndicatorLayer::setAccuracyRadius(const PropertyValue& value) { + if (value == getAccuracyRadius()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void LocationIndicatorLayer::setAccuracyRadiusTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get().options = options; + baseImpl = std::move(impl_); +} + +TransitionOptions LocationIndicatorLayer::getAccuracyRadiusTransition() const { + return impl().paint.template get().options; +} + +PropertyValue LocationIndicatorLayer::getDefaultAccuracyRadiusBorderColor() { + return {Color::white()}; +} + +const PropertyValue& LocationIndicatorLayer::getAccuracyRadiusBorderColor() const { + return impl().paint.template get().value; +} + +void LocationIndicatorLayer::setAccuracyRadiusBorderColor(const PropertyValue& value) { + if (value == getAccuracyRadiusBorderColor()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void LocationIndicatorLayer::setAccuracyRadiusBorderColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get().options = options; + baseImpl = std::move(impl_); +} + +TransitionOptions LocationIndicatorLayer::getAccuracyRadiusBorderColorTransition() const { + return impl().paint.template get().options; +} + +PropertyValue LocationIndicatorLayer::getDefaultAccuracyRadiusColor() { + return {Color::white()}; +} + +const PropertyValue& LocationIndicatorLayer::getAccuracyRadiusColor() const { + return impl().paint.template get().value; +} + +void LocationIndicatorLayer::setAccuracyRadiusColor(const PropertyValue& value) { + if (value == getAccuracyRadiusColor()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void LocationIndicatorLayer::setAccuracyRadiusColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get().options = options; + baseImpl = std::move(impl_); +} + +TransitionOptions LocationIndicatorLayer::getAccuracyRadiusColorTransition() const { + return impl().paint.template get().options; +} + +PropertyValue LocationIndicatorLayer::getDefaultBearingImageSize() { + return {0}; +} + +const PropertyValue& LocationIndicatorLayer::getBearingImageSize() const { + return impl().paint.template get().value; +} + +void LocationIndicatorLayer::setBearingImageSize(const PropertyValue& value) { + if (value == getBearingImageSize()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void LocationIndicatorLayer::setBearingImageSizeTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get().options = options; + baseImpl = std::move(impl_); +} + +TransitionOptions LocationIndicatorLayer::getBearingImageSizeTransition() const { + return impl().paint.template get().options; +} + +PropertyValue> LocationIndicatorLayer::getDefaultLocation() { + return {{{0, 0, 0}}}; +} + +const PropertyValue>& LocationIndicatorLayer::getLocation() const { + return impl().paint.template get().value; +} + +void LocationIndicatorLayer::setLocation(const PropertyValue>& value) { + if (value == getLocation()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void LocationIndicatorLayer::setLocationTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get().options = options; + baseImpl = std::move(impl_); +} + +TransitionOptions LocationIndicatorLayer::getLocationTransition() const { + return impl().paint.template get().options; +} + +PropertyValue LocationIndicatorLayer::getDefaultShadowImageSize() { + return {0}; +} + +const PropertyValue& LocationIndicatorLayer::getShadowImageSize() const { + return impl().paint.template get().value; +} + +void LocationIndicatorLayer::setShadowImageSize(const PropertyValue& value) { + if (value == getShadowImageSize()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void LocationIndicatorLayer::setShadowImageSizeTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get().options = options; + baseImpl = std::move(impl_); +} + +TransitionOptions LocationIndicatorLayer::getShadowImageSizeTransition() const { + return impl().paint.template get().options; +} + +PropertyValue LocationIndicatorLayer::getDefaultTopImageSize() { + return {0}; +} + +const PropertyValue& LocationIndicatorLayer::getTopImageSize() const { + return impl().paint.template get().value; +} + +void LocationIndicatorLayer::setTopImageSize(const PropertyValue& value) { + if (value == getTopImageSize()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void LocationIndicatorLayer::setTopImageSizeTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get().options = options; + baseImpl = std::move(impl_); +} + +TransitionOptions LocationIndicatorLayer::getTopImageSizeTransition() const { + return impl().paint.template get().options; +} + +using namespace conversion; + +namespace { + +constexpr uint8_t kPaintPropertyCount = 14u; + +enum class Property : uint8_t { + AccuracyRadius, + AccuracyRadiusBorderColor, + AccuracyRadiusColor, + BearingImageSize, + Location, + ShadowImageSize, + TopImageSize, + AccuracyRadiusTransition, + AccuracyRadiusBorderColorTransition, + AccuracyRadiusColorTransition, + BearingImageSizeTransition, + LocationTransition, + ShadowImageSizeTransition, + TopImageSizeTransition, + Bearing = kPaintPropertyCount, + BearingImage, + ImageTiltDisplacement, + PerspectiveCompensation, + ShadowImage, + TopImage, +}; + +template +constexpr uint8_t toUint8(T t) noexcept { + return uint8_t(mbgl::underlying_type(t)); +} + +MAPBOX_ETERNAL_CONSTEXPR const auto layerProperties = mapbox::eternal::hash_map( + {{"accuracy-radius", toUint8(Property::AccuracyRadius)}, + {"accuracy-radius-border-color", toUint8(Property::AccuracyRadiusBorderColor)}, + {"accuracy-radius-color", toUint8(Property::AccuracyRadiusColor)}, + {"bearing-image-size", toUint8(Property::BearingImageSize)}, + {"location", toUint8(Property::Location)}, + {"shadow-image-size", toUint8(Property::ShadowImageSize)}, + {"top-image-size", toUint8(Property::TopImageSize)}, + {"accuracy-radius-transition", toUint8(Property::AccuracyRadiusTransition)}, + {"accuracy-radius-border-color-transition", toUint8(Property::AccuracyRadiusBorderColorTransition)}, + {"accuracy-radius-color-transition", toUint8(Property::AccuracyRadiusColorTransition)}, + {"bearing-image-size-transition", toUint8(Property::BearingImageSizeTransition)}, + {"location-transition", toUint8(Property::LocationTransition)}, + {"shadow-image-size-transition", toUint8(Property::ShadowImageSizeTransition)}, + {"top-image-size-transition", toUint8(Property::TopImageSizeTransition)}, + {"bearing", toUint8(Property::Bearing)}, + {"bearing-image", toUint8(Property::BearingImage)}, + {"image-tilt-displacement", toUint8(Property::ImageTiltDisplacement)}, + {"perspective-compensation", toUint8(Property::PerspectiveCompensation)}, + {"shadow-image", toUint8(Property::ShadowImage)}, + {"top-image", toUint8(Property::TopImage)}}); + +StyleProperty getLayerProperty(const LocationIndicatorLayer& layer, Property property) { + switch (property) { + case Property::AccuracyRadius: + return makeStyleProperty(layer.getAccuracyRadius()); + case Property::AccuracyRadiusBorderColor: + return makeStyleProperty(layer.getAccuracyRadiusBorderColor()); + case Property::AccuracyRadiusColor: + return makeStyleProperty(layer.getAccuracyRadiusColor()); + case Property::BearingImageSize: + return makeStyleProperty(layer.getBearingImageSize()); + case Property::Location: + return makeStyleProperty(layer.getLocation()); + case Property::ShadowImageSize: + return makeStyleProperty(layer.getShadowImageSize()); + case Property::TopImageSize: + return makeStyleProperty(layer.getTopImageSize()); + case Property::AccuracyRadiusTransition: + return makeStyleProperty(layer.getAccuracyRadiusTransition()); + case Property::AccuracyRadiusBorderColorTransition: + return makeStyleProperty(layer.getAccuracyRadiusBorderColorTransition()); + case Property::AccuracyRadiusColorTransition: + return makeStyleProperty(layer.getAccuracyRadiusColorTransition()); + case Property::BearingImageSizeTransition: + return makeStyleProperty(layer.getBearingImageSizeTransition()); + case Property::LocationTransition: + return makeStyleProperty(layer.getLocationTransition()); + case Property::ShadowImageSizeTransition: + return makeStyleProperty(layer.getShadowImageSizeTransition()); + case Property::TopImageSizeTransition: + return makeStyleProperty(layer.getTopImageSizeTransition()); + case Property::Bearing: + return makeStyleProperty(layer.getBearing()); + case Property::BearingImage: + return makeStyleProperty(layer.getBearingImage()); + case Property::ImageTiltDisplacement: + return makeStyleProperty(layer.getImageTiltDisplacement()); + case Property::PerspectiveCompensation: + return makeStyleProperty(layer.getPerspectiveCompensation()); + case Property::ShadowImage: + return makeStyleProperty(layer.getShadowImage()); + case Property::TopImage: + return makeStyleProperty(layer.getTopImage()); + } + return {}; +} + +StyleProperty getLayerProperty(const LocationIndicatorLayer& layer, const std::string& name) { + const auto it = layerProperties.find(name.c_str()); + if (it == layerProperties.end()) { + return {}; + } + return getLayerProperty(layer, static_cast(it->second)); +} + +} // namespace + +Value LocationIndicatorLayer::serialize() const { + auto result = Layer::serialize(); + assert(result.getObject()); + for (const auto& property : layerProperties) { + auto styleProperty = getLayerProperty(*this, static_cast(property.second)); + if (styleProperty.getKind() == StyleProperty::Kind::Undefined) continue; + serializeProperty(result, styleProperty, property.first.c_str(), property.second < kPaintPropertyCount); + } + return result; +} + +optional LocationIndicatorLayer::setPropertyInternal(const std::string& name, const Convertible& value) { + const auto it = layerProperties.find(name.c_str()); + if (it == layerProperties.end()) return Error{"layer doesn't support this property"}; + + auto property = static_cast(it->second); + + if (property == Property::AccuracyRadius || property == Property::BearingImageSize || + property == Property::ShadowImageSize || property == Property::TopImageSize || property == Property::Bearing || + property == Property::ImageTiltDisplacement || property == Property::PerspectiveCompensation) { + Error error; + const auto& typedValue = convert>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::AccuracyRadius) { + setAccuracyRadius(*typedValue); + return nullopt; + } + + if (property == Property::BearingImageSize) { + setBearingImageSize(*typedValue); + return nullopt; + } + + if (property == Property::ShadowImageSize) { + setShadowImageSize(*typedValue); + return nullopt; + } + + if (property == Property::TopImageSize) { + setTopImageSize(*typedValue); + return nullopt; + } + + if (property == Property::Bearing) { + setBearing(*typedValue); + return nullopt; + } + + if (property == Property::ImageTiltDisplacement) { + setImageTiltDisplacement(*typedValue); + return nullopt; + } + + if (property == Property::PerspectiveCompensation) { + setPerspectiveCompensation(*typedValue); + return nullopt; + } + } + if (property == Property::AccuracyRadiusBorderColor || property == Property::AccuracyRadiusColor) { + Error error; + const auto& typedValue = convert>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::AccuracyRadiusBorderColor) { + setAccuracyRadiusBorderColor(*typedValue); + return nullopt; + } + + if (property == Property::AccuracyRadiusColor) { + setAccuracyRadiusColor(*typedValue); + return nullopt; + } + } + if (property == Property::Location) { + Error error; + const auto& typedValue = convert>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setLocation(*typedValue); + return nullopt; + } + if (property == Property::BearingImage || property == Property::ShadowImage || property == Property::TopImage) { + Error error; + const auto& typedValue = convert>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::BearingImage) { + setBearingImage(*typedValue); + return nullopt; + } + + if (property == Property::ShadowImage) { + setShadowImage(*typedValue); + return nullopt; + } + + if (property == Property::TopImage) { + setTopImage(*typedValue); + return nullopt; + } + } + + Error error; + optional transition = convert(value, error); + if (!transition) { + return error; + } + + if (property == Property::AccuracyRadiusTransition) { + setAccuracyRadiusTransition(*transition); + return nullopt; + } + + if (property == Property::AccuracyRadiusBorderColorTransition) { + setAccuracyRadiusBorderColorTransition(*transition); + return nullopt; + } + + if (property == Property::AccuracyRadiusColorTransition) { + setAccuracyRadiusColorTransition(*transition); + return nullopt; + } + + if (property == Property::BearingImageSizeTransition) { + setBearingImageSizeTransition(*transition); + return nullopt; + } + + if (property == Property::LocationTransition) { + setLocationTransition(*transition); + return nullopt; + } + + if (property == Property::ShadowImageSizeTransition) { + setShadowImageSizeTransition(*transition); + return nullopt; + } + + if (property == Property::TopImageSizeTransition) { + setTopImageSizeTransition(*transition); + return nullopt; + } + + return Error{"layer doesn't support this property"}; +} + +StyleProperty LocationIndicatorLayer::getProperty(const std::string& name) const { + return getLayerProperty(*this, name); +} + +Mutable LocationIndicatorLayer::mutableBaseImpl() const { + return staticMutableCast(mutableImpl()); +} + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/src/mbgl/style/layers/location_indicator_layer_impl.cpp b/src/mbgl/style/layers/location_indicator_layer_impl.cpp new file mode 100644 index 000000000..90a37f4b4 --- /dev/null +++ b/src/mbgl/style/layers/location_indicator_layer_impl.cpp @@ -0,0 +1,12 @@ +#include +#include + +namespace mbgl { +namespace style { + +bool LocationIndicatorLayer::Impl::hasLayoutDifference(const Layer::Impl&) const { + return false; +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/layers/location_indicator_layer_impl.hpp b/src/mbgl/style/layers/location_indicator_layer_impl.hpp new file mode 100644 index 000000000..954484ecb --- /dev/null +++ b/src/mbgl/style/layers/location_indicator_layer_impl.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +class TransformState; + +namespace style { + +class LocationIndicatorLayer::Impl : public Layer::Impl { +public: + using Layer::Impl::Impl; + + bool hasLayoutDifference(const Layer::Impl &) const override; + void stringifyLayout(rapidjson::Writer &) const override; + + LocationIndicatorLayoutProperties::Unevaluated layout; + LocationIndicatorPaintProperties::Transitionable paint; + DECLARE_LAYER_TYPE_INFO; +}; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/layers/location_indicator_layer_properties.cpp b/src/mbgl/style/layers/location_indicator_layer_properties.cpp new file mode 100644 index 000000000..818e45b5a --- /dev/null +++ b/src/mbgl/style/layers/location_indicator_layer_properties.cpp @@ -0,0 +1,35 @@ +// clang-format off + +// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`. + +#include + +#include + +namespace mbgl { +namespace style { + +LocationIndicatorLayerProperties::LocationIndicatorLayerProperties( + Immutable impl_) + : LayerProperties(std::move(impl_)) {} + +LocationIndicatorLayerProperties::LocationIndicatorLayerProperties( + Immutable impl_, + LocationIndicatorPaintProperties::PossiblyEvaluated evaluated_) + : LayerProperties(std::move(impl_)), + evaluated(std::move(evaluated_)) {} + +LocationIndicatorLayerProperties::~LocationIndicatorLayerProperties() = default; + +unsigned long LocationIndicatorLayerProperties::constantsMask() const { + return evaluated.constantsMask(); +} + +const LocationIndicatorLayer::Impl& LocationIndicatorLayerProperties::layerImpl() const { + return static_cast(*baseImpl); +} + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/src/mbgl/style/layers/location_indicator_layer_properties.hpp b/src/mbgl/style/layers/location_indicator_layer_properties.hpp new file mode 100644 index 000000000..d1f0879f7 --- /dev/null +++ b/src/mbgl/style/layers/location_indicator_layer_properties.hpp @@ -0,0 +1,114 @@ +// clang-format off + +// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +struct Bearing : LayoutProperty { + static constexpr const char *name() { return "bearing"; } + static float defaultValue() { return 0; } +}; + +struct BearingImage : LayoutProperty { + static constexpr const char *name() { return "bearing-image"; } + static expression::Image defaultValue() { return {}; } +}; + +struct ImageTiltDisplacement : LayoutProperty { + static constexpr const char *name() { return "image-tilt-displacement"; } + static float defaultValue() { return 0; } +}; + +struct PerspectiveCompensation : LayoutProperty { + static constexpr const char *name() { return "perspective-compensation"; } + static float defaultValue() { return 0.85; } +}; + +struct ShadowImage : LayoutProperty { + static constexpr const char *name() { return "shadow-image"; } + static expression::Image defaultValue() { return {}; } +}; + +struct TopImage : LayoutProperty { + static constexpr const char *name() { return "top-image"; } + static expression::Image defaultValue() { return {}; } +}; + +struct AccuracyRadius : PaintProperty { + static float defaultValue() { return 0; } +}; + +struct AccuracyRadiusBorderColor : PaintProperty { + static Color defaultValue() { return Color::white(); } +}; + +struct AccuracyRadiusColor : PaintProperty { + static Color defaultValue() { return Color::white(); } +}; + +struct BearingImageSize : PaintProperty { + static float defaultValue() { return 0; } +}; + +struct Location : PaintProperty> { + static std::array defaultValue() { return {{0, 0, 0}}; } +}; + +struct ShadowImageSize : PaintProperty { + static float defaultValue() { return 0; } +}; + +struct TopImageSize : PaintProperty { + static float defaultValue() { return 0; } +}; + +class LocationIndicatorLayoutProperties : public Properties< + Bearing, + BearingImage, + ImageTiltDisplacement, + PerspectiveCompensation, + ShadowImage, + TopImage +> {}; + +class LocationIndicatorPaintProperties : public Properties< + AccuracyRadius, + AccuracyRadiusBorderColor, + AccuracyRadiusColor, + BearingImageSize, + Location, + ShadowImageSize, + TopImageSize +> {}; + +class LocationIndicatorLayerProperties final : public LayerProperties { +public: + explicit LocationIndicatorLayerProperties(Immutable); + LocationIndicatorLayerProperties( + Immutable, + LocationIndicatorPaintProperties::PossiblyEvaluated); + ~LocationIndicatorLayerProperties() override; + + unsigned long constantsMask() const override; + + const LocationIndicatorLayer::Impl& layerImpl() const; + // Data members. + LocationIndicatorPaintProperties::PossiblyEvaluated evaluated; +}; + +} // namespace style +} // namespace mbgl + +// clang-format on