diff options
111 files changed, 4853 insertions, 233 deletions
diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 602cf90dad..d281872102 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -139,6 +139,10 @@ set(MBGL_CORE_FILES src/mbgl/programs/collision_box_program.cpp src/mbgl/programs/collision_box_program.hpp src/mbgl/programs/debug_program.hpp + src/mbgl/programs/extrusion_texture_program.cpp + src/mbgl/programs/extrusion_texture_program.hpp + src/mbgl/programs/fill_extrusion_program.cpp + src/mbgl/programs/fill_extrusion_program.hpp src/mbgl/programs/fill_program.cpp src/mbgl/programs/fill_program.hpp src/mbgl/programs/line_program.cpp @@ -162,6 +166,8 @@ set(MBGL_CORE_FILES src/mbgl/renderer/debug_bucket.hpp src/mbgl/renderer/fill_bucket.cpp src/mbgl/renderer/fill_bucket.hpp + src/mbgl/renderer/fill_extrusion_bucket.cpp + src/mbgl/renderer/fill_extrusion_bucket.hpp src/mbgl/renderer/frame_history.cpp src/mbgl/renderer/frame_history.hpp src/mbgl/renderer/group_by_layout.cpp @@ -176,6 +182,7 @@ set(MBGL_CORE_FILES src/mbgl/renderer/painter_clipping.cpp src/mbgl/renderer/painter_debug.cpp src/mbgl/renderer/painter_fill.cpp + src/mbgl/renderer/painter_fill_extrusion.cpp src/mbgl/renderer/painter_line.cpp src/mbgl/renderer/painter_raster.cpp src/mbgl/renderer/painter_symbol.cpp @@ -213,8 +220,14 @@ set(MBGL_CORE_FILES src/mbgl/shaders/collision_box.hpp src/mbgl/shaders/debug.cpp src/mbgl/shaders/debug.hpp + src/mbgl/shaders/extrusion_texture.cpp + src/mbgl/shaders/extrusion_texture.hpp src/mbgl/shaders/fill.cpp src/mbgl/shaders/fill.hpp + src/mbgl/shaders/fill_extrusion.cpp + src/mbgl/shaders/fill_extrusion.hpp + src/mbgl/shaders/fill_extrusion_pattern.cpp + src/mbgl/shaders/fill_extrusion_pattern.hpp src/mbgl/shaders/fill_outline.cpp src/mbgl/shaders/fill_outline.hpp src/mbgl/shaders/fill_outline_pattern.cpp @@ -270,6 +283,8 @@ set(MBGL_CORE_FILES include/mbgl/style/image.hpp include/mbgl/style/layer.hpp include/mbgl/style/layer_type.hpp + include/mbgl/style/light.hpp + include/mbgl/style/position.hpp include/mbgl/style/property_value.hpp include/mbgl/style/query.hpp include/mbgl/style/source.hpp @@ -288,6 +303,7 @@ set(MBGL_CORE_FILES src/mbgl/style/layer_impl.hpp src/mbgl/style/layer_observer.hpp src/mbgl/style/layout_property.hpp + src/mbgl/style/light_impl.hpp src/mbgl/style/observer.hpp src/mbgl/style/paint_property.hpp src/mbgl/style/paint_property_binder.hpp @@ -304,10 +320,9 @@ set(MBGL_CORE_FILES src/mbgl/style/source_observer.hpp src/mbgl/style/style.cpp src/mbgl/style/style.hpp - src/mbgl/style/style.hpp - src/mbgl/style/style.hpp src/mbgl/style/tile_source_impl.cpp src/mbgl/style/tile_source_impl.hpp + src/mbgl/style/transitioning_property.hpp src/mbgl/style/types.cpp src/mbgl/style/update_batch.hpp src/mbgl/style/update_parameters.hpp @@ -320,7 +335,9 @@ set(MBGL_CORE_FILES include/mbgl/style/conversion/geojson.hpp include/mbgl/style/conversion/geojson_options.hpp include/mbgl/style/conversion/layer.hpp + include/mbgl/style/conversion/light.hpp include/mbgl/style/conversion/make_property_setters.hpp + include/mbgl/style/conversion/position.hpp include/mbgl/style/conversion/property_setter.hpp include/mbgl/style/conversion/property_value.hpp include/mbgl/style/conversion/source.hpp @@ -474,6 +491,7 @@ set(MBGL_CORE_FILES include/mbgl/util/geometry.hpp include/mbgl/util/ignore.hpp include/mbgl/util/image.hpp + include/mbgl/util/indexed_tuple.hpp include/mbgl/util/interpolate.hpp include/mbgl/util/logging.hpp include/mbgl/util/noncopyable.hpp @@ -487,6 +505,7 @@ set(MBGL_CORE_FILES include/mbgl/util/tileset.hpp include/mbgl/util/timer.hpp include/mbgl/util/traits.hpp + include/mbgl/util/type_list.hpp include/mbgl/util/unitbezier.hpp include/mbgl/util/util.hpp include/mbgl/util/variant.hpp @@ -514,7 +533,6 @@ set(MBGL_CORE_FILES src/mbgl/util/http_timeout.hpp src/mbgl/util/i18n.cpp src/mbgl/util/i18n.hpp - src/mbgl/util/indexed_tuple.hpp src/mbgl/util/interpolate.cpp src/mbgl/util/intersection_tests.cpp src/mbgl/util/intersection_tests.hpp @@ -548,7 +566,6 @@ set(MBGL_CORE_FILES src/mbgl/util/tile_cover.cpp src/mbgl/util/tile_cover.hpp src/mbgl/util/token.hpp - src/mbgl/util/type_list.hpp src/mbgl/util/url.cpp src/mbgl/util/url.hpp src/mbgl/util/utf.hpp diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake index 9012ae9616..51dedb0052 100644 --- a/cmake/test-files.cmake +++ b/cmake/test-files.cmake @@ -82,6 +82,7 @@ set(MBGL_TEST_FILES test/style/conversion/function.test.cpp test/style/conversion/geojson_options.test.cpp test/style/conversion/layer.test.cpp + test/style/conversion/light.test.cpp test/style/conversion/stringify.test.cpp # style @@ -125,6 +126,7 @@ set(MBGL_TEST_FILES test/util/merge_lines.test.cpp test/util/number_conversions.test.cpp test/util/offscreen_texture.test.cpp + test/util/position.test.cpp test/util/projection.test.cpp test/util/run_loop.test.cpp test/util/text_conversions.test.cpp diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 0e3cee4e70..84ea3104d8 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -30,6 +30,7 @@ namespace style { class Image; class Source; class Layer; +class Light; } // namespace style class Map : private util::noncopyable { @@ -179,6 +180,10 @@ public: void removeImage(const std::string&); const style::Image* getImage(const std::string&); + // Light + void setLight(std::unique_ptr<style::Light>); + style::Light* getLight(); + // Defaults std::string getStyleName() const; LatLng getDefaultLatLng() const; diff --git a/include/mbgl/style/conversion/constant.hpp b/include/mbgl/style/conversion/constant.hpp index 1e1fdc2ee8..07c0a35fae 100644 --- a/include/mbgl/style/conversion/constant.hpp +++ b/include/mbgl/style/conversion/constant.hpp @@ -4,6 +4,7 @@ #include <mbgl/util/optional.hpp> #include <mbgl/util/color.hpp> #include <mbgl/util/enum.hpp> +#include <mbgl/util/string.hpp> #include <array> #include <string> @@ -92,45 +93,25 @@ struct Converter<Color> { } }; -template <> -struct Converter<std::array<float, 2>> { +template <size_t N> +struct Converter<std::array<float, N>> { template <class V> - optional<std::array<float, 2>> operator()(const V& value, Error& error) const { - if (!isArray(value) || arrayLength(value) != 2) { - error = { "value must be an array of two numbers" }; + optional<std::array<float, N>> operator()(const V& value, Error& error) const { + if (!isArray(value) || arrayLength(value) != N) { + error = { "value must be an array of " + util::toString(N) + " numbers" }; return {}; } - optional<float> first = toNumber(arrayMember(value, 0)); - optional<float> second = toNumber(arrayMember(value, 1)); - if (!first || !second) { - error = { "value must be an array of two numbers" }; - return {}; - } - - return std::array<float, 2> {{ *first, *second }}; - } -}; - -template <> -struct Converter<std::array<float, 4>> { - template <class V> - optional<std::array<float, 4>> operator()(const V& value, Error& error) const { - if (!isArray(value) || arrayLength(value) != 4) { - error = { "value must be an array of four numbers" }; - return {}; - } - - optional<float> first = toNumber(arrayMember(value, 0)); - optional<float> second = toNumber(arrayMember(value, 1)); - optional<float> third = toNumber(arrayMember(value, 2)); - optional<float> fourth = toNumber(arrayMember(value, 3)); - if (!first || !second) { - error = { "value must be an array of four numbers" }; - return {}; + std::array<float, N> result; + for (size_t i = 0; i < N; i++) { + optional<float> n = toNumber(arrayMember(value, i)); + if (!n) { + error = { "value must be an array of " + util::toString(N) + " numbers" }; + return {}; + } + result[i] = *n; } - - return std::array<float, 4> {{ *first, *second, *third, *fourth }}; + return result; } }; diff --git a/include/mbgl/style/conversion/layer.hpp b/include/mbgl/style/conversion/layer.hpp index efb1df8fef..3a64c36bf5 100644 --- a/include/mbgl/style/conversion/layer.hpp +++ b/include/mbgl/style/conversion/layer.hpp @@ -4,6 +4,7 @@ #include <mbgl/style/layers/background_layer.hpp> #include <mbgl/style/layers/circle_layer.hpp> #include <mbgl/style/layers/fill_layer.hpp> +#include <mbgl/style/layers/fill_extrusion_layer.hpp> #include <mbgl/style/layers/line_layer.hpp> #include <mbgl/style/layers/raster_layer.hpp> #include <mbgl/style/layers/symbol_layer.hpp> @@ -92,6 +93,8 @@ public: if (*type == "fill") { converted = convertVectorLayer<FillLayer>(*id, value, error); + } else if (*type == "fill-extrusion") { + converted = convertVectorLayer<FillExtrusionLayer>(*id, value, error); } else if (*type == "line") { converted = convertVectorLayer<LineLayer>(*id, value, error); } else if (*type == "circle") { diff --git a/include/mbgl/style/conversion/light.hpp b/include/mbgl/style/conversion/light.hpp new file mode 100644 index 0000000000..631ed04ccb --- /dev/null +++ b/include/mbgl/style/conversion/light.hpp @@ -0,0 +1,122 @@ +#pragma once + +#include <mbgl/style/light.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion/position.hpp> +#include <mbgl/style/conversion/property_value.hpp> +#include <mbgl/style/conversion/transition_options.hpp> + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter<Light> { +public: + template <class V> + optional<Light> operator()(const V& value, Error& error) const { + if (!isObject(value)) { + error = { "light must be an object" }; + return {}; + } + + Light light; + + const auto anchor = objectMember(value, "anchor"); + if (anchor) { + optional<PropertyValue<LightAnchorType>> convertedAnchor = + convert<PropertyValue<LightAnchorType>>(*anchor, error); + + if (convertedAnchor) { + light.get<LightAnchor>().value = *convertedAnchor; + } else { + return {}; + } + } + + const auto anchorTransition = objectMember(value, "anchor-transition"); + if (anchorTransition) { + optional<TransitionOptions> transition = + convert<TransitionOptions>(*anchorTransition, error); + if (transition) { + light.get<LightAnchor>().transition = *transition; + } else { + return {}; + } + } + + const auto color = objectMember(value, "color"); + if (color) { + optional<PropertyValue<Color>> convertedColor = + convert<PropertyValue<Color>>(*color, error); + + if (convertedColor) { + light.get<LightColor>().value = *convertedColor; + } else { + return {}; + } + } + + const auto colorTransition = objectMember(value, "color-transition"); + if (colorTransition) { + optional<TransitionOptions> transition = + convert<TransitionOptions>(*colorTransition, error); + if (transition) { + light.get<LightColor>().transition = *transition; + } else { + return {}; + } + } + + const auto position = objectMember(value, "position"); + if (position) { + optional<PropertyValue<Position>> convertedPosition = + convert<PropertyValue<Position>>(*position, error); + + if (convertedPosition) { + light.get<LightPosition>().value = *convertedPosition; + } else { + return {}; + } + } + + const auto positionTransition = objectMember(value, "position-transition"); + if (positionTransition) { + optional<TransitionOptions> transition = + convert<TransitionOptions>(*positionTransition, error); + if (transition) { + light.get<LightPosition>().transition = *transition; + } else { + return {}; + } + } + + const auto intensity = objectMember(value, "intensity"); + if (intensity) { + optional<PropertyValue<float>> convertedIntensity = + convert<PropertyValue<float>>(*intensity, error); + + if (convertedIntensity) { + light.get<LightIntensity>().value = *convertedIntensity; + } else { + return {}; + } + } + + const auto intensityTransition = objectMember(value, "intensity-transition"); + if (intensityTransition) { + optional<TransitionOptions> transition = + convert<TransitionOptions>(*intensityTransition, error); + if (transition) { + light.get<LightIntensity>().transition = *transition; + } else { + return {}; + } + } + return { light }; + }; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/conversion/position.hpp b/include/mbgl/style/conversion/position.hpp new file mode 100644 index 0000000000..7036b03822 --- /dev/null +++ b/include/mbgl/style/conversion/position.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/position.hpp> +#include <mbgl/util/optional.hpp> + +#include <array> + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter<Position> { + template <class V> + optional<Position> operator()(const V& value, Error& error) const { + optional<std::array<float, 3>> spherical = convert<std::array<float, 3>>(value, error); + + if (!spherical) { + return {}; + } + + return Position(*spherical); + } +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/layer.hpp b/include/mbgl/style/layer.hpp index f09eb0165a..56f2c48fa7 100644 --- a/include/mbgl/style/layer.hpp +++ b/include/mbgl/style/layer.hpp @@ -66,7 +66,7 @@ public: // Convenience method for dynamic dispatch on the concrete layer type. Using // method overloading, this allows consolidation of logic common to vector-based - // layers (Fill, Line, Circle, or Symbol). For example: + // layers (Fill, FillExtrusion, Line, Circle, or Symbol). For example: // // struct Visitor { // void operator()(CustomLayer&) { ... } diff --git a/include/mbgl/style/light.hpp b/include/mbgl/style/light.hpp new file mode 100644 index 0000000000..bec8e6ddeb --- /dev/null +++ b/include/mbgl/style/light.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include <mbgl/style/property_value.hpp> +#include <mbgl/style/transition_options.hpp> +#include <mbgl/style/types.hpp> +#include <mbgl/style/position.hpp> +#include <mbgl/util/color.hpp> +#include <mbgl/util/indexed_tuple.hpp> + +namespace mbgl { +namespace style { + +template <class T> +class LightProperty { +public: + using Type = T; + using ValueType = PropertyValue<T>; + + PropertyValue<T> value; + TransitionOptions transition; +}; + +struct LightAnchor : LightProperty<LightAnchorType> { + static LightAnchorType defaultValue() { + return LightAnchorType::Viewport; + } +}; + +struct LightPosition : LightProperty<Position> { + static Position defaultValue() { + std::array<float, 3> default_ = { { 1.15, 210, 30 } }; + return Position{ { default_ } }; + } +}; + +struct LightColor : LightProperty<Color> { + static Color defaultValue() { + return Color::white(); + } +}; + +struct LightIntensity : LightProperty<float> { + static float defaultValue() { + return 0.5; + } +}; + +using LightProperties = TypeList<LightAnchor, LightPosition, LightColor, LightIntensity>; +class Light : public IndexedTuple<LightProperties, LightProperties> {}; + +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/position.hpp b/include/mbgl/style/position.hpp new file mode 100644 index 0000000000..3be8d1c55e --- /dev/null +++ b/include/mbgl/style/position.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include <mbgl/util/constants.hpp> + +#include <array> + +namespace mbgl { +namespace style { +class Position { +public: + Position() = default; + Position(std::array<float, 3>& position_) + : radial(position_[0]), azimuthal(position_[1]), polar(position_[2]) { + calculateCartesian(); + }; + + friend bool operator==(const Position& lhs, const Position& rhs) { + return lhs.radial == rhs.radial && lhs.azimuthal == rhs.azimuthal && lhs.polar == rhs.polar; + // TODO this doesn't address wrapping, which would be better addressed by comparing cartesian coordinates but being calculated floats are ont to be trusted. + } + + friend bool operator!=(const Position& lhs, const Position& rhs) { + return !(lhs == rhs); + } + + const std::array<float, 3> getCartesian() const { + return { { x, y, z } }; + }; + + const std::array<float, 3> getSpherical() const { + return { { radial, azimuthal, polar } }; + }; + + void set(std::array<float, 3>& position_) { + radial = position_[0]; + azimuthal = position_[1]; + polar = position_[2]; + calculateCartesian(); + }; + + // Utility function to be used only during interpolation; this leaves spherical coordinates undefined. + void setCartesian(std::array<float, 3>& position_) { + x = position_[0]; + y = position_[1]; + z = position_[2]; + } + +private: + float radial; + float azimuthal; + float polar; + float x; + float y; + float z; + + void calculateCartesian() { + // We abstract "north"/"up" (compass-wise) to be 0° when really this is 90° (π/2): we + // correct for that here + const float _a = (azimuthal + 90) * util::DEG2RAD; + const float _p = polar * util::DEG2RAD; + + x = radial * std::cos(_a) * std::sin(_p); + y = radial * std::sin(_a) * std::sin(_p); + z = radial * std::cos(_p); + }; +}; +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp index 1f2f5c3105..e0436efb67 100644 --- a/include/mbgl/style/types.hpp +++ b/include/mbgl/style/types.hpp @@ -92,5 +92,10 @@ enum class IconTextFitType : uint8_t { Height }; +enum class LightAnchorType: bool { + Map, + Viewport +}; + } // namespace style } // namespace mbgl diff --git a/src/mbgl/util/indexed_tuple.hpp b/include/mbgl/util/indexed_tuple.hpp index a414639530..a414639530 100644 --- a/src/mbgl/util/indexed_tuple.hpp +++ b/include/mbgl/util/indexed_tuple.hpp diff --git a/include/mbgl/util/interpolate.hpp b/include/mbgl/util/interpolate.hpp index d463ffc056..a2103f18b2 100644 --- a/include/mbgl/util/interpolate.hpp +++ b/include/mbgl/util/interpolate.hpp @@ -2,6 +2,7 @@ #include <mbgl/util/color.hpp> #include <mbgl/util/range.hpp> +#include <mbgl/style/position.hpp> #include <array> #include <vector> @@ -47,6 +48,17 @@ public: }; template <> +struct Interpolator<style::Position> { +public: + style::Position operator()(const style::Position& a, const style::Position& b, const double t) { + auto pos = style::Position(); + auto interpolated = interpolate(a.getCartesian(), b.getCartesian(), t); + pos.setCartesian(interpolated); + return { pos }; + } +}; + +template <> struct Interpolator<Color> { public: Color operator()(const Color& a, const Color& b, const double t) { diff --git a/src/mbgl/util/type_list.hpp b/include/mbgl/util/type_list.hpp index 4a5e95c8a4..4a5e95c8a4 100644 --- a/src/mbgl/util/type_list.hpp +++ b/include/mbgl/util/type_list.hpp diff --git a/mapbox-gl-js b/mapbox-gl-js -Subproject d7945c30a07b5e733ded00036136fd7bf543497 +Subproject d3b39cbba4f09d26ef51159aa56270e67006c51 diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java new file mode 100644 index 0000000000..6772da73b1 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java @@ -0,0 +1,339 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +package com.mapbox.mapboxsdk.style.layers; + +import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; +import android.support.annotation.UiThread; + +import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor; + +import com.mapbox.mapboxsdk.style.layers.TransitionOptions; + +/** + * An extruded (3D) polygon. + * + * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layers-fill-extrusion">The online documentation</a> + */ +@UiThread +public class FillExtrusionLayer extends Layer { + + /** + * Creates a FillExtrusionLayer. + * + * @param nativePtr pointer used by core + */ + public FillExtrusionLayer(long nativePtr) { + super(nativePtr); + } + + /** + * Creates a FillExtrusionLayer. + * + * @param layerId the id of the layer + * @param sourceId the id of the source + */ + public FillExtrusionLayer(String layerId, String sourceId) { + initialize(layerId, sourceId); + } + + protected native void initialize(String layerId, String sourceId); + + /** + * Set the source layer. + * + * @param sourceLayer the source layer to set + */ + public void setSourceLayer(String sourceLayer) { + nativeSetSourceLayer(sourceLayer); + } + + /** + * Set the source Layer. + * + * @param sourceLayer the source layer to set + * @return This + */ + public FillExtrusionLayer withSourceLayer(String sourceLayer) { + setSourceLayer(sourceLayer); + return this; + } + + /** + * Get the source layer. + * + * @return sourceLayer the source layer to get + */ + public String getSourceLayer() { + return nativeGetSourceLayer(); + } + + /** + * Set a single filter. + * + * @param filter the filter to set + */ + public void setFilter(Filter.Statement filter) { + nativeSetFilter(filter.toArray()); + } + + /** + * Set a single filter. + * + * @param filter the filter to set + * @return This + */ + public FillExtrusionLayer withFilter(Filter.Statement filter) { + setFilter(filter); + return this; + } + + /** + * Set a property or properties. + * + * @param properties the var-args properties + * @return This + */ + public FillExtrusionLayer withProperties(@NonNull PropertyValue<?>... properties) { + setProperties(properties); + return this; + } + + // Property getters + + /** + * Get the FillExtrusionOpacity property + * + * @return property wrapper value around Float + */ + @SuppressWarnings("unchecked") + public PropertyValue<Float> getFillExtrusionOpacity() { + return (PropertyValue<Float>) new PropertyValue("fill-extrusion-opacity", nativeGetFillExtrusionOpacity()); + } + + /** + * Get the FillExtrusionOpacity property transition options + * + * @return transition options for Float + */ + public TransitionOptions getFillExtrusionOpacityTransition() { + return nativeGetFillExtrusionOpacityTransition(); + } + + /** + * Set the FillExtrusionOpacity property transition options + * + * @param options transition options for Float + */ + public void setFillExtrusionOpacityTransition(TransitionOptions options) { + nativeSetFillExtrusionOpacityTransition(options.getDuration(), options.getDelay()); + } + + /** + * Get the FillExtrusionColor property + * + * @return property wrapper value around String + */ + @SuppressWarnings("unchecked") + public PropertyValue<String> getFillExtrusionColor() { + return (PropertyValue<String>) new PropertyValue("fill-extrusion-color", nativeGetFillExtrusionColor()); + } + + /** + * The base color of the extruded fill. The extrusion's surfaces will be shaded differently based on this color in combination with the root `light` settings. If this color is specified as `rgba` with an alpha component, the alpha component will be ignored; use `fill-extrusion-opacity` to set layer opacity. + * + * @return int representation of a rgba string color + * @throws RuntimeException thrown if property isn't a value + */ + @ColorInt + public int getFillExtrusionColorAsInt() { + PropertyValue<String> value = getFillExtrusionColor(); + if (value.isValue()) { + return rgbaToColor(value.getValue()); + } else { + throw new RuntimeException("fill-extrusion-color was set as a Function"); + } + } + + /** + * Get the FillExtrusionColor property transition options + * + * @return transition options for String + */ + public TransitionOptions getFillExtrusionColorTransition() { + return nativeGetFillExtrusionColorTransition(); + } + + /** + * Set the FillExtrusionColor property transition options + * + * @param options transition options for String + */ + public void setFillExtrusionColorTransition(TransitionOptions options) { + nativeSetFillExtrusionColorTransition(options.getDuration(), options.getDelay()); + } + + /** + * Get the FillExtrusionTranslate property + * + * @return property wrapper value around Float[] + */ + @SuppressWarnings("unchecked") + public PropertyValue<Float[]> getFillExtrusionTranslate() { + return (PropertyValue<Float[]>) new PropertyValue("fill-extrusion-translate", nativeGetFillExtrusionTranslate()); + } + + /** + * Get the FillExtrusionTranslate property transition options + * + * @return transition options for Float[] + */ + public TransitionOptions getFillExtrusionTranslateTransition() { + return nativeGetFillExtrusionTranslateTransition(); + } + + /** + * Set the FillExtrusionTranslate property transition options + * + * @param options transition options for Float[] + */ + public void setFillExtrusionTranslateTransition(TransitionOptions options) { + nativeSetFillExtrusionTranslateTransition(options.getDuration(), options.getDelay()); + } + + /** + * Get the FillExtrusionTranslateAnchor property + * + * @return property wrapper value around String + */ + @SuppressWarnings("unchecked") + public PropertyValue<String> getFillExtrusionTranslateAnchor() { + return (PropertyValue<String>) new PropertyValue("fill-extrusion-translate-anchor", nativeGetFillExtrusionTranslateAnchor()); + } + + /** + * Get the FillExtrusionPattern property + * + * @return property wrapper value around String + */ + @SuppressWarnings("unchecked") + public PropertyValue<String> getFillExtrusionPattern() { + return (PropertyValue<String>) new PropertyValue("fill-extrusion-pattern", nativeGetFillExtrusionPattern()); + } + + /** + * Get the FillExtrusionPattern property transition options + * + * @return transition options for String + */ + public TransitionOptions getFillExtrusionPatternTransition() { + return nativeGetFillExtrusionPatternTransition(); + } + + /** + * Set the FillExtrusionPattern property transition options + * + * @param options transition options for String + */ + public void setFillExtrusionPatternTransition(TransitionOptions options) { + nativeSetFillExtrusionPatternTransition(options.getDuration(), options.getDelay()); + } + + /** + * Get the FillExtrusionHeight property + * + * @return property wrapper value around Float + */ + @SuppressWarnings("unchecked") + public PropertyValue<Float> getFillExtrusionHeight() { + return (PropertyValue<Float>) new PropertyValue("fill-extrusion-height", nativeGetFillExtrusionHeight()); + } + + /** + * Get the FillExtrusionHeight property transition options + * + * @return transition options for Float + */ + public TransitionOptions getFillExtrusionHeightTransition() { + return nativeGetFillExtrusionHeightTransition(); + } + + /** + * Set the FillExtrusionHeight property transition options + * + * @param options transition options for Float + */ + public void setFillExtrusionHeightTransition(TransitionOptions options) { + nativeSetFillExtrusionHeightTransition(options.getDuration(), options.getDelay()); + } + + /** + * Get the FillExtrusionBase property + * + * @return property wrapper value around Float + */ + @SuppressWarnings("unchecked") + public PropertyValue<Float> getFillExtrusionBase() { + return (PropertyValue<Float>) new PropertyValue("fill-extrusion-base", nativeGetFillExtrusionBase()); + } + + /** + * Get the FillExtrusionBase property transition options + * + * @return transition options for Float + */ + public TransitionOptions getFillExtrusionBaseTransition() { + return nativeGetFillExtrusionBaseTransition(); + } + + /** + * Set the FillExtrusionBase property transition options + * + * @param options transition options for Float + */ + public void setFillExtrusionBaseTransition(TransitionOptions options) { + nativeSetFillExtrusionBaseTransition(options.getDuration(), options.getDelay()); + } + + private native Object nativeGetFillExtrusionOpacity(); + + private native TransitionOptions nativeGetFillExtrusionOpacityTransition(); + + private native void nativeSetFillExtrusionOpacityTransition(long duration, long delay); + + private native Object nativeGetFillExtrusionColor(); + + private native TransitionOptions nativeGetFillExtrusionColorTransition(); + + private native void nativeSetFillExtrusionColorTransition(long duration, long delay); + + private native Object nativeGetFillExtrusionTranslate(); + + private native TransitionOptions nativeGetFillExtrusionTranslateTransition(); + + private native void nativeSetFillExtrusionTranslateTransition(long duration, long delay); + + private native Object nativeGetFillExtrusionTranslateAnchor(); + + private native Object nativeGetFillExtrusionPattern(); + + private native TransitionOptions nativeGetFillExtrusionPatternTransition(); + + private native void nativeSetFillExtrusionPatternTransition(long duration, long delay); + + private native Object nativeGetFillExtrusionHeight(); + + private native TransitionOptions nativeGetFillExtrusionHeightTransition(); + + private native void nativeSetFillExtrusionHeightTransition(long duration, long delay); + + private native Object nativeGetFillExtrusionBase(); + + private native TransitionOptions nativeGetFillExtrusionBaseTransition(); + + private native void nativeSetFillExtrusionBaseTransition(long duration, long delay); + + @Override + protected native void finalize() throws Throwable; + +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java index 641ee551a2..48e0ec5de3 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java @@ -446,6 +446,27 @@ public final class Property { @Retention(RetentionPolicy.SOURCE) public @interface CIRCLE_PITCH_SCALE {} + // FILL_EXTRUSION_TRANSLATE_ANCHOR: Controls the translation reference point. + + /** + * The fill extrusion is translated relative to the map. + */ + public static final String FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP = "map"; + /** + * The fill extrusion is translated relative to the viewport. + */ + public static final String FILL_EXTRUSION_TRANSLATE_ANCHOR_VIEWPORT = "viewport"; + + /** + * Controls the translation reference point. + */ + @StringDef({ + FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP, + FILL_EXTRUSION_TRANSLATE_ANCHOR_VIEWPORT, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FILL_EXTRUSION_TRANSLATE_ANCHOR {} + private Property() { } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java index e37245000e..e4ea9676fa 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java @@ -1029,6 +1029,170 @@ public class PropertyFactory { } /** + * The opacity of the entire fill extrusion layer. This is rendered on a per-layer, not per-feature, basis, and data-driven styling is not available. + * + * @param value a Float value + * @return property wrapper around Float + */ + public static PropertyValue<Float> fillExtrusionOpacity(Float value) { + return new PaintPropertyValue<>("fill-extrusion-opacity", value); + } + + + /** + * The opacity of the entire fill extrusion layer. This is rendered on a per-layer, not per-feature, basis, and data-driven styling is not available. + * + * @param <Z> the zoom parameter type + * @param function a wrapper {@link CameraFunction} for Float + * @return property wrapper around a Float function + */ + public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> fillExtrusionOpacity(CameraFunction<Z, Float> function) { + return new PaintPropertyValue<>("fill-extrusion-opacity", function); + } + + /** + * The base color of the extruded fill. The extrusion's surfaces will be shaded differently based on this color in combination with the root `light` settings. If this color is specified as `rgba` with an alpha component, the alpha component will be ignored; use {@link PropertyFactory#fillExtrusionOpacity} to set layer opacity. + * + * @param value a int color value + * @return property wrapper around String color + */ + public static PropertyValue<String> fillExtrusionColor(@ColorInt int value) { + return new PaintPropertyValue<>("fill-extrusion-color", colorToRgbaString(value)); + } + + /** + * The base color of the extruded fill. The extrusion's surfaces will be shaded differently based on this color in combination with the root `light` settings. If this color is specified as `rgba` with an alpha component, the alpha component will be ignored; use {@link PropertyFactory#fillExtrusionOpacity} to set layer opacity. + * + * @param value a String value + * @return property wrapper around String + */ + public static PropertyValue<String> fillExtrusionColor(String value) { + return new PaintPropertyValue<>("fill-extrusion-color", value); + } + + + /** + * The base color of the extruded fill. The extrusion's surfaces will be shaded differently based on this color in combination with the root `light` settings. If this color is specified as `rgba` with an alpha component, the alpha component will be ignored; use {@link PropertyFactory#fillExtrusionOpacity} to set layer opacity. + * + * @param <T> the function input type + * @param function a wrapper function for String + * @return property wrapper around a String function + */ + public static <T> PropertyValue<Function<T, String>> fillExtrusionColor(Function<T, String> function) { + return new PaintPropertyValue<>("fill-extrusion-color", function); + } + + /** + * The geometry's offset. Values are [x, y] where negatives indicate left and up (on the flat plane), respectively. + * + * @param value a Float[] value + * @return property wrapper around Float[] + */ + public static PropertyValue<Float[]> fillExtrusionTranslate(Float[] value) { + return new PaintPropertyValue<>("fill-extrusion-translate", value); + } + + + /** + * The geometry's offset. Values are [x, y] where negatives indicate left and up (on the flat plane), respectively. + * + * @param <Z> the zoom parameter type + * @param function a wrapper {@link CameraFunction} for Float[] + * @return property wrapper around a Float[] function + */ + public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> fillExtrusionTranslate(CameraFunction<Z, Float[]> function) { + return new PaintPropertyValue<>("fill-extrusion-translate", function); + } + + /** + * Controls the translation reference point. + * + * @param value a String value + * @return property wrapper around String + */ + public static PropertyValue<String> fillExtrusionTranslateAnchor(@Property.FILL_EXTRUSION_TRANSLATE_ANCHOR String value) { + return new PaintPropertyValue<>("fill-extrusion-translate-anchor", value); + } + + + /** + * Controls the translation reference point. + * + * @param <Z> the zoom parameter type + * @param function a wrapper {@link CameraFunction} for String + * @return property wrapper around a String function + */ + public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> fillExtrusionTranslateAnchor(CameraFunction<Z, String> function) { + return new PaintPropertyValue<>("fill-extrusion-translate-anchor", function); + } + + /** + * Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * + * @param value a String value + * @return property wrapper around String + */ + public static PropertyValue<String> fillExtrusionPattern(String value) { + return new PaintPropertyValue<>("fill-extrusion-pattern", value); + } + + + /** + * Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * + * @param <Z> the zoom parameter type + * @param function a wrapper {@link CameraFunction} for String + * @return property wrapper around a String function + */ + public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> fillExtrusionPattern(CameraFunction<Z, String> function) { + return new PaintPropertyValue<>("fill-extrusion-pattern", function); + } + + /** + * The height with which to extrude this layer. + * + * @param value a Float value + * @return property wrapper around Float + */ + public static PropertyValue<Float> fillExtrusionHeight(Float value) { + return new PaintPropertyValue<>("fill-extrusion-height", value); + } + + + /** + * The height with which to extrude this layer. + * + * @param <T> the function input type + * @param function a wrapper function for Float + * @return property wrapper around a Float function + */ + public static <T> PropertyValue<Function<T, Float>> fillExtrusionHeight(Function<T, Float> function) { + return new PaintPropertyValue<>("fill-extrusion-height", function); + } + + /** + * The height with which to extrude the base of this layer. Must be less than or equal to {@link PropertyFactory#fillExtrusionHeight}. + * + * @param value a Float value + * @return property wrapper around Float + */ + public static PropertyValue<Float> fillExtrusionBase(Float value) { + return new PaintPropertyValue<>("fill-extrusion-base", value); + } + + + /** + * The height with which to extrude the base of this layer. Must be less than or equal to {@link PropertyFactory#fillExtrusionHeight}. + * + * @param <T> the function input type + * @param function a wrapper function for Float + * @return property wrapper around a Float function + */ + public static <T> PropertyValue<Function<T, Float>> fillExtrusionBase(Function<T, Float> function) { + return new PaintPropertyValue<>("fill-extrusion-base", function); + } + + /** * The opacity at which the image will be drawn. * * @param value a Float value diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java index 5bab1de904..851660f06e 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java @@ -3,33 +3,42 @@ package com.mapbox.mapboxsdk.testapp.style; import android.graphics.Color; +import android.support.test.espresso.Espresso; +import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; +import timber.log.Timber; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.functions.CompositeFunction; import com.mapbox.mapboxsdk.style.functions.CameraFunction; +import com.mapbox.mapboxsdk.style.functions.SourceFunction; +import com.mapbox.mapboxsdk.style.functions.stops.CategoricalStops; import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops; +import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops; import com.mapbox.mapboxsdk.style.functions.stops.IntervalStops; +import com.mapbox.mapboxsdk.style.functions.stops.Stop; +import com.mapbox.mapboxsdk.style.functions.stops.Stops; import com.mapbox.mapboxsdk.style.layers.BackgroundLayer; -import com.mapbox.mapboxsdk.style.layers.TransitionOptions; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity; +import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource; import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; -import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import timber.log.Timber; - -import static com.mapbox.mapboxsdk.style.functions.Function.zoom; +import static com.mapbox.mapboxsdk.style.functions.Function.*; import static com.mapbox.mapboxsdk.style.functions.stops.Stop.stop; -import static com.mapbox.mapboxsdk.style.functions.stops.Stops.exponential; -import static com.mapbox.mapboxsdk.style.functions.stops.Stops.interval; -import static com.mapbox.mapboxsdk.style.layers.Property.NONE; -import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.backgroundColor; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.backgroundOpacity; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.backgroundPattern; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static com.mapbox.mapboxsdk.style.functions.stops.Stops.*; +import static org.junit.Assert.*; +import static com.mapbox.mapboxsdk.style.layers.Property.*; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*; + +import com.mapbox.mapboxsdk.style.layers.TransitionOptions; +import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity; /** * Basic smoke tests for BackgroundLayer @@ -44,7 +53,7 @@ public class BackgroundLayerTest extends BaseActivityTest { return EspressoTestActivity.class; } - private void setupLayer() { + private void setupLayer(){ Timber.i("Retrieving layer"); layer = mapboxMap.getLayerAs("background"); } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java new file mode 100644 index 0000000000..fec9a6c119 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java @@ -0,0 +1,760 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +package com.mapbox.mapboxsdk.testapp.style; + +import android.graphics.Color; +import android.support.test.espresso.Espresso; +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; +import timber.log.Timber; + +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.functions.CompositeFunction; +import com.mapbox.mapboxsdk.style.functions.CameraFunction; +import com.mapbox.mapboxsdk.style.functions.SourceFunction; +import com.mapbox.mapboxsdk.style.functions.stops.CategoricalStops; +import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops; +import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops; +import com.mapbox.mapboxsdk.style.functions.stops.IntervalStops; +import com.mapbox.mapboxsdk.style.functions.stops.Stop; +import com.mapbox.mapboxsdk.style.functions.stops.Stops; +import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity; +import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource; +import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static com.mapbox.mapboxsdk.style.functions.Function.*; +import static com.mapbox.mapboxsdk.style.functions.stops.Stop.stop; +import static com.mapbox.mapboxsdk.style.functions.stops.Stops.*; +import static org.junit.Assert.*; +import static com.mapbox.mapboxsdk.style.layers.Property.*; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*; + +import com.mapbox.mapboxsdk.style.layers.TransitionOptions; +import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity; + +/** + * Basic smoke tests for FillExtrusionLayer + */ +@RunWith(AndroidJUnit4.class) +public class FillExtrusionLayerTest extends BaseActivityTest { + + private FillExtrusionLayer layer; + + @Override + protected Class getActivityClass() { + return EspressoTestActivity.class; + } + + private void setupLayer(){ + if ((layer = mapboxMap.getLayerAs("my-layer")) == null) { + Timber.i("Adding layer"); + layer = new FillExtrusionLayer("my-layer", "composite"); + layer.setSourceLayer("composite"); + mapboxMap.addLayer(layer); + // Layer reference is now stale, get new reference + layer = mapboxMap.getLayerAs("my-layer"); + } + } + + @Test + public void testSetVisibility() { + validateTestSetup(); + setupLayer(); + Timber.i("Visibility"); + assertNotNull(layer); + + // Get initial + assertEquals(layer.getVisibility().getValue(), VISIBLE); + + // Set + layer.setProperties(visibility(NONE)); + assertEquals(layer.getVisibility().getValue(), NONE); + } + + @Test + public void testSourceLayer() { + validateTestSetup(); + setupLayer(); + Timber.i("SourceLayer"); + assertNotNull(layer); + + // Get initial + assertEquals(layer.getSourceLayer(), "composite"); + + // Set + final String sourceLayer = "test"; + layer.setSourceLayer(sourceLayer); + assertEquals(layer.getSourceLayer(), sourceLayer); + } + + @Test + public void testFillExtrusionOpacityTransition() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-opacityTransitionOptions"); + assertNotNull(layer); + + // Set and Get + TransitionOptions options = new TransitionOptions(300, 100); + layer.setFillExtrusionOpacityTransition(options); + assertEquals(layer.getFillExtrusionOpacityTransition(), options); + } + + @Test + public void testFillExtrusionOpacityAsConstant() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-opacity"); + assertNotNull(layer); + + // Set and Get + layer.setProperties(fillExtrusionOpacity(0.3f)); + assertEquals((Float) layer.getFillExtrusionOpacity().getValue(), (Float) 0.3f); + } + + @Test + public void testFillExtrusionOpacityAsCameraFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionOpacity( + zoom( + exponential( + stop(2, fillExtrusionOpacity(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getFillExtrusionOpacity()); + assertNotNull(layer.getFillExtrusionOpacity().getFunction()); + assertEquals(CameraFunction.class, layer.getFillExtrusionOpacity().getFunction().getClass()); + assertEquals(ExponentialStops.class, layer.getFillExtrusionOpacity().getFunction().getStops().getClass()); + assertEquals(0.5f, ((ExponentialStops) layer.getFillExtrusionOpacity().getFunction().getStops()).getBase(), 0.001); + assertEquals(1, ((ExponentialStops) layer.getFillExtrusionOpacity().getFunction().getStops()).size()); + } + + @Test + public void testFillExtrusionColorTransition() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-colorTransitionOptions"); + assertNotNull(layer); + + // Set and Get + TransitionOptions options = new TransitionOptions(300, 100); + layer.setFillExtrusionColorTransition(options); + assertEquals(layer.getFillExtrusionColorTransition(), options); + } + + @Test + public void testFillExtrusionColorAsConstant() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-color"); + assertNotNull(layer); + + // Set and Get + layer.setProperties(fillExtrusionColor("rgba(0, 0, 0, 1)")); + assertEquals((String) layer.getFillExtrusionColor().getValue(), (String) "rgba(0, 0, 0, 1)"); + } + + @Test + public void testFillExtrusionColorAsCameraFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionColor( + zoom( + exponential( + stop(2, fillExtrusionColor("rgba(0, 0, 0, 1)")) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getFillExtrusionColor()); + assertNotNull(layer.getFillExtrusionColor().getFunction()); + assertEquals(CameraFunction.class, layer.getFillExtrusionColor().getFunction().getClass()); + assertEquals(ExponentialStops.class, layer.getFillExtrusionColor().getFunction().getStops().getClass()); + assertEquals(0.5f, ((ExponentialStops) layer.getFillExtrusionColor().getFunction().getStops()).getBase(), 0.001); + assertEquals(1, ((ExponentialStops) layer.getFillExtrusionColor().getFunction().getStops()).size()); + } + + @Test + public void testFillExtrusionColorAsIdentitySourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionColor(property("FeaturePropertyA", Stops.<String>identity())) + ); + + // Verify + assertNotNull(layer.getFillExtrusionColor()); + assertNotNull(layer.getFillExtrusionColor().getFunction()); + assertEquals(SourceFunction.class, layer.getFillExtrusionColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionColor().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getFillExtrusionColor().getFunction().getStops().getClass()); + } + + @Test + public void testFillExtrusionColorAsExponentialSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionColor( + property( + "FeaturePropertyA", + exponential( + stop(Color.RED, fillExtrusionColor(Color.RED)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getFillExtrusionColor()); + assertNotNull(layer.getFillExtrusionColor().getFunction()); + assertEquals(SourceFunction.class, layer.getFillExtrusionColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionColor().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getFillExtrusionColor().getFunction().getStops().getClass()); + } + + @Test + public void testFillExtrusionColorAsCategoricalSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionColor( + property( + "FeaturePropertyA", + categorical( + stop("valueA", fillExtrusionColor(Color.RED)) + ) + ).withDefaultValue(fillExtrusionColor(Color.GREEN)) + ) + ); + + // Verify + assertNotNull(layer.getFillExtrusionColor()); + assertNotNull(layer.getFillExtrusionColor().getFunction()); + assertEquals(SourceFunction.class, layer.getFillExtrusionColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionColor().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getFillExtrusionColor().getFunction().getStops().getClass()); + assertNotNull(((SourceFunction) layer.getFillExtrusionColor().getFunction()).getDefaultValue()); + assertNotNull(((SourceFunction) layer.getFillExtrusionColor().getFunction()).getDefaultValue().getValue()); + assertEquals(Color.GREEN, (int) ((SourceFunction) layer.getFillExtrusionColor().getFunction()).getDefaultValue().getColorInt()); + } + + @Test + public void testFillExtrusionColorAsIntConstant() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-color"); + assertNotNull(layer); + + // Set and Get + layer.setProperties(fillExtrusionColor(Color.RED)); + assertEquals(layer.getFillExtrusionColorAsInt(), Color.RED); + } + + @Test + public void testFillExtrusionTranslateTransition() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-translateTransitionOptions"); + assertNotNull(layer); + + // Set and Get + TransitionOptions options = new TransitionOptions(300, 100); + layer.setFillExtrusionTranslateTransition(options); + assertEquals(layer.getFillExtrusionTranslateTransition(), options); + } + + @Test + public void testFillExtrusionTranslateAsConstant() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-translate"); + assertNotNull(layer); + + // Set and Get + layer.setProperties(fillExtrusionTranslate(new Float[]{0f,0f})); + assertEquals((Float[]) layer.getFillExtrusionTranslate().getValue(), (Float[]) new Float[]{0f,0f}); + } + + @Test + public void testFillExtrusionTranslateAsCameraFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-translate"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionTranslate( + zoom( + exponential( + stop(2, fillExtrusionTranslate(new Float[]{0f,0f})) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getFillExtrusionTranslate()); + assertNotNull(layer.getFillExtrusionTranslate().getFunction()); + assertEquals(CameraFunction.class, layer.getFillExtrusionTranslate().getFunction().getClass()); + assertEquals(ExponentialStops.class, layer.getFillExtrusionTranslate().getFunction().getStops().getClass()); + assertEquals(0.5f, ((ExponentialStops) layer.getFillExtrusionTranslate().getFunction().getStops()).getBase(), 0.001); + assertEquals(1, ((ExponentialStops) layer.getFillExtrusionTranslate().getFunction().getStops()).size()); + } + + @Test + public void testFillExtrusionTranslateAnchorAsConstant() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-translate-anchor"); + assertNotNull(layer); + + // Set and Get + layer.setProperties(fillExtrusionTranslateAnchor(FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP)); + assertEquals((String) layer.getFillExtrusionTranslateAnchor().getValue(), (String) FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP); + } + + @Test + public void testFillExtrusionTranslateAnchorAsCameraFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-translate-anchor"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionTranslateAnchor( + zoom( + interval( + stop(2, fillExtrusionTranslateAnchor(FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP)) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getFillExtrusionTranslateAnchor()); + assertNotNull(layer.getFillExtrusionTranslateAnchor().getFunction()); + assertEquals(CameraFunction.class, layer.getFillExtrusionTranslateAnchor().getFunction().getClass()); + assertEquals(IntervalStops.class, layer.getFillExtrusionTranslateAnchor().getFunction().getStops().getClass()); + assertEquals(1, ((IntervalStops) layer.getFillExtrusionTranslateAnchor().getFunction().getStops()).size()); + } + + @Test + public void testFillExtrusionPatternTransition() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-patternTransitionOptions"); + assertNotNull(layer); + + // Set and Get + TransitionOptions options = new TransitionOptions(300, 100); + layer.setFillExtrusionPatternTransition(options); + assertEquals(layer.getFillExtrusionPatternTransition(), options); + } + + @Test + public void testFillExtrusionPatternAsConstant() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-pattern"); + assertNotNull(layer); + + // Set and Get + layer.setProperties(fillExtrusionPattern("pedestrian-polygon")); + assertEquals((String) layer.getFillExtrusionPattern().getValue(), (String) "pedestrian-polygon"); + } + + @Test + public void testFillExtrusionPatternAsCameraFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-pattern"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionPattern( + zoom( + interval( + stop(2, fillExtrusionPattern("pedestrian-polygon")) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getFillExtrusionPattern()); + assertNotNull(layer.getFillExtrusionPattern().getFunction()); + assertEquals(CameraFunction.class, layer.getFillExtrusionPattern().getFunction().getClass()); + assertEquals(IntervalStops.class, layer.getFillExtrusionPattern().getFunction().getStops().getClass()); + assertEquals(1, ((IntervalStops) layer.getFillExtrusionPattern().getFunction().getStops()).size()); + } + + @Test + public void testFillExtrusionHeightTransition() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-heightTransitionOptions"); + assertNotNull(layer); + + // Set and Get + TransitionOptions options = new TransitionOptions(300, 100); + layer.setFillExtrusionHeightTransition(options); + assertEquals(layer.getFillExtrusionHeightTransition(), options); + } + + @Test + public void testFillExtrusionHeightAsConstant() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-height"); + assertNotNull(layer); + + // Set and Get + layer.setProperties(fillExtrusionHeight(0.3f)); + assertEquals((Float) layer.getFillExtrusionHeight().getValue(), (Float) 0.3f); + } + + @Test + public void testFillExtrusionHeightAsCameraFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-height"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionHeight( + zoom( + exponential( + stop(2, fillExtrusionHeight(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getFillExtrusionHeight()); + assertNotNull(layer.getFillExtrusionHeight().getFunction()); + assertEquals(CameraFunction.class, layer.getFillExtrusionHeight().getFunction().getClass()); + assertEquals(ExponentialStops.class, layer.getFillExtrusionHeight().getFunction().getStops().getClass()); + assertEquals(0.5f, ((ExponentialStops) layer.getFillExtrusionHeight().getFunction().getStops()).getBase(), 0.001); + assertEquals(1, ((ExponentialStops) layer.getFillExtrusionHeight().getFunction().getStops()).size()); + } + + @Test + public void testFillExtrusionHeightAsIdentitySourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-height"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionHeight(property("FeaturePropertyA", Stops.<Float>identity())) + ); + + // Verify + assertNotNull(layer.getFillExtrusionHeight()); + assertNotNull(layer.getFillExtrusionHeight().getFunction()); + assertEquals(SourceFunction.class, layer.getFillExtrusionHeight().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getFillExtrusionHeight().getFunction().getStops().getClass()); + } + + @Test + public void testFillExtrusionHeightAsExponentialSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-height"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionHeight( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, fillExtrusionHeight(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getFillExtrusionHeight()); + assertNotNull(layer.getFillExtrusionHeight().getFunction()); + assertEquals(SourceFunction.class, layer.getFillExtrusionHeight().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getFillExtrusionHeight().getFunction().getStops().getClass()); + } + + @Test + public void testFillExtrusionHeightAsCategoricalSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-height"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionHeight( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, fillExtrusionHeight(0.3f)) + ) + ).withDefaultValue(fillExtrusionHeight(0.3f)) + ) + ); + + // Verify + assertNotNull(layer.getFillExtrusionHeight()); + assertNotNull(layer.getFillExtrusionHeight().getFunction()); + assertEquals(SourceFunction.class, layer.getFillExtrusionHeight().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getFillExtrusionHeight().getFunction().getStops().getClass()); + assertNotNull(((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getDefaultValue()); + assertNotNull(((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getDefaultValue().getValue()); + assertEquals(0.3f, ((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getDefaultValue().getValue()); + } + + @Test + public void testFillExtrusionHeightAsCompositeFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-height"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionHeight( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, fillExtrusionHeight(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(fillExtrusionHeight(0.3f)) + ) + ); + + // Verify + assertNotNull(layer.getFillExtrusionHeight()); + assertNotNull(layer.getFillExtrusionHeight().getFunction()); + assertEquals(CompositeFunction.class, layer.getFillExtrusionHeight().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getFillExtrusionHeight().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getFillExtrusionHeight().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getFillExtrusionHeight().getFunction().getStops()).size()); + + ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops = + (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getFillExtrusionHeight().getFunction().getStops(); + Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next(); + assertEquals(0f, stop.in.zoom, 0.001); + assertEquals(0.3f, stop.in.value, 0.001f); + assertEquals(0.9f, stop.out, 0.001f); + } + + @Test + public void testFillExtrusionBaseTransition() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-baseTransitionOptions"); + assertNotNull(layer); + + // Set and Get + TransitionOptions options = new TransitionOptions(300, 100); + layer.setFillExtrusionBaseTransition(options); + assertEquals(layer.getFillExtrusionBaseTransition(), options); + } + + @Test + public void testFillExtrusionBaseAsConstant() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-base"); + assertNotNull(layer); + + // Set and Get + layer.setProperties(fillExtrusionBase(0.3f)); + assertEquals((Float) layer.getFillExtrusionBase().getValue(), (Float) 0.3f); + } + + @Test + public void testFillExtrusionBaseAsCameraFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-base"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionBase( + zoom( + exponential( + stop(2, fillExtrusionBase(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getFillExtrusionBase()); + assertNotNull(layer.getFillExtrusionBase().getFunction()); + assertEquals(CameraFunction.class, layer.getFillExtrusionBase().getFunction().getClass()); + assertEquals(ExponentialStops.class, layer.getFillExtrusionBase().getFunction().getStops().getClass()); + assertEquals(0.5f, ((ExponentialStops) layer.getFillExtrusionBase().getFunction().getStops()).getBase(), 0.001); + assertEquals(1, ((ExponentialStops) layer.getFillExtrusionBase().getFunction().getStops()).size()); + } + + @Test + public void testFillExtrusionBaseAsIdentitySourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-base"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionBase(property("FeaturePropertyA", Stops.<Float>identity())) + ); + + // Verify + assertNotNull(layer.getFillExtrusionBase()); + assertNotNull(layer.getFillExtrusionBase().getFunction()); + assertEquals(SourceFunction.class, layer.getFillExtrusionBase().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionBase().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getFillExtrusionBase().getFunction().getStops().getClass()); + } + + @Test + public void testFillExtrusionBaseAsExponentialSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-base"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionBase( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, fillExtrusionBase(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getFillExtrusionBase()); + assertNotNull(layer.getFillExtrusionBase().getFunction()); + assertEquals(SourceFunction.class, layer.getFillExtrusionBase().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionBase().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getFillExtrusionBase().getFunction().getStops().getClass()); + } + + @Test + public void testFillExtrusionBaseAsCategoricalSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-base"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionBase( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, fillExtrusionBase(0.3f)) + ) + ).withDefaultValue(fillExtrusionBase(0.3f)) + ) + ); + + // Verify + assertNotNull(layer.getFillExtrusionBase()); + assertNotNull(layer.getFillExtrusionBase().getFunction()); + assertEquals(SourceFunction.class, layer.getFillExtrusionBase().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionBase().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getFillExtrusionBase().getFunction().getStops().getClass()); + assertNotNull(((SourceFunction) layer.getFillExtrusionBase().getFunction()).getDefaultValue()); + assertNotNull(((SourceFunction) layer.getFillExtrusionBase().getFunction()).getDefaultValue().getValue()); + assertEquals(0.3f, ((SourceFunction) layer.getFillExtrusionBase().getFunction()).getDefaultValue().getValue()); + } + + @Test + public void testFillExtrusionBaseAsCompositeFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("fill-extrusion-base"); + assertNotNull(layer); + + // Set + layer.setProperties( + fillExtrusionBase( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, fillExtrusionBase(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(fillExtrusionBase(0.3f)) + ) + ); + + // Verify + assertNotNull(layer.getFillExtrusionBase()); + assertNotNull(layer.getFillExtrusionBase().getFunction()); + assertEquals(CompositeFunction.class, layer.getFillExtrusionBase().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getFillExtrusionBase().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getFillExtrusionBase().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getFillExtrusionBase().getFunction().getStops()).size()); + + ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops = + (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getFillExtrusionBase().getFunction().getStops(); + Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next(); + assertEquals(0f, stop.in.zoom, 0.001); + assertEquals(0.3f, stop.in.value, 0.001f); + assertEquals(0.9f, stop.out, 0.001f); + } + +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index 2d2a262055..fc10a01356 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -448,6 +448,17 @@ android:value=".activity.FeatureOverviewActivity"/> </activity> <activity + android:name=".activity.style.FillExtrusionActivity" + android:description="@string/description_fill_extrusion_layer" + android:label="@string/activity_fill_extrusion_layer"> + <meta-data + android:name="@string/category" + android:value="@string/category_style"/> + <meta-data + android:name="android.support.PARENT_ACTIVITY" + android:value=".activity.FeatureOverviewActivity"/> + </activity> + <activity android:name=".activity.style.SymbolLayerActivity" android:description="@string/description_symbol_layer" android:label="@string/activity_symbol_layer"> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java new file mode 100644 index 0000000000..9a7790c6e5 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java @@ -0,0 +1,135 @@ +package com.mapbox.mapboxsdk.testapp.activity.style; + +import android.graphics.Color; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.services.commons.geojson.Polygon; + +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionHeight; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionOpacity; + +/** + * Test activity showcasing fill extrusions + */ +public class FillExtrusionActivity extends AppCompatActivity { + + private MapView mapView; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_fill_extrusion_layer); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(new OnMapReadyCallback() { + @Override + public void onMapReady(@NonNull + final MapboxMap map) { + Polygon domTower = Polygon.fromCoordinates(new double[][][] { + new double[][] { + new double[] { + 5.12112557888031, + 52.09071040847704 + }, + new double[] { + 5.121227502822875, + 52.09053901776669 + }, + new double[] { + 5.121484994888306, + 52.090601641371805 + }, + new double[] { + 5.1213884353637695, + 52.090766439912635 + }, + new double[] { + 5.12112557888031, + 52.09071040847704 + } + } + }); + + GeoJsonSource source = new GeoJsonSource("extrusion-source", domTower); + map.addSource(source); + + map.addLayer( + new FillExtrusionLayer("extrusion-layer", source.getId()) + .withProperties( + fillExtrusionHeight(40f), + fillExtrusionOpacity(0.5f), + fillExtrusionColor(Color.RED) + ) + ); + + map.animateCamera( + CameraUpdateFactory.newCameraPosition( + new CameraPosition.Builder() + .target(new LatLng(52.09071040847704, 5.12112557888031)) + .tilt(45.0) + .zoom(18) + .build() + ), + 10000 + ); + } + }); + } + + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_fill_extrusion_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_fill_extrusion_layer.xml new file mode 100644 index 0000000000..304841dc69 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_fill_extrusion_layer.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <com.mapbox.mapboxsdk.maps.MapView + android:id="@id/mapView" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:mapbox_cameraTargetLat="52.090710" + app:mapbox_cameraTargetLng="5.121125" + app:mapbox_cameraZoom="10" + app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/> + +</RelativeLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml index 3df14caf0c..6270eac72d 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml @@ -57,6 +57,7 @@ <string name="activity_marker_view_rectangle">Marker views in rectangle</string> <string name="activity_url_transform">Url transform</string> <string name="activity_restricted_bounds">Restrict camera to a bounds</string> + <string name="activity_fill_extrusion_layer">Fill extrusions</string> <!--Description--> <string name="description_user_location_tracking">Tracks the location of the user</string> @@ -113,6 +114,7 @@ <string name="description_circle_layer">Show bus stops and route in Singapore</string> <string name="description_url_transform">Transform urls on the fly</string> <string name="description_restricted_bounds">Limit viewport to Iceland</string> + <string name="description_fill_extrusion_layer">Shows how to add 3D extruded shapes</string> <!--Categories--> <string name="category">category</string> diff --git a/platform/android/config.cmake b/platform/android/config.cmake index 7c10ba96f8..c111744ed0 100644 --- a/platform/android/config.cmake +++ b/platform/android/config.cmake @@ -130,6 +130,8 @@ add_library(mbgl-android STATIC platform/android/src/style/layers/circle_layer.hpp platform/android/src/style/layers/custom_layer.cpp platform/android/src/style/layers/custom_layer.hpp + platform/android/src/style/layers/fill_extrusion_layer.cpp + platform/android/src/style/layers/fill_extrusion_layer.hpp platform/android/src/style/layers/fill_layer.cpp platform/android/src/style/layers/fill_layer.hpp platform/android/src/style/layers/layer.cpp diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js index 09563f3b9d..5d8fc4cc6d 100644 --- a/platform/android/scripts/generate-style-code.js +++ b/platform/android/scripts/generate-style-code.js @@ -34,9 +34,6 @@ var layers = Object.keys(spec.layer.type.values).map((type) => { }; }); -// XXX Remove fill-extrusion layer for now -layers = _(layers).filter(layer => layer.type != "fill-extrusion").value(); - // Process all layer properties const layoutProperties = _(layers).map('layoutProperties').flatten().value(); const paintProperties = _(layers).map('paintProperties').flatten().value(); diff --git a/platform/android/src/style/layers/fill_extrusion_layer.cpp b/platform/android/src/style/layers/fill_extrusion_layer.cpp new file mode 100644 index 0000000000..492e1729b9 --- /dev/null +++ b/platform/android/src/style/layers/fill_extrusion_layer.cpp @@ -0,0 +1,200 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#include "fill_extrusion_layer.hpp" + +#include <string> + +#include "../conversion/property_value.hpp" +#include "../conversion/transition_options.hpp" + +namespace mbgl { +namespace android { + + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ + FillExtrusionLayer::FillExtrusionLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId) + : Layer(env, std::make_unique<mbgl::style::FillExtrusionLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) { + } + + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ + FillExtrusionLayer::FillExtrusionLayer(mbgl::Map& map, mbgl::style::FillExtrusionLayer& coreLayer) + : Layer(map, coreLayer) { + } + + /** + * Creates an owning peer object (for layers not attached to the map) + */ + FillExtrusionLayer::FillExtrusionLayer(mbgl::Map& map, std::unique_ptr<mbgl::style::FillExtrusionLayer> coreLayer) + : Layer(map, std::move(coreLayer)) { + } + + FillExtrusionLayer::~FillExtrusionLayer() = default; + + // Property getters + + jni::Object<jni::ObjectTag> FillExtrusionLayer::getFillExtrusionOpacity(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionOpacity()); + return jni::Object<jni::ObjectTag>(*converted); + } + + jni::Object<TransitionOptions> FillExtrusionLayer::getFillExtrusionOpacityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionOpacityTransition(); + return *convert<jni::Object<TransitionOptions>>(env, options); + } + + void FillExtrusionLayer::setFillExtrusionOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::setFillExtrusionOpacityTransition(options); + } + + jni::Object<jni::ObjectTag> FillExtrusionLayer::getFillExtrusionColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionColor()); + return jni::Object<jni::ObjectTag>(*converted); + } + + jni::Object<TransitionOptions> FillExtrusionLayer::getFillExtrusionColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionColorTransition(); + return *convert<jni::Object<TransitionOptions>>(env, options); + } + + void FillExtrusionLayer::setFillExtrusionColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::setFillExtrusionColorTransition(options); + } + + jni::Object<jni::ObjectTag> FillExtrusionLayer::getFillExtrusionTranslate(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionTranslate()); + return jni::Object<jni::ObjectTag>(*converted); + } + + jni::Object<TransitionOptions> FillExtrusionLayer::getFillExtrusionTranslateTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionTranslateTransition(); + return *convert<jni::Object<TransitionOptions>>(env, options); + } + + void FillExtrusionLayer::setFillExtrusionTranslateTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::setFillExtrusionTranslateTransition(options); + } + + jni::Object<jni::ObjectTag> FillExtrusionLayer::getFillExtrusionTranslateAnchor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionTranslateAnchor()); + return jni::Object<jni::ObjectTag>(*converted); + } + + jni::Object<jni::ObjectTag> FillExtrusionLayer::getFillExtrusionPattern(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionPattern()); + return jni::Object<jni::ObjectTag>(*converted); + } + + jni::Object<TransitionOptions> FillExtrusionLayer::getFillExtrusionPatternTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionPatternTransition(); + return *convert<jni::Object<TransitionOptions>>(env, options); + } + + void FillExtrusionLayer::setFillExtrusionPatternTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::setFillExtrusionPatternTransition(options); + } + + jni::Object<jni::ObjectTag> FillExtrusionLayer::getFillExtrusionHeight(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionHeight()); + return jni::Object<jni::ObjectTag>(*converted); + } + + jni::Object<TransitionOptions> FillExtrusionLayer::getFillExtrusionHeightTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionHeightTransition(); + return *convert<jni::Object<TransitionOptions>>(env, options); + } + + void FillExtrusionLayer::setFillExtrusionHeightTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::setFillExtrusionHeightTransition(options); + } + + jni::Object<jni::ObjectTag> FillExtrusionLayer::getFillExtrusionBase(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionBase()); + return jni::Object<jni::ObjectTag>(*converted); + } + + jni::Object<TransitionOptions> FillExtrusionLayer::getFillExtrusionBaseTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionBaseTransition(); + return *convert<jni::Object<TransitionOptions>>(env, options); + } + + void FillExtrusionLayer::setFillExtrusionBaseTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::setFillExtrusionBaseTransition(options); + } + + + jni::Class<FillExtrusionLayer> FillExtrusionLayer::javaClass; + + jni::jobject* FillExtrusionLayer::createJavaPeer(jni::JNIEnv& env) { + static auto constructor = FillExtrusionLayer::javaClass.template GetConstructor<jni::jlong>(env); + return FillExtrusionLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)); + } + + void FillExtrusionLayer::registerNative(jni::JNIEnv& env) { + // Lookup the class + FillExtrusionLayer::javaClass = *jni::Class<FillExtrusionLayer>::Find(env).NewGlobalRef(env).release(); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + + // Register the peer + jni::RegisterNativePeer<FillExtrusionLayer>( + env, FillExtrusionLayer::javaClass, "nativePtr", + std::make_unique<FillExtrusionLayer, JNIEnv&, jni::String, jni::String>, + "initialize", + "finalize", + METHOD(&FillExtrusionLayer::getFillExtrusionOpacityTransition, "nativeGetFillExtrusionOpacityTransition"), + METHOD(&FillExtrusionLayer::setFillExtrusionOpacityTransition, "nativeSetFillExtrusionOpacityTransition"), + METHOD(&FillExtrusionLayer::getFillExtrusionOpacity, "nativeGetFillExtrusionOpacity"), + METHOD(&FillExtrusionLayer::getFillExtrusionColorTransition, "nativeGetFillExtrusionColorTransition"), + METHOD(&FillExtrusionLayer::setFillExtrusionColorTransition, "nativeSetFillExtrusionColorTransition"), + METHOD(&FillExtrusionLayer::getFillExtrusionColor, "nativeGetFillExtrusionColor"), + METHOD(&FillExtrusionLayer::getFillExtrusionTranslateTransition, "nativeGetFillExtrusionTranslateTransition"), + METHOD(&FillExtrusionLayer::setFillExtrusionTranslateTransition, "nativeSetFillExtrusionTranslateTransition"), + METHOD(&FillExtrusionLayer::getFillExtrusionTranslate, "nativeGetFillExtrusionTranslate"), + METHOD(&FillExtrusionLayer::getFillExtrusionTranslateAnchor, "nativeGetFillExtrusionTranslateAnchor"), + METHOD(&FillExtrusionLayer::getFillExtrusionPatternTransition, "nativeGetFillExtrusionPatternTransition"), + METHOD(&FillExtrusionLayer::setFillExtrusionPatternTransition, "nativeSetFillExtrusionPatternTransition"), + METHOD(&FillExtrusionLayer::getFillExtrusionPattern, "nativeGetFillExtrusionPattern"), + METHOD(&FillExtrusionLayer::getFillExtrusionHeightTransition, "nativeGetFillExtrusionHeightTransition"), + METHOD(&FillExtrusionLayer::setFillExtrusionHeightTransition, "nativeSetFillExtrusionHeightTransition"), + METHOD(&FillExtrusionLayer::getFillExtrusionHeight, "nativeGetFillExtrusionHeight"), + METHOD(&FillExtrusionLayer::getFillExtrusionBaseTransition, "nativeGetFillExtrusionBaseTransition"), + METHOD(&FillExtrusionLayer::setFillExtrusionBaseTransition, "nativeSetFillExtrusionBaseTransition"), + METHOD(&FillExtrusionLayer::getFillExtrusionBase, "nativeGetFillExtrusionBase")); + } + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/layers/fill_extrusion_layer.hpp b/platform/android/src/style/layers/fill_extrusion_layer.hpp new file mode 100644 index 0000000000..11a74bc8ef --- /dev/null +++ b/platform/android/src/style/layers/fill_extrusion_layer.hpp @@ -0,0 +1,62 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#pragma once + +#include "layer.hpp" +#include "../transition_options.hpp" +#include <mbgl/style/layers/fill_extrusion_layer.hpp> +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class FillExtrusionLayer : public Layer { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer"; }; + + static jni::Class<FillExtrusionLayer> javaClass; + + static void registerNative(jni::JNIEnv&); + + FillExtrusionLayer(jni::JNIEnv&, jni::String, jni::String); + + FillExtrusionLayer(mbgl::Map&, mbgl::style::FillExtrusionLayer&); + + FillExtrusionLayer(mbgl::Map&, std::unique_ptr<mbgl::style::FillExtrusionLayer>); + + ~FillExtrusionLayer(); + + // Properties + + jni::Object<jni::ObjectTag> getFillExtrusionOpacity(jni::JNIEnv&); + void setFillExtrusionOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Object<TransitionOptions> getFillExtrusionOpacityTransition(jni::JNIEnv&); + + jni::Object<jni::ObjectTag> getFillExtrusionColor(jni::JNIEnv&); + void setFillExtrusionColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Object<TransitionOptions> getFillExtrusionColorTransition(jni::JNIEnv&); + + jni::Object<jni::ObjectTag> getFillExtrusionTranslate(jni::JNIEnv&); + void setFillExtrusionTranslateTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Object<TransitionOptions> getFillExtrusionTranslateTransition(jni::JNIEnv&); + + jni::Object<jni::ObjectTag> getFillExtrusionTranslateAnchor(jni::JNIEnv&); + + jni::Object<jni::ObjectTag> getFillExtrusionPattern(jni::JNIEnv&); + void setFillExtrusionPatternTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Object<TransitionOptions> getFillExtrusionPatternTransition(jni::JNIEnv&); + + jni::Object<jni::ObjectTag> getFillExtrusionHeight(jni::JNIEnv&); + void setFillExtrusionHeightTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Object<TransitionOptions> getFillExtrusionHeightTransition(jni::JNIEnv&); + + jni::Object<jni::ObjectTag> getFillExtrusionBase(jni::JNIEnv&); + void setFillExtrusionBaseTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Object<TransitionOptions> getFillExtrusionBaseTransition(jni::JNIEnv&); + jni::jobject* createJavaPeer(jni::JNIEnv&); + +}; // class FillExtrusionLayer + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp index 58c0c5ee84..1ebad19a87 100644 --- a/platform/android/src/style/layers/layer.cpp +++ b/platform/android/src/style/layers/layer.cpp @@ -139,6 +139,8 @@ namespace android { layer.as<SymbolLayer>()->setSourceLayer(layerId); } else if (layer.is<CircleLayer>()) { layer.as<CircleLayer>()->setSourceLayer(layerId); + } else if(layer.is<FillExtrusionLayer>()) { + layer.as<FillExtrusionLayer>()->setSourceLayer(layerId); } else { mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support source layer"); } @@ -156,6 +158,8 @@ namespace android { sourceLayerId = layer.as<SymbolLayer>()->getSourceLayer(); } else if (layer.is<CircleLayer>()) { sourceLayerId = layer.as<CircleLayer>()->getSourceLayer(); + } else if (layer.is<FillExtrusionLayer>()) { + sourceLayerId = layer.as<FillExtrusionLayer>()->getSourceLayer(); } else { mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support source layer"); } diff --git a/platform/android/src/style/layers/layers.cpp b/platform/android/src/style/layers/layers.cpp index 5c6ee1ae8f..5c49f875ee 100644 --- a/platform/android/src/style/layers/layers.cpp +++ b/platform/android/src/style/layers/layers.cpp @@ -3,6 +3,7 @@ #include <mbgl/style/layer.hpp> #include <mbgl/style/layers/background_layer.hpp> #include <mbgl/style/layers/circle_layer.hpp> +#include <mbgl/style/layers/fill_extrusion_layer.hpp> #include <mbgl/style/layers/fill_layer.hpp> #include <mbgl/style/layers/line_layer.hpp> #include <mbgl/style/layers/raster_layer.hpp> @@ -12,11 +13,13 @@ #include "background_layer.hpp" #include "circle_layer.hpp" #include "custom_layer.hpp" +#include "fill_extrusion_layer.hpp" #include "fill_layer.hpp" #include "line_layer.hpp" #include "raster_layer.hpp" #include "symbol_layer.hpp" #include "unknown_layer.hpp" +#include "fill_extrusion_layer.hpp" namespace mbgl { namespace android { @@ -26,6 +29,8 @@ static Layer* initializeLayerPeer(mbgl::Map& map, mbgl::style::Layer& coreLayer) return new BackgroundLayer(map, *coreLayer.as<mbgl::style::BackgroundLayer>()); } else if (coreLayer.is<mbgl::style::CircleLayer>()) { return new CircleLayer(map, *coreLayer.as<mbgl::style::CircleLayer>()); + } else if (coreLayer.is<mbgl::style::FillExtrusionLayer>()) { + return new FillExtrusionLayer(map, *coreLayer.as<mbgl::style::FillExtrusionLayer>()); } else if (coreLayer.is<mbgl::style::FillLayer>()) { return new FillLayer(map, *coreLayer.as<mbgl::style::FillLayer>()); } else if (coreLayer.is<mbgl::style::LineLayer>()) { @@ -51,6 +56,8 @@ static Layer* initializeLayerPeer(Map& map, std::unique_ptr<mbgl::style::Layer> return createPeer<style::BackgroundLayer, BackgroundLayer>(map, std::move(coreLayer)); } else if (coreLayer->is<style::CircleLayer>()) { return createPeer<style::CircleLayer, CircleLayer>(map, std::move(coreLayer)); + } else if (coreLayer->is<style::FillExtrusionLayer>()) { + return createPeer<style::FillExtrusionLayer, FillExtrusionLayer>(map, std::move(coreLayer)); } else if (coreLayer->is<style::FillLayer>()) { return createPeer<style::FillLayer, FillLayer>(map, std::move(coreLayer)); } else if (coreLayer->is<style::LineLayer>()) { @@ -85,6 +92,7 @@ void registerNativeLayers(jni::JNIEnv& env) { BackgroundLayer::registerNative(env); CircleLayer::registerNative(env); CustomLayer::registerNative(env); + FillExtrusionLayer::registerNative(env); FillLayer::registerNative(env); LineLayer::registerNative(env); RasterLayer::registerNative(env); diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs index 86f8c46f53..2d9ce635b8 100644 --- a/platform/darwin/docs/guides/For Style Authors.md.ejs +++ b/platform/darwin/docs/guides/For Style Authors.md.ejs @@ -240,7 +240,7 @@ whose names differ from the style specification are listed below: <% for (const type in renamedProperties) { -%> <% if (renamedProperties.hasOwnProperty(type)) { -%> -### <%- camelize(type) %> style layers +### <%- camelize(unhyphenate(type)) %> style layers In style JSON | In Objective-C | In Swift --------------|----------------|--------- diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js index 7e798154e4..5d5adbbef6 100644 --- a/platform/darwin/scripts/generate-style-code.js +++ b/platform/darwin/scripts/generate-style-code.js @@ -13,11 +13,6 @@ const suffix = 'StyleLayer'; let spec = _.merge(require('../../../mapbox-gl-js/src/style-spec/reference/v8'), require('./style-spec-overrides-v8.json')); -/// -// Temporarily IGNORE layers that are in the spec yet still not supported in mbgl core -/// -delete spec.layer.type.values['fill-extrusion']; - // Rename properties and keep `original` for use with setters and getters _.forOwn(cocoaConventions, function (properties, kind) { _.forOwn(properties, function (newName, oldName) { diff --git a/platform/darwin/scripts/style-spec-cocoa-conventions-v8.json b/platform/darwin/scripts/style-spec-cocoa-conventions-v8.json index 9d80ff3896..830b6d69f9 100644 --- a/platform/darwin/scripts/style-spec-cocoa-conventions-v8.json +++ b/platform/darwin/scripts/style-spec-cocoa-conventions-v8.json @@ -30,6 +30,10 @@ "fill-translate": "fill-translation", "fill-translate-anchor": "fill-translation-anchor" }, + "paint_fill-extrusion": { + "fill-extrusion-translate": "fill-extrusion-translation", + "fill-extrusion-translate-anchor": "fill-extrusion-translation-anchor" + }, "paint_raster": { "raster-brightness-min": "minimum-raster-brightness", "raster-brightness-max": "maximum-raster-brightness", diff --git a/platform/darwin/scripts/style-spec-overrides-v8.json b/platform/darwin/scripts/style-spec-overrides-v8.json index 28740d458b..a594ef2957 100644 --- a/platform/darwin/scripts/style-spec-overrides-v8.json +++ b/platform/darwin/scripts/style-spec-overrides-v8.json @@ -5,6 +5,9 @@ "fill": { "doc": "An `MGLFillStyleLayer` is a style layer that renders one or more filled (and optionally stroked) polygons on the map.\n\nUse a fill style layer to configure the visual appearance of polygon or multipolygon features in vector tiles loaded by an `MGLVectorSource` object or `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances in an `MGLShapeSource` object." }, + "fill-extrusion": { + "doc": "An `MGLFillExtrusionStyleLayer` is a style layer that renders one or more 3D extruded polygons on the map.\n\nUse a fill-extrusion style layer to configure the visual appearance of polygon or multipolygon features in vector tiles loaded by an `MGLVectorSource` object or `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances in an `MGLShapeSource` object." + }, "line": { "doc": "An `MGLLineStyleLayer` is a style layer that renders one or more stroked polylines on the map.\n\nUse a line style layer to configure the visual appearance of polyline or multipolyline features in vector tiles loaded by an `MGLVectorSource` object or `MGLPolyline`, `MGLPolylineFeature`, `MGLMultiPolyline`, or `MGLMultiPolylineFeature` instances in an `MGLShapeSource` object." }, @@ -50,6 +53,17 @@ "doc": "Name of image in style images to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512)." } }, + "paint_fill-extrusion": { + "fill-extrusion-pattern": { + "doc": "Name of image in style images to use for drawing image fill-extrusions. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512)." + }, + "fill-extrusion-translate": { + "doc": "The geometry's offset." + }, + "fill-extrusion-color": { + "doc": "The base color of this layer. The extrusion's surfaces will be shaded differently based on this color in combination with the `light` settings. If this color is specified with an alpha component, the alpha component will be ignored; use `fill-extrusion-opacity` to set layer opacityco." + } + }, "paint_line": { "line-pattern": { "doc": "Name of image in style images to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512)." diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.h b/platform/darwin/src/MGLFillExtrusionStyleLayer.h new file mode 100644 index 0000000000..84f6bedde4 --- /dev/null +++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.h @@ -0,0 +1,364 @@ +// This file is generated. +// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. + +#import "MGLFoundation.h" +#import "MGLStyleValue.h" +#import "MGLVectorStyleLayer.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + Controls the translation reference point. + + Values of this type are used in the `MGLFillExtrusionStyleLayer.fillExtrusionTranslationAnchor` + property. + */ +typedef NS_ENUM(NSUInteger, MGLFillExtrusionTranslationAnchor) { + /** + The fill extrusion is translated relative to the map. + */ + MGLFillExtrusionTranslationAnchorMap, + /** + The fill extrusion is translated relative to the viewport. + */ + MGLFillExtrusionTranslationAnchorViewport, +}; + +/** + An `MGLFillExtrusionStyleLayer` is a style layer that renders one or more 3D + extruded polygons on the map. + + Use a fill-extrusion style layer to configure the visual appearance of polygon + or multipolygon features in vector tiles loaded by an `MGLVectorSource` object + or `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or + `MGLMultiPolygonFeature` instances in an `MGLShapeSource` object. + + You can access an existing fill-extrusion style layer using the + `-[MGLStyle layerWithIdentifier:]` method if you know its identifier; + otherwise, find it using the `MGLStyle.layers` property. You can also create a + new fill-extrusion style layer and add it to the style using a method such as + `-[MGLStyle addLayer:]`. + + ### Example + + ```swift + let layer = MGLFillExtrusionStyleLayer(identifier: "buildings", source: buildings) + layer.sourceLayerIdentifier = "building" + layer.fillExtrusionHeight = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "height", options: nil) + layer.fillExtrusionBase = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "min_height", options: nil) + layer.predicate = NSPredicate(format: "extrude == TRUE") + mapView.style?.addLayer(layer) + ``` + */ +MGL_EXPORT +@interface MGLFillExtrusionStyleLayer : MGLVectorStyleLayer + +/** + Returns a fill-extrusion style layer initialized with an identifier and source. + + After initializing and configuring the style layer, add it to a map view’s + style using the `-[MGLStyle addLayer:]` or + `-[MGLStyle insertLayer:belowLayer:]` method. + + @param identifier A string that uniquely identifies the source in the style to + which it is added. + @param source The source from which to obtain the data to style. If the source + has not yet been added to the current style, the behavior is undefined. + @return An initialized foreground style layer. + */ +- (instancetype)initWithIdentifier:(NSString *)identifier source:(MGLSource *)source; + +#pragma mark - Accessing the Paint Attributes + +/** + The height with which to extrude the base of this layer. Must be less than or + equal to `fillExtrusionHeight`. + + This property is measured in meters. + + The default value of this property is an `MGLStyleValue` object containing an + `NSNumber` object containing the float `0`. Set this property to `nil` to reset + it to the default value. + + This property is only applied to the style if `fillExtrusionHeight` is + non-`nil`. Otherwise, it is ignored. + + You can set this property to an instance of: + + * `MGLConstantStyleValue` + * `MGLCameraStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + */ +@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillExtrusionBase; + +/** + The transition affecting any changes to this layer’s `fillExtrusionBase` property. + + This property corresponds to the `fill-extrusion-base-transition` property in the style JSON file format. +*/ +@property (nonatomic) MGLTransition fillExtrusionBaseTransition; + +#if TARGET_OS_IPHONE +/** + The base color of this layer. The extrusion's surfaces will be shaded + differently based on this color in combination with the `light` settings. If + this color is specified with an alpha component, the alpha component will be + ignored; use `fillExtrusionOpacity` to set layer opacityco. + + The default value of this property is an `MGLStyleValue` object containing + `UIColor.blackColor`. Set this property to `nil` to reset it to the default + value. + + This property is only applied to the style if `fillExtrusionPattern` is set to + `nil`. Otherwise, it is ignored. + + You can set this property to an instance of: + + * `MGLConstantStyleValue` + * `MGLCameraStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + */ +@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *fillExtrusionColor; +#else +/** + The base color of this layer. The extrusion's surfaces will be shaded + differently based on this color in combination with the `light` settings. If + this color is specified with an alpha component, the alpha component will be + ignored; use `fillExtrusionOpacity` to set layer opacityco. + + The default value of this property is an `MGLStyleValue` object containing + `NSColor.blackColor`. Set this property to `nil` to reset it to the default + value. + + This property is only applied to the style if `fillExtrusionPattern` is set to + `nil`. Otherwise, it is ignored. + + You can set this property to an instance of: + + * `MGLConstantStyleValue` + * `MGLCameraStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + */ +@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *fillExtrusionColor; +#endif + +/** + The transition affecting any changes to this layer’s `fillExtrusionColor` property. + + This property corresponds to the `fill-extrusion-color-transition` property in the style JSON file format. +*/ +@property (nonatomic) MGLTransition fillExtrusionColorTransition; + +/** + The height with which to extrude this layer. + + This property is measured in meters. + + The default value of this property is an `MGLStyleValue` object containing an + `NSNumber` object containing the float `0`. Set this property to `nil` to reset + it to the default value. + + You can set this property to an instance of: + + * `MGLConstantStyleValue` + * `MGLCameraStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + */ +@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillExtrusionHeight; + +/** + The transition affecting any changes to this layer’s `fillExtrusionHeight` property. + + This property corresponds to the `fill-extrusion-height-transition` property in the style JSON file format. +*/ +@property (nonatomic) MGLTransition fillExtrusionHeightTransition; + +/** + The opacity of the entire fill extrusion layer. This is rendered on a + per-layer, not per-feature, basis, and data-driven styling is not available. + + The default value of this property is an `MGLStyleValue` object containing an + `NSNumber` object containing the float `1`. Set this property to `nil` to reset + it to the default value. + + You can set this property to an instance of: + + * `MGLConstantStyleValue` + * `MGLCameraStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + */ +@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillExtrusionOpacity; + +/** + The transition affecting any changes to this layer’s `fillExtrusionOpacity` property. + + This property corresponds to the `fill-extrusion-opacity-transition` property in the style JSON file format. +*/ +@property (nonatomic) MGLTransition fillExtrusionOpacityTransition; + +/** + Name of image in style images to use for drawing image fill-extrusions. For + seamless patterns, image width and height must be a factor of two (2, 4, 8, + ..., 512). + + You can set this property to an instance of: + + * `MGLConstantStyleValue` + * `MGLCameraStyleFunction` with an interpolation mode of + `MGLInterpolationModeInterval` + */ +@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *fillExtrusionPattern; + +/** + The transition affecting any changes to this layer’s `fillExtrusionPattern` property. + + This property corresponds to the `fill-extrusion-pattern-transition` property in the style JSON file format. +*/ +@property (nonatomic) MGLTransition fillExtrusionPatternTransition; + +#if TARGET_OS_IPHONE +/** + The geometry's offset. + + This property is measured in points. + + The default value of this property is an `MGLStyleValue` object containing an + `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0 + points downward. Set this property to `nil` to reset it to the default value. + + This attribute corresponds to the <a + href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-extrusion-translate"><code>fill-extrusion-translate</code></a> + layout property in the Mapbox Style Specification. + + You can set this property to an instance of: + + * `MGLConstantStyleValue` + * `MGLCameraStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + */ +@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslation; +#else +/** + The geometry's offset. + + This property is measured in points. + + The default value of this property is an `MGLStyleValue` object containing an + `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0 + points upward. Set this property to `nil` to reset it to the default value. + + This attribute corresponds to the <a + href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-extrusion-translate"><code>fill-extrusion-translate</code></a> + layout property in the Mapbox Style Specification. + + You can set this property to an instance of: + + * `MGLConstantStyleValue` + * `MGLCameraStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + */ +@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslation; +#endif + +/** + The transition affecting any changes to this layer’s `fillExtrusionTranslation` property. + + This property corresponds to the `fill-extrusion-translate-transition` property in the style JSON file format. +*/ +@property (nonatomic) MGLTransition fillExtrusionTranslationTransition; + +@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslate __attribute__((unavailable("Use fillExtrusionTranslation instead."))); + +/** + Controls the translation reference point. + + The default value of this property is an `MGLStyleValue` object containing an + `NSValue` object containing `MGLFillExtrusionTranslationAnchorMap`. Set this + property to `nil` to reset it to the default value. + + This property is only applied to the style if `fillExtrusionTranslation` is + non-`nil`. Otherwise, it is ignored. + + This attribute corresponds to the <a + href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-extrusion-translate-anchor"><code>fill-extrusion-translate-anchor</code></a> + layout property in the Mapbox Style Specification. + + You can set this property to an instance of: + + * `MGLConstantStyleValue` + * `MGLCameraStyleFunction` with an interpolation mode of + `MGLInterpolationModeInterval` + */ +@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslationAnchor; + +@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslateAnchor __attribute__((unavailable("Use fillExtrusionTranslationAnchor instead."))); + +@end + +/** + Methods for wrapping an enumeration value for a style layer attribute in an + `MGLFillExtrusionStyleLayer` object and unwrapping its raw value. + */ +@interface NSValue (MGLFillExtrusionStyleLayerAdditions) + +#pragma mark Working with Fill extrusion Style Layer Attribute Values + +/** + Creates a new value object containing the given `MGLFillExtrusionTranslationAnchor` enumeration. + + @param fillExtrusionTranslationAnchor The value for the new object. + @return A new value object that contains the enumeration value. + */ ++ (instancetype)valueWithMGLFillExtrusionTranslationAnchor:(MGLFillExtrusionTranslationAnchor)fillExtrusionTranslationAnchor; + +/** + The `MGLFillExtrusionTranslationAnchor` enumeration representation of the value. + */ +@property (readonly) MGLFillExtrusionTranslationAnchor MGLFillExtrusionTranslationAnchorValue; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm new file mode 100644 index 0000000000..b00ed8e09f --- /dev/null +++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm @@ -0,0 +1,335 @@ +// This file is generated. +// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. + +#import "MGLSource.h" +#import "NSPredicate+MGLAdditions.h" +#import "NSDate+MGLAdditions.h" +#import "MGLStyleLayer_Private.h" +#import "MGLStyleValue_Private.h" +#import "MGLFillExtrusionStyleLayer.h" + +#include <mbgl/style/transition_options.hpp> +#include <mbgl/style/layers/fill_extrusion_layer.hpp> + +namespace mbgl { + + MBGL_DEFINE_ENUM(MGLFillExtrusionTranslationAnchor, { + { MGLFillExtrusionTranslationAnchorMap, "map" }, + { MGLFillExtrusionTranslationAnchorViewport, "viewport" }, + }); + +} + +@interface MGLFillExtrusionStyleLayer () + +@property (nonatomic, readonly) mbgl::style::FillExtrusionLayer *rawLayer; + +@end + +@implementation MGLFillExtrusionStyleLayer + +- (instancetype)initWithIdentifier:(NSString *)identifier source:(MGLSource *)source +{ + auto layer = std::make_unique<mbgl::style::FillExtrusionLayer>(identifier.UTF8String, source.identifier.UTF8String); + return self = [super initWithPendingLayer:std::move(layer)]; +} + +- (mbgl::style::FillExtrusionLayer *)rawLayer +{ + return (mbgl::style::FillExtrusionLayer *)super.rawLayer; +} + +- (NSString *)sourceIdentifier +{ + MGLAssertStyleLayerIsValid(); + + return @(self.rawLayer->getSourceID().c_str()); +} + +- (NSString *)sourceLayerIdentifier +{ + MGLAssertStyleLayerIsValid(); + + auto layerID = self.rawLayer->getSourceLayer(); + return layerID.empty() ? nil : @(layerID.c_str()); +} + +- (void)setSourceLayerIdentifier:(NSString *)sourceLayerIdentifier +{ + MGLAssertStyleLayerIsValid(); + + self.rawLayer->setSourceLayer(sourceLayerIdentifier.UTF8String ?: ""); +} + +- (void)setPredicate:(NSPredicate *)predicate +{ + MGLAssertStyleLayerIsValid(); + + self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter()); +} + +- (NSPredicate *)predicate +{ + MGLAssertStyleLayerIsValid(); + + return [NSPredicate mgl_predicateWithFilter:self.rawLayer->getFilter()]; +} + +#pragma mark - Accessing the Paint Attributes + +- (void)setFillExtrusionBase:(MGLStyleValue<NSNumber *> *)fillExtrusionBase { + MGLAssertStyleLayerIsValid(); + + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(fillExtrusionBase); + self.rawLayer->setFillExtrusionBase(mbglValue); +} + +- (MGLStyleValue<NSNumber *> *)fillExtrusionBase { + MGLAssertStyleLayerIsValid(); + + auto propertyValue = self.rawLayer->getFillExtrusionBase(); + if (propertyValue.isUndefined()) { + return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillExtrusionBase()); + } + return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); +} + +- (void)setFillExtrusionBaseTransition:(MGLTransition )transition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } }; + self.rawLayer->setFillExtrusionBaseTransition(options); +} + +- (MGLTransition)fillExtrusionBaseTransition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getFillExtrusionBaseTransition(); + MGLTransition transition; + transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero())); + transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero())); + + return transition; +} + +- (void)setFillExtrusionColor:(MGLStyleValue<MGLColor *> *)fillExtrusionColor { + MGLAssertStyleLayerIsValid(); + + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(fillExtrusionColor); + self.rawLayer->setFillExtrusionColor(mbglValue); +} + +- (MGLStyleValue<MGLColor *> *)fillExtrusionColor { + MGLAssertStyleLayerIsValid(); + + auto propertyValue = self.rawLayer->getFillExtrusionColor(); + if (propertyValue.isUndefined()) { + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillExtrusionColor()); + } + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue); +} + +- (void)setFillExtrusionColorTransition:(MGLTransition )transition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } }; + self.rawLayer->setFillExtrusionColorTransition(options); +} + +- (MGLTransition)fillExtrusionColorTransition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getFillExtrusionColorTransition(); + MGLTransition transition; + transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero())); + transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero())); + + return transition; +} + +- (void)setFillExtrusionHeight:(MGLStyleValue<NSNumber *> *)fillExtrusionHeight { + MGLAssertStyleLayerIsValid(); + + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(fillExtrusionHeight); + self.rawLayer->setFillExtrusionHeight(mbglValue); +} + +- (MGLStyleValue<NSNumber *> *)fillExtrusionHeight { + MGLAssertStyleLayerIsValid(); + + auto propertyValue = self.rawLayer->getFillExtrusionHeight(); + if (propertyValue.isUndefined()) { + return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillExtrusionHeight()); + } + return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); +} + +- (void)setFillExtrusionHeightTransition:(MGLTransition )transition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } }; + self.rawLayer->setFillExtrusionHeightTransition(options); +} + +- (MGLTransition)fillExtrusionHeightTransition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getFillExtrusionHeightTransition(); + MGLTransition transition; + transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero())); + transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero())); + + return transition; +} + +- (void)setFillExtrusionOpacity:(MGLStyleValue<NSNumber *> *)fillExtrusionOpacity { + MGLAssertStyleLayerIsValid(); + + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(fillExtrusionOpacity); + self.rawLayer->setFillExtrusionOpacity(mbglValue); +} + +- (MGLStyleValue<NSNumber *> *)fillExtrusionOpacity { + MGLAssertStyleLayerIsValid(); + + auto propertyValue = self.rawLayer->getFillExtrusionOpacity(); + if (propertyValue.isUndefined()) { + return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultFillExtrusionOpacity()); + } + return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); +} + +- (void)setFillExtrusionOpacityTransition:(MGLTransition )transition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } }; + self.rawLayer->setFillExtrusionOpacityTransition(options); +} + +- (MGLTransition)fillExtrusionOpacityTransition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getFillExtrusionOpacityTransition(); + MGLTransition transition; + transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero())); + transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero())); + + return transition; +} + +- (void)setFillExtrusionPattern:(MGLStyleValue<NSString *> *)fillExtrusionPattern { + MGLAssertStyleLayerIsValid(); + + auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue(fillExtrusionPattern); + self.rawLayer->setFillExtrusionPattern(mbglValue); +} + +- (MGLStyleValue<NSString *> *)fillExtrusionPattern { + MGLAssertStyleLayerIsValid(); + + auto propertyValue = self.rawLayer->getFillExtrusionPattern(); + if (propertyValue.isUndefined()) { + return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(self.rawLayer->getDefaultFillExtrusionPattern()); + } + return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(propertyValue); +} + +- (void)setFillExtrusionPatternTransition:(MGLTransition )transition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } }; + self.rawLayer->setFillExtrusionPatternTransition(options); +} + +- (MGLTransition)fillExtrusionPatternTransition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getFillExtrusionPatternTransition(); + MGLTransition transition; + transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero())); + transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero())); + + return transition; +} + +- (void)setFillExtrusionTranslation:(MGLStyleValue<NSValue *> *)fillExtrusionTranslation { + MGLAssertStyleLayerIsValid(); + + auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toInterpolatablePropertyValue(fillExtrusionTranslation); + self.rawLayer->setFillExtrusionTranslate(mbglValue); +} + +- (MGLStyleValue<NSValue *> *)fillExtrusionTranslation { + MGLAssertStyleLayerIsValid(); + + auto propertyValue = self.rawLayer->getFillExtrusionTranslate(); + if (propertyValue.isUndefined()) { + return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(self.rawLayer->getDefaultFillExtrusionTranslate()); + } + return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue); +} + +- (void)setFillExtrusionTranslationTransition:(MGLTransition )transition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } }; + self.rawLayer->setFillExtrusionTranslateTransition(options); +} + +- (MGLTransition)fillExtrusionTranslationTransition { + MGLAssertStyleLayerIsValid(); + + mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getFillExtrusionTranslateTransition(); + MGLTransition transition; + transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero())); + transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero())); + + return transition; +} + +- (void)setFillExtrusionTranslate:(MGLStyleValue<NSValue *> *)fillExtrusionTranslate { +} + +- (MGLStyleValue<NSValue *> *)fillExtrusionTranslate { + return self.fillExtrusionTranslation; +} + +- (void)setFillExtrusionTranslationAnchor:(MGLStyleValue<NSValue *> *)fillExtrusionTranslationAnchor { + MGLAssertStyleLayerIsValid(); + + auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillExtrusionTranslationAnchor>().toEnumPropertyValue(fillExtrusionTranslationAnchor); + self.rawLayer->setFillExtrusionTranslateAnchor(mbglValue); +} + +- (MGLStyleValue<NSValue *> *)fillExtrusionTranslationAnchor { + MGLAssertStyleLayerIsValid(); + + auto propertyValue = self.rawLayer->getFillExtrusionTranslateAnchor(); + if (propertyValue.isUndefined()) { + return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillExtrusionTranslationAnchor>().toEnumStyleValue(self.rawLayer->getDefaultFillExtrusionTranslateAnchor()); + } + return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillExtrusionTranslationAnchor>().toEnumStyleValue(propertyValue); +} + +- (void)setFillExtrusionTranslateAnchor:(MGLStyleValue<NSValue *> *)fillExtrusionTranslateAnchor { +} + +- (MGLStyleValue<NSValue *> *)fillExtrusionTranslateAnchor { + return self.fillExtrusionTranslationAnchor; +} + +@end + +@implementation NSValue (MGLFillExtrusionStyleLayerAdditions) + ++ (NSValue *)valueWithMGLFillExtrusionTranslationAnchor:(MGLFillExtrusionTranslationAnchor)fillExtrusionTranslationAnchor { + return [NSValue value:&fillExtrusionTranslationAnchor withObjCType:@encode(MGLFillExtrusionTranslationAnchor)]; +} + +- (MGLFillExtrusionTranslationAnchor)MGLFillExtrusionTranslationAnchorValue { + MGLFillExtrusionTranslationAnchor fillExtrusionTranslationAnchor; + [self getValue:&fillExtrusionTranslationAnchor]; + return fillExtrusionTranslationAnchor; +} + +@end diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm index a4cf8d9cce..81b6446e7f 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -3,6 +3,7 @@ #import "MGLMapView_Private.h" #import "MGLStyleLayer.h" #import "MGLFillStyleLayer.h" +#import "MGLFillExtrusionStyleLayer.h" #import "MGLLineStyleLayer.h" #import "MGLCircleStyleLayer.h" #import "MGLSymbolStyleLayer.h" @@ -28,6 +29,7 @@ #include <mbgl/util/default_styles.hpp> #include <mbgl/style/image.hpp> #include <mbgl/style/layers/fill_layer.hpp> +#include <mbgl/style/layers/fill_extrusion_layer.hpp> #include <mbgl/style/layers/line_layer.hpp> #include <mbgl/style/layers/symbol_layer.hpp> #include <mbgl/style/layers/raster_layer.hpp> @@ -332,6 +334,8 @@ static NSURL *MGLStyleURL_emerald; if (auto fillLayer = rawLayer->as<mbgl::style::FillLayer>()) { return [[MGLFillStyleLayer alloc] initWithRawLayer:fillLayer]; + } else if (auto fillExtrusionLayer = rawLayer->as<mbgl::style::FillExtrusionLayer>()) { + return [[MGLFillExtrusionStyleLayer alloc] initWithRawLayer:fillExtrusionLayer]; } else if (auto lineLayer = rawLayer->as<mbgl::style::LineLayer>()) { return [[MGLLineStyleLayer alloc] initWithRawLayer:lineLayer]; } else if (auto symbolLayer = rawLayer->as<mbgl::style::SymbolLayer>()) { diff --git a/platform/darwin/src/MGLStyleLayer.h.ejs b/platform/darwin/src/MGLStyleLayer.h.ejs index e29ea4611d..df42621c6d 100644 --- a/platform/darwin/src/MGLStyleLayer.h.ejs +++ b/platform/darwin/src/MGLStyleLayer.h.ejs @@ -161,7 +161,7 @@ which it is added. */ @interface NSValue (MGL<%- camelize(type) %>StyleLayerAdditions) -#pragma mark Working with <%- camelize(type) %> Style Layer Attribute Values +#pragma mark Working with <%- camelize(unhyphenate(type)) %> Style Layer Attribute Values <% for (let property of enumProperties) { -%> /** diff --git a/platform/darwin/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift index 177d97d523..6d2dc597a9 100644 --- a/platform/darwin/test/MGLDocumentationExampleTests.swift +++ b/platform/darwin/test/MGLDocumentationExampleTests.swift @@ -158,6 +158,22 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate { XCTAssertNotNil(mapView.style?.layer(withIdentifier: "parks")) } + + func testMGLFillExtrusionStyleLayer() { + let buildings = MGLVectorSource(identifier: "buildings", configurationURL: URL(string: "https://example.com/style.json")!) + mapView.style?.addSource(buildings) + + //#-example-code + let layer = MGLFillExtrusionStyleLayer(identifier: "buildings", source: buildings) + layer.sourceLayerIdentifier = "building" + layer.fillExtrusionHeight = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "height", options: nil) + layer.fillExtrusionBase = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "min_height", options: nil) + layer.predicate = NSPredicate(format: "extrude == TRUE") + mapView.style?.addLayer(layer) + //#-end-example-code + + XCTAssertNotNil(mapView.style?.layer(withIdentifier: "buildings")) + } func testMGLSymbolStyleLayer() { let pois = MGLVectorSource(identifier: "pois", configurationURL: URL(string: "https://example.com/style.json")!) diff --git a/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm b/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm new file mode 100644 index 0000000000..5d99c815ea --- /dev/null +++ b/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm @@ -0,0 +1,445 @@ +// This file is generated. +// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. + +#import "MGLStyleLayerTests.h" +#import "../../darwin/src/NSDate+MGLAdditions.h" + +#import "MGLStyleLayer_Private.h" + +#include <mbgl/style/layers/fill_extrusion_layer.hpp> +#include <mbgl/style/transition_options.hpp> + +@interface MGLFillExtrusionLayerTests : MGLStyleLayerTests +@end + +@implementation MGLFillExtrusionLayerTests + ++ (NSString *)layerType { + return @"fill-extrusion"; +} + +- (void)testPredicates { + MGLPointFeature *feature = [[MGLPointFeature alloc] init]; + MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil]; + MGLFillExtrusionStyleLayer *layer = [[MGLFillExtrusionStyleLayer alloc] initWithIdentifier:@"layerID" source:source]; + + XCTAssertNil(layer.sourceLayerIdentifier); + layer.sourceLayerIdentifier = @"layerID"; + XCTAssertEqualObjects(layer.sourceLayerIdentifier, @"layerID"); + layer.sourceLayerIdentifier = nil; + XCTAssertNil(layer.sourceLayerIdentifier); + + XCTAssertNil(layer.predicate); + layer.predicate = [NSPredicate predicateWithValue:NO]; + XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]); + layer.predicate = nil; + XCTAssertNil(layer.predicate); +} + +- (void)testProperties { + MGLPointFeature *feature = [[MGLPointFeature alloc] init]; + MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil]; + + MGLFillExtrusionStyleLayer *layer = [[MGLFillExtrusionStyleLayer alloc] initWithIdentifier:@"layerID" source:source]; + XCTAssertNotEqual(layer.rawLayer, nullptr); + XCTAssertTrue(layer.rawLayer->is<mbgl::style::FillExtrusionLayer>()); + auto rawLayer = layer.rawLayer->as<mbgl::style::FillExtrusionLayer>(); + + MGLTransition transitionTest = MGLTransitionMake(5, 4); + + + // fill-extrusion-base + { + XCTAssertTrue(rawLayer->getFillExtrusionBase().isUndefined(), + @"fill-extrusion-base should be unset initially."); + MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillExtrusionBase; + + MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; + layer.fillExtrusionBase = constantStyleValue; + mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; + XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue, + @"Setting fillExtrusionBase to a constant value should update fill-extrusion-base."); + XCTAssertEqualObjects(layer.fillExtrusionBase, constantStyleValue, + @"fillExtrusionBase should round-trip constant values."); + + MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; + layer.fillExtrusionBase = functionStyleValue; + + mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; + + XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue, + @"Setting fillExtrusionBase to a camera function should update fill-extrusion-base."); + XCTAssertEqualObjects(layer.fillExtrusionBase, functionStyleValue, + @"fillExtrusionBase should round-trip camera functions."); + + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.fillExtrusionBase = functionStyleValue; + + mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue, + @"Setting fillExtrusionBase to a source function should update fill-extrusion-base."); + XCTAssertEqualObjects(layer.fillExtrusionBase, functionStyleValue, + @"fillExtrusionBase should round-trip source functions."); + + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.fillExtrusionBase = functionStyleValue; + + std::map<float, float> innerStops { {18, 0xff} }; + mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue, + @"Setting fillExtrusionBase to a composite function should update fill-extrusion-base."); + XCTAssertEqualObjects(layer.fillExtrusionBase, functionStyleValue, + @"fillExtrusionBase should round-trip composite functions."); + + + layer.fillExtrusionBase = nil; + XCTAssertTrue(rawLayer->getFillExtrusionBase().isUndefined(), + @"Unsetting fillExtrusionBase should return fill-extrusion-base to the default value."); + XCTAssertEqualObjects(layer.fillExtrusionBase, defaultStyleValue, + @"fillExtrusionBase should return the default value after being unset."); + // Transition property test + layer.fillExtrusionBaseTransition = transitionTest; + auto toptions = rawLayer->getFillExtrusionBaseTransition(); + XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay); + XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration); + + MGLTransition fillExtrusionBaseTransition = layer.fillExtrusionBaseTransition; + XCTAssertEqual(fillExtrusionBaseTransition.delay, transitionTest.delay); + XCTAssertEqual(fillExtrusionBaseTransition.duration, transitionTest.duration); + } + + // fill-extrusion-color + { + XCTAssertTrue(rawLayer->getFillExtrusionColor().isUndefined(), + @"fill-extrusion-color should be unset initially."); + MGLStyleValue<MGLColor *> *defaultStyleValue = layer.fillExtrusionColor; + + MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; + layer.fillExtrusionColor = constantStyleValue; + mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; + XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue, + @"Setting fillExtrusionColor to a constant value should update fill-extrusion-color."); + XCTAssertEqualObjects(layer.fillExtrusionColor, constantStyleValue, + @"fillExtrusionColor should round-trip constant values."); + + MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; + layer.fillExtrusionColor = functionStyleValue; + + mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} }; + propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; + + XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue, + @"Setting fillExtrusionColor to a camera function should update fill-extrusion-color."); + XCTAssertEqualObjects(layer.fillExtrusionColor, functionStyleValue, + @"fillExtrusionColor should round-trip camera functions."); + + functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.fillExtrusionColor = functionStyleValue; + + mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue, + @"Setting fillExtrusionColor to a source function should update fill-extrusion-color."); + XCTAssertEqualObjects(layer.fillExtrusionColor, functionStyleValue, + @"fillExtrusionColor should round-trip source functions."); + + functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.fillExtrusionColor = functionStyleValue; + + std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} }; + mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue, + @"Setting fillExtrusionColor to a composite function should update fill-extrusion-color."); + XCTAssertEqualObjects(layer.fillExtrusionColor, functionStyleValue, + @"fillExtrusionColor should round-trip composite functions."); + + + layer.fillExtrusionColor = nil; + XCTAssertTrue(rawLayer->getFillExtrusionColor().isUndefined(), + @"Unsetting fillExtrusionColor should return fill-extrusion-color to the default value."); + XCTAssertEqualObjects(layer.fillExtrusionColor, defaultStyleValue, + @"fillExtrusionColor should return the default value after being unset."); + // Transition property test + layer.fillExtrusionColorTransition = transitionTest; + auto toptions = rawLayer->getFillExtrusionColorTransition(); + XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay); + XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration); + + MGLTransition fillExtrusionColorTransition = layer.fillExtrusionColorTransition; + XCTAssertEqual(fillExtrusionColorTransition.delay, transitionTest.delay); + XCTAssertEqual(fillExtrusionColorTransition.duration, transitionTest.duration); + } + + // fill-extrusion-height + { + XCTAssertTrue(rawLayer->getFillExtrusionHeight().isUndefined(), + @"fill-extrusion-height should be unset initially."); + MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillExtrusionHeight; + + MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; + layer.fillExtrusionHeight = constantStyleValue; + mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; + XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue, + @"Setting fillExtrusionHeight to a constant value should update fill-extrusion-height."); + XCTAssertEqualObjects(layer.fillExtrusionHeight, constantStyleValue, + @"fillExtrusionHeight should round-trip constant values."); + + MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; + layer.fillExtrusionHeight = functionStyleValue; + + mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; + + XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue, + @"Setting fillExtrusionHeight to a camera function should update fill-extrusion-height."); + XCTAssertEqualObjects(layer.fillExtrusionHeight, functionStyleValue, + @"fillExtrusionHeight should round-trip camera functions."); + + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.fillExtrusionHeight = functionStyleValue; + + mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue, + @"Setting fillExtrusionHeight to a source function should update fill-extrusion-height."); + XCTAssertEqualObjects(layer.fillExtrusionHeight, functionStyleValue, + @"fillExtrusionHeight should round-trip source functions."); + + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.fillExtrusionHeight = functionStyleValue; + + std::map<float, float> innerStops { {18, 0xff} }; + mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue, + @"Setting fillExtrusionHeight to a composite function should update fill-extrusion-height."); + XCTAssertEqualObjects(layer.fillExtrusionHeight, functionStyleValue, + @"fillExtrusionHeight should round-trip composite functions."); + + + layer.fillExtrusionHeight = nil; + XCTAssertTrue(rawLayer->getFillExtrusionHeight().isUndefined(), + @"Unsetting fillExtrusionHeight should return fill-extrusion-height to the default value."); + XCTAssertEqualObjects(layer.fillExtrusionHeight, defaultStyleValue, + @"fillExtrusionHeight should return the default value after being unset."); + // Transition property test + layer.fillExtrusionHeightTransition = transitionTest; + auto toptions = rawLayer->getFillExtrusionHeightTransition(); + XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay); + XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration); + + MGLTransition fillExtrusionHeightTransition = layer.fillExtrusionHeightTransition; + XCTAssertEqual(fillExtrusionHeightTransition.delay, transitionTest.delay); + XCTAssertEqual(fillExtrusionHeightTransition.duration, transitionTest.duration); + } + + // fill-extrusion-opacity + { + XCTAssertTrue(rawLayer->getFillExtrusionOpacity().isUndefined(), + @"fill-extrusion-opacity should be unset initially."); + MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillExtrusionOpacity; + + MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; + layer.fillExtrusionOpacity = constantStyleValue; + mbgl::style::PropertyValue<float> propertyValue = { 0xff }; + XCTAssertEqual(rawLayer->getFillExtrusionOpacity(), propertyValue, + @"Setting fillExtrusionOpacity to a constant value should update fill-extrusion-opacity."); + XCTAssertEqualObjects(layer.fillExtrusionOpacity, constantStyleValue, + @"fillExtrusionOpacity should round-trip constant values."); + + MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; + layer.fillExtrusionOpacity = functionStyleValue; + + mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} }; + propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; + + XCTAssertEqual(rawLayer->getFillExtrusionOpacity(), propertyValue, + @"Setting fillExtrusionOpacity to a camera function should update fill-extrusion-opacity."); + XCTAssertEqualObjects(layer.fillExtrusionOpacity, functionStyleValue, + @"fillExtrusionOpacity should round-trip camera functions."); + + + + layer.fillExtrusionOpacity = nil; + XCTAssertTrue(rawLayer->getFillExtrusionOpacity().isUndefined(), + @"Unsetting fillExtrusionOpacity should return fill-extrusion-opacity to the default value."); + XCTAssertEqualObjects(layer.fillExtrusionOpacity, defaultStyleValue, + @"fillExtrusionOpacity should return the default value after being unset."); + + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; + XCTAssertThrowsSpecificNamed(layer.fillExtrusionOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; + XCTAssertThrowsSpecificNamed(layer.fillExtrusionOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + // Transition property test + layer.fillExtrusionOpacityTransition = transitionTest; + auto toptions = rawLayer->getFillExtrusionOpacityTransition(); + XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay); + XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration); + + MGLTransition fillExtrusionOpacityTransition = layer.fillExtrusionOpacityTransition; + XCTAssertEqual(fillExtrusionOpacityTransition.delay, transitionTest.delay); + XCTAssertEqual(fillExtrusionOpacityTransition.duration, transitionTest.duration); + } + + // fill-extrusion-pattern + { + XCTAssertTrue(rawLayer->getFillExtrusionPattern().isUndefined(), + @"fill-extrusion-pattern should be unset initially."); + MGLStyleValue<NSString *> *defaultStyleValue = layer.fillExtrusionPattern; + + MGLStyleValue<NSString *> *constantStyleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Fill Extrusion Pattern"]; + layer.fillExtrusionPattern = constantStyleValue; + mbgl::style::PropertyValue<std::string> propertyValue = { "Fill Extrusion Pattern" }; + XCTAssertEqual(rawLayer->getFillExtrusionPattern(), propertyValue, + @"Setting fillExtrusionPattern to a constant value should update fill-extrusion-pattern."); + XCTAssertEqualObjects(layer.fillExtrusionPattern, constantStyleValue, + @"fillExtrusionPattern should round-trip constant values."); + + MGLStyleValue<NSString *> * functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; + layer.fillExtrusionPattern = functionStyleValue; + + mbgl::style::IntervalStops<std::string> intervalStops = { {{18, "Fill Extrusion Pattern"}} }; + propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops }; + + XCTAssertEqual(rawLayer->getFillExtrusionPattern(), propertyValue, + @"Setting fillExtrusionPattern to a camera function should update fill-extrusion-pattern."); + XCTAssertEqualObjects(layer.fillExtrusionPattern, functionStyleValue, + @"fillExtrusionPattern should round-trip camera functions."); + + + + layer.fillExtrusionPattern = nil; + XCTAssertTrue(rawLayer->getFillExtrusionPattern().isUndefined(), + @"Unsetting fillExtrusionPattern should return fill-extrusion-pattern to the default value."); + XCTAssertEqualObjects(layer.fillExtrusionPattern, defaultStyleValue, + @"fillExtrusionPattern should return the default value after being unset."); + + functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; + XCTAssertThrowsSpecificNamed(layer.fillExtrusionPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; + XCTAssertThrowsSpecificNamed(layer.fillExtrusionPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + // Transition property test + layer.fillExtrusionPatternTransition = transitionTest; + auto toptions = rawLayer->getFillExtrusionPatternTransition(); + XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay); + XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration); + + MGLTransition fillExtrusionPatternTransition = layer.fillExtrusionPatternTransition; + XCTAssertEqual(fillExtrusionPatternTransition.delay, transitionTest.delay); + XCTAssertEqual(fillExtrusionPatternTransition.duration, transitionTest.duration); + } + + // fill-extrusion-translate + { + XCTAssertTrue(rawLayer->getFillExtrusionTranslate().isUndefined(), + @"fill-extrusion-translate should be unset initially."); + MGLStyleValue<NSValue *> *defaultStyleValue = layer.fillExtrusionTranslation; + + MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue: +#if TARGET_OS_IPHONE + [NSValue valueWithCGVector:CGVectorMake(1, 1)] +#else + [NSValue valueWithMGLVector:CGVectorMake(1, -1)] +#endif + ]; + layer.fillExtrusionTranslation = constantStyleValue; + mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } }; + XCTAssertEqual(rawLayer->getFillExtrusionTranslate(), propertyValue, + @"Setting fillExtrusionTranslation to a constant value should update fill-extrusion-translate."); + XCTAssertEqualObjects(layer.fillExtrusionTranslation, constantStyleValue, + @"fillExtrusionTranslation should round-trip constant values."); + + MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; + layer.fillExtrusionTranslation = functionStyleValue; + + mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} }; + propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; + + XCTAssertEqual(rawLayer->getFillExtrusionTranslate(), propertyValue, + @"Setting fillExtrusionTranslation to a camera function should update fill-extrusion-translate."); + XCTAssertEqualObjects(layer.fillExtrusionTranslation, functionStyleValue, + @"fillExtrusionTranslation should round-trip camera functions."); + + + + layer.fillExtrusionTranslation = nil; + XCTAssertTrue(rawLayer->getFillExtrusionTranslate().isUndefined(), + @"Unsetting fillExtrusionTranslation should return fill-extrusion-translate to the default value."); + XCTAssertEqualObjects(layer.fillExtrusionTranslation, defaultStyleValue, + @"fillExtrusionTranslation should return the default value after being unset."); + + functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; + XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; + XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + } + + // fill-extrusion-translate-anchor + { + XCTAssertTrue(rawLayer->getFillExtrusionTranslateAnchor().isUndefined(), + @"fill-extrusion-translate-anchor should be unset initially."); + MGLStyleValue<NSValue *> *defaultStyleValue = layer.fillExtrusionTranslationAnchor; + + MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLFillExtrusionTranslationAnchor:MGLFillExtrusionTranslationAnchorViewport]]; + layer.fillExtrusionTranslationAnchor = constantStyleValue; + mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport }; + XCTAssertEqual(rawLayer->getFillExtrusionTranslateAnchor(), propertyValue, + @"Setting fillExtrusionTranslationAnchor to a constant value should update fill-extrusion-translate-anchor."); + XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, constantStyleValue, + @"fillExtrusionTranslationAnchor should round-trip constant values."); + + MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; + layer.fillExtrusionTranslationAnchor = functionStyleValue; + + mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = { {{18, mbgl::style::TranslateAnchorType::Viewport}} }; + propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops }; + + XCTAssertEqual(rawLayer->getFillExtrusionTranslateAnchor(), propertyValue, + @"Setting fillExtrusionTranslationAnchor to a camera function should update fill-extrusion-translate-anchor."); + XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, functionStyleValue, + @"fillExtrusionTranslationAnchor should round-trip camera functions."); + + + + layer.fillExtrusionTranslationAnchor = nil; + XCTAssertTrue(rawLayer->getFillExtrusionTranslateAnchor().isUndefined(), + @"Unsetting fillExtrusionTranslationAnchor should return fill-extrusion-translate-anchor to the default value."); + XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, defaultStyleValue, + @"fillExtrusionTranslationAnchor should return the default value after being unset."); + + functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; + XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; + XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + } +} + +- (void)testPropertyNames { + [self testPropertyName:@"fill-extrusion-base" isBoolean:NO]; + [self testPropertyName:@"fill-extrusion-color" isBoolean:NO]; + [self testPropertyName:@"fill-extrusion-height" isBoolean:NO]; + [self testPropertyName:@"fill-extrusion-opacity" isBoolean:NO]; + [self testPropertyName:@"fill-extrusion-pattern" isBoolean:NO]; + [self testPropertyName:@"fill-extrusion-translation" isBoolean:NO]; + [self testPropertyName:@"fill-extrusion-translation-anchor" isBoolean:NO]; +} + +- (void)testValueAdditions { + XCTAssertEqual([NSValue valueWithMGLFillExtrusionTranslationAnchor:MGLFillExtrusionTranslationAnchorMap].MGLFillExtrusionTranslationAnchorValue, MGLFillExtrusionTranslationAnchorMap); + XCTAssertEqual([NSValue valueWithMGLFillExtrusionTranslationAnchor:MGLFillExtrusionTranslationAnchorViewport].MGLFillExtrusionTranslationAnchorValue, MGLFillExtrusionTranslationAnchorViewport); +} + +@end diff --git a/platform/darwin/test/MGLStyleLayerTests.mm.ejs b/platform/darwin/test/MGLStyleLayerTests.mm.ejs index a405ae58c4..5fdfc3d44e 100644 --- a/platform/darwin/test/MGLStyleLayerTests.mm.ejs +++ b/platform/darwin/test/MGLStyleLayerTests.mm.ejs @@ -11,7 +11,7 @@ #import "MGLStyleLayer_Private.h" -#include <mbgl/style/layers/<%- type %>_layer.hpp> +#include <mbgl/style/layers/<%- type.replace('-', '_') %>_layer.hpp> #include <mbgl/style/transition_options.hpp> @interface MGL<%- camelize(type) %>LayerTests : MGLStyleLayerTests diff --git a/platform/glfw/glfw_view.cpp b/platform/glfw/glfw_view.cpp index f4ade26885..26c946ed76 100644 --- a/platform/glfw/glfw_view.cpp +++ b/platform/glfw/glfw_view.cpp @@ -22,7 +22,7 @@ GLFWView::GLFWView(bool fullscreen_, bool benchmark_) : fullscreen(fullscreen_), benchmark(benchmark_) { glfwSetErrorCallback(glfwError); - std::srand(std::time(nullptr)); + std::srand(static_cast<unsigned int>(std::time(nullptr))); if (!glfwInit()) { mbgl::Log::Error(mbgl::Event::OpenGL, "failed to initialize glfw"); @@ -542,7 +542,7 @@ void showDebugImage(std::string name, const char *data, size_t width, size_t hei static GLFWwindow *debugWindow = nullptr; if (!debugWindow) { - debugWindow = glfwCreateWindow(width, height, name.c_str(), nullptr, nullptr); + debugWindow = glfwCreateWindow(static_cast<int>(width), static_cast<int>(height), name.c_str(), nullptr, nullptr); if (!debugWindow) { glfwTerminate(); fprintf(stderr, "Failed to initialize window\n"); @@ -552,7 +552,7 @@ void showDebugImage(std::string name, const char *data, size_t width, size_t hei GLFWwindow *currentWindow = glfwGetCurrentContext(); - glfwSetWindowSize(debugWindow, width, height); + glfwSetWindowSize(debugWindow, static_cast<int>(width), static_cast<int>(height)); glfwMakeContextCurrent(debugWindow); int fbWidth, fbHeight; @@ -573,7 +573,7 @@ void showColorDebugImage(std::string name, const char *data, size_t logicalWidth static GLFWwindow *debugWindow = nullptr; if (!debugWindow) { - debugWindow = glfwCreateWindow(logicalWidth, logicalHeight, name.c_str(), nullptr, nullptr); + debugWindow = glfwCreateWindow(static_cast<int>(logicalWidth), static_cast<int>(logicalHeight), name.c_str(), nullptr, nullptr); if (!debugWindow) { glfwTerminate(); fprintf(stderr, "Failed to initialize window\n"); @@ -583,7 +583,7 @@ void showColorDebugImage(std::string name, const char *data, size_t logicalWidth GLFWwindow *currentWindow = glfwGetCurrentContext(); - glfwSetWindowSize(debugWindow, logicalWidth, logicalHeight); + glfwSetWindowSize(debugWindow, static_cast<int>(logicalWidth), static_cast<int>(logicalHeight)); glfwMakeContextCurrent(debugWindow); int fbWidth, fbHeight; diff --git a/platform/glfw/main.cpp b/platform/glfw/main.cpp index 172b7e3284..59d2ce3ec6 100644 --- a/platform/glfw/main.cpp +++ b/platform/glfw/main.cpp @@ -199,4 +199,4 @@ int main(int argc, char *argv[]) { view = nullptr; return 0; -} +}
\ No newline at end of file diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index d72076274c..fb35070a79 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -17,6 +17,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Xcode 8.0 or higher is now recommended for using this SDK. ([#8775](https://github.com/mapbox/mapbox-gl-native/pull/8775)) * Updated MGLMapView’s logo view to display [the new Mapbox logo](https://www.mapbox.com/blog/new-mapbox-logo/). ([#8771](https://github.com/mapbox/mapbox-gl-native/pull/8771)) * Fixed a crash or console spew when MGLMapView is initialized with a frame smaller than 64 points wide by 64 points tall. ([#8562](https://github.com/mapbox/mapbox-gl-native/pull/8562)) +* Added support for 3D extrusion of buildings and other polygonal features via the `MGLFillExtrusionStyleLayer` class and the `fill-extrusion` layer type in style JSON. ([#8431](https://github.com/mapbox/mapbox-gl-native/pull/8431)) * The error passed into `-[MGLMapViewDelegate mapViewDidFailLoadingMap:withError:]` now includes a more specific description and failure reason. ([#8418](https://github.com/mapbox/mapbox-gl-native/pull/8418)) * Fixed an issue where gesture recognizers associated with map view interactivity were not disabled when their related interactions were disabled. ([#8304](https://github.com/mapbox/mapbox-gl-native/pull/8304)) * Fixed an issue where re-adding a layer that had been previously removed from a style would reset its paint properties. Moved initializers for `MGLTileSource`, `MGLStyleLayer`, and `MGLForegroundStyleLayer` to their concrete subclasses; because these classes were already intended for initialization only via concrete subclasses, this should have no developer impact. ([#8626](https://github.com/mapbox/mapbox-gl-native/pull/8626)) diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md index ed9018b121..fee4d70b34 100644 --- a/platform/ios/docs/guides/For Style Authors.md +++ b/platform/ios/docs/guides/For Style Authors.md @@ -181,6 +181,7 @@ In style JSON | In the SDK `background` | `MGLBackgroundStyleLayer` `circle` | `MGLCircleStyleLayer` `fill` | `MGLFillStyleLayer` +`fill-extrusion` | `MGLFillExtrusionStyleLayer` `line` | `MGLLineStyleLayer` `raster` | `MGLRasterStyleLayer` `symbol` | `MGLSymbolStyleLayer` @@ -206,6 +207,13 @@ In style JSON | In Objective-C | In Swift `fill-translate` | `MGLFillStyleLayer.fillTranslation` | `MGLFillStyleLayer.fillTranslation` `fill-translate-anchor` | `MGLFillStyleLayer.fillTranslationAnchor` | `MGLFillStyleLayer.fillTranslationAnchor` +### Fill extrusion style layers + +In style JSON | In Objective-C | In Swift +--------------|----------------|--------- +`fill-extrusion-translate` | `MGLFillExtrusionStyleLayer.fillExtrusionTranslation` | `MGLFillExtrusionStyleLayer.fillExtrusionTranslation` +`fill-extrusion-translate-anchor` | `MGLFillExtrusionStyleLayer.fillExtrusionTranslationAnchor` | `MGLFillExtrusionStyleLayer.fillExtrusionTranslationAnchor` + ### Line style layers In style JSON | In Objective-C | In Swift diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 1d25a9679e..a071022844 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -453,6 +453,11 @@ DD4823761D94AE6C00EB71B7 /* line_filter_style.json in Resources */ = {isa = PBXBuildFile; fileRef = DD4823731D94AE6C00EB71B7 /* line_filter_style.json */; }; DD4823771D94AE6C00EB71B7 /* numeric_filter_style.json in Resources */ = {isa = PBXBuildFile; fileRef = DD4823741D94AE6C00EB71B7 /* numeric_filter_style.json */; }; DD58A4C61D822BD000E1F038 /* MGLExpressionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DD58A4C51D822BD000E1F038 /* MGLExpressionTests.mm */; }; + FA68F14A1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = FA68F1481E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FA68F14B1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = FA68F1481E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FA68F14D1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA68F1491E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm */; }; + FA68F14E1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA68F1491E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm */; }; + FAE1CDCB1E9D79CB00C40B5B /* MGLFillExtrusionStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = FAE1CDC81E9D79C600C40B5B /* MGLFillExtrusionStyleLayerTests.mm */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -914,6 +919,9 @@ DD4823731D94AE6C00EB71B7 /* line_filter_style.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = line_filter_style.json; sourceTree = "<group>"; }; DD4823741D94AE6C00EB71B7 /* numeric_filter_style.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = numeric_filter_style.json; sourceTree = "<group>"; }; DD58A4C51D822BD000E1F038 /* MGLExpressionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLExpressionTests.mm; path = ../../darwin/test/MGLExpressionTests.mm; sourceTree = "<group>"; }; + FA68F1481E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFillExtrusionStyleLayer.h; sourceTree = "<group>"; }; + FA68F1491E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFillExtrusionStyleLayer.mm; sourceTree = "<group>"; }; + FAE1CDC81E9D79C600C40B5B /* MGLFillExtrusionStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLFillExtrusionStyleLayerTests.mm; path = ../../darwin/test/MGLFillExtrusionStyleLayerTests.mm; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -990,6 +998,8 @@ 35136D381D42271A00C20EFD /* MGLBackgroundStyleLayer.mm */, 353933F11D3FB753003F57D7 /* MGLCircleStyleLayer.h */, 35136D3B1D42272500C20EFD /* MGLCircleStyleLayer.mm */, + FA68F1481E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h */, + FA68F1491E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm */, 35D13AC11D3D19DD00AFB4E0 /* MGLFillStyleLayer.h */, 35D13AC21D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm */, 3538AA1B1D542239008EC33D /* MGLForegroundStyleLayer.h */, @@ -1061,6 +1071,7 @@ DA2DBBCC1D51E80400D38FF9 /* MGLStyleLayerTests.h */, DA2DBBCD1D51E80400D38FF9 /* MGLStyleLayerTests.m */, DA3C6FF21E2859E700F962BE /* test-Bridging-Header.h */, + FAE1CDC81E9D79C600C40B5B /* MGLFillExtrusionStyleLayerTests.mm */, 3575797F1D501E09000B822E /* MGLFillStyleLayerTests.mm */, 357579821D502AE6000B822E /* MGLRasterStyleLayerTests.mm */, 357579841D502AF5000B822E /* MGLSymbolStyleLayerTests.mm */, @@ -1636,6 +1647,7 @@ 40CF6DBB1DAC3C6600A4D18B /* MGLShape_Private.h in Headers */, 4018B1CA1CDC288E00F666AF /* MGLAnnotationView.h in Headers */, 35E79F201D41266300957B9E /* MGLStyleLayer_Private.h in Headers */, + FA68F14A1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */, 353933FB1D3FB7C0003F57D7 /* MGLRasterStyleLayer.h in Headers */, DA8847EF1CBAFA5100AB86E3 /* MGLAccountManager.h in Headers */, DA8848511CBAFB9800AB86E3 /* MGLAPIClient.h in Headers */, @@ -1719,6 +1731,7 @@ 35B82BF91D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */, DA35A2CA1CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */, 350098BC1D480108004B2AF0 /* MGLVectorSource.h in Headers */, + FA68F14B1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */, 353933FC1D3FB7C0003F57D7 /* MGLRasterStyleLayer.h in Headers */, 3566C76D1D4A8DFA008152BC /* MGLRasterSource.h in Headers */, DAED38641D62D0FC00D7640F /* NSURL+MGLAdditions.h in Headers */, @@ -2151,6 +2164,7 @@ DA2E88621CC0382C00F24E7B /* MGLOfflinePackTests.m in Sources */, 55E2AD131E5B125400E8C587 /* MGLOfflineStorageTests.mm in Sources */, 920A3E5D1E6F995200C16EFC /* MGLSourceQueryTests.m in Sources */, + FAE1CDCB1E9D79CB00C40B5B /* MGLFillExtrusionStyleLayerTests.mm in Sources */, DA35A2AA1CCA058D00E826B2 /* MGLCoordinateFormatterTests.m in Sources */, 357579831D502AE6000B822E /* MGLRasterStyleLayerTests.mm in Sources */, 353D23961D0B0DFE002BE09D /* MGLAnnotationViewTests.m in Sources */, @@ -2216,6 +2230,7 @@ DA88481F1CBAFA6200AB86E3 /* MGLMultiPoint.mm in Sources */, DA88482B1CBAFA6200AB86E3 /* MGLTypes.m in Sources */, 4018B1C71CDC287F00F666AF /* MGLAnnotationView.mm in Sources */, + FA68F14D1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm in Sources */, 404C26E41D89B877000AA13D /* MGLTileSource.mm in Sources */, 355AE0011E9281DA00F3939D /* MGLScaleBar.mm in Sources */, DA88481D1CBAFA6200AB86E3 /* MGLMapCamera.mm in Sources */, @@ -2295,6 +2310,7 @@ DAA4E4301CBB730400178DFB /* MGLLocationManager.m in Sources */, DAA4E4321CBB730400178DFB /* MGLMapView.mm in Sources */, DAA4E41E1CBB730400178DFB /* MGLMapCamera.mm in Sources */, + FA68F14E1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm in Sources */, 404C26E51D89B877000AA13D /* MGLTileSource.mm in Sources */, 355AE0021E9281DA00F3939D /* MGLScaleBar.mm in Sources */, 4018B1C81CDC287F00F666AF /* MGLAnnotationView.mm in Sources */, diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml index 87af09a9b9..e0ce29beba 100644 --- a/platform/ios/jazzy.yml +++ b/platform/ios/jazzy.yml @@ -85,6 +85,7 @@ custom_categories: - MGLVectorStyleLayer - MGLCircleStyleLayer - MGLFillStyleLayer + - MGLFillExtrusionStyleLayer - MGLLineStyleLayer - MGLSymbolStyleLayer - name: Offline Maps diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index 2623777d8f..9a9dc702ca 100644 --- a/platform/ios/src/Mapbox.h +++ b/platform/ios/src/Mapbox.h @@ -38,6 +38,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLStyleLayer.h" #import "MGLForegroundStyleLayer.h" #import "MGLVectorStyleLayer.h" +#import "MGLFillExtrusionStyleLayer.h" #import "MGLFillStyleLayer.h" #import "MGLLineStyleLayer.h" #import "MGLSymbolStyleLayer.h" diff --git a/platform/ios/vendor/SMCalloutView b/platform/ios/vendor/SMCalloutView -Subproject d6ecaba377c9f963aef630faf86e3b8f8cdb88d +Subproject 2aede5d8d1577101bf18405246220e7a710df60 diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 0e3f9b8fbf..73fa638692 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -14,6 +14,7 @@ * Updated MGLMapView’s logo view to display [the new Mapbox logo](https://www.mapbox.com/blog/new-mapbox-logo/). ([#8771](https://github.com/mapbox/mapbox-gl-native/pull/8771)) * Fixed an issue causing attribution button text to appear blue instead of black. ([#8701](https://github.com/mapbox/mapbox-gl-native/pull/8701)) * Fixed a crash or console spew when MGLMapView is initialized with a frame smaller than 64 points wide by 64 points tall. ([#8562](https://github.com/mapbox/mapbox-gl-native/pull/8562)) +* Added support for 3D extrusion of buildings and other polygonal features via the `MGLFillExtrusionStyleLayer` class and the `fill-extrusion` layer type in style JSON. ([#8431](https://github.com/mapbox/mapbox-gl-native/pull/8431)) * The error passed into `-[MGLMapViewDelegate mapViewDidFailLoadingMap:withError:]` now includes a more specific description and failure reason. ([#8418](https://github.com/mapbox/mapbox-gl-native/pull/8418)) * Fixed an issue where re-adding a layer that had been previously removed from a style would reset its paint properties. Moved initializers for `MGLTileSource`, `MGLStyleLayer`, and `MGLForegroundStyleLayer` to their concrete subclasses; because these classes were already intended for initialization only via concrete subclasses, this should have no developer impact. ([#8626](https://github.com/mapbox/mapbox-gl-native/pull/8626)) diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md index 9ed69ac9ea..b9163582b4 100644 --- a/platform/macos/docs/guides/For Style Authors.md +++ b/platform/macos/docs/guides/For Style Authors.md @@ -170,6 +170,7 @@ In style JSON | In the SDK `background` | `MGLBackgroundStyleLayer` `circle` | `MGLCircleStyleLayer` `fill` | `MGLFillStyleLayer` +`fill-extrusion` | `MGLFillExtrusionStyleLayer` `line` | `MGLLineStyleLayer` `raster` | `MGLRasterStyleLayer` `symbol` | `MGLSymbolStyleLayer` @@ -195,6 +196,13 @@ In style JSON | In Objective-C | In Swift `fill-translate` | `MGLFillStyleLayer.fillTranslation` | `MGLFillStyleLayer.fillTranslation` `fill-translate-anchor` | `MGLFillStyleLayer.fillTranslationAnchor` | `MGLFillStyleLayer.fillTranslationAnchor` +### Fill extrusion style layers + +In style JSON | In Objective-C | In Swift +--------------|----------------|--------- +`fill-extrusion-translate` | `MGLFillExtrusionStyleLayer.fillExtrusionTranslation` | `MGLFillExtrusionStyleLayer.fillExtrusionTranslation` +`fill-extrusion-translate-anchor` | `MGLFillExtrusionStyleLayer.fillExtrusionTranslationAnchor` | `MGLFillExtrusionStyleLayer.fillExtrusionTranslationAnchor` + ### Line style layers In style JSON | In Objective-C | In Swift diff --git a/platform/macos/jazzy.yml b/platform/macos/jazzy.yml index 1f0e2fbc74..3c24b351cc 100644 --- a/platform/macos/jazzy.yml +++ b/platform/macos/jazzy.yml @@ -71,6 +71,7 @@ custom_categories: - MGLVectorStyleLayer - MGLCircleStyleLayer - MGLFillStyleLayer + - MGLFillExtrusionStyleLayer - MGLLineStyleLayer - MGLSymbolStyleLayer - name: Offline Maps diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index 175629d126..97adf445e1 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -139,6 +139,9 @@ DA8F25B21D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8F25A61D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h */; }; DA8F25B31D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25A71D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.mm */; }; DAA48EFD1D6A4731006A7E36 /* StyleLayerIconTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = DAA48EFC1D6A4731006A7E36 /* StyleLayerIconTransformer.m */; }; + DAA998FB1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DAA998F91E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DAA998FC1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAA998FA1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm */; }; + DAA999011E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAA999001E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm */; }; DAB2CCE51DF632ED001B2FE1 /* LimeGreenStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB2CCE41DF632ED001B2FE1 /* LimeGreenStyleLayer.m */; }; DAC2ABC51CC6D343006D18C4 /* MGLAnnotationImage_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */; }; DACB0C391E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DACB0C381E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m */; }; @@ -453,6 +456,9 @@ DAA32CC11E4C4F93006F8D24 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; }; DAA48EFB1D6A4731006A7E36 /* StyleLayerIconTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StyleLayerIconTransformer.h; sourceTree = "<group>"; }; DAA48EFC1D6A4731006A7E36 /* StyleLayerIconTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StyleLayerIconTransformer.m; sourceTree = "<group>"; }; + DAA998F91E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFillExtrusionStyleLayer.h; sourceTree = "<group>"; }; + DAA998FA1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFillExtrusionStyleLayer.mm; sourceTree = "<group>"; }; + DAA999001E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFillExtrusionStyleLayerTests.mm; sourceTree = "<group>"; }; DAB2CCE31DF632ED001B2FE1 /* LimeGreenStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LimeGreenStyleLayer.h; sourceTree = "<group>"; }; DAB2CCE41DF632ED001B2FE1 /* LimeGreenStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LimeGreenStyleLayer.m; sourceTree = "<group>"; }; DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationImage_Private.h; sourceTree = "<group>"; }; @@ -599,6 +605,8 @@ DA8F25861D51C9E10010E6B5 /* MGLBackgroundStyleLayer.mm */, 3527428B1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.h */, 3527428C1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.mm */, + DAA998F91E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h */, + DAA998FA1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm */, 35602BF81D3EA99F0050646F /* MGLFillStyleLayer.h */, 35602BF91D3EA99F0050646F /* MGLFillStyleLayer.mm */, 35602BFD1D3EA9B40050646F /* MGLForegroundStyleLayer.h */, @@ -785,6 +793,7 @@ 40E1601A1DF216E6005EA6D9 /* MGLStyleLayerTests.h */, 40E1601B1DF216E6005EA6D9 /* MGLStyleLayerTests.m */, DA2207BA1DC076930002F84D /* test-Bridging-Header.h */, + DAA999001E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm */, DA8F25741D51C5F40010E6B5 /* MGLFillStyleLayerTests.mm */, DA8F25751D51C5F40010E6B5 /* MGLRasterStyleLayerTests.mm */, DA8F25761D51C5F40010E6B5 /* MGLSymbolStyleLayerTests.mm */, @@ -1078,6 +1087,7 @@ 3527428D1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.h in Headers */, DA00FC8A1D5EEAC3009AABC8 /* MGLAttributionInfo.h in Headers */, DAE6C3B21CC31EF300DB3429 /* MGLAttributionButton.h in Headers */, + DAA998FB1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h in Headers */, 40B77E451DB11BC9003DA2FE /* NSArray+MGLAdditions.h in Headers */, 35C5D8471D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.h in Headers */, DAE6C3A31CC31E9400DB3429 /* MGLAnnotationImage.h in Headers */, @@ -1405,6 +1415,7 @@ DAE6C3911CC31E2A00DB3429 /* MGLPolygon.mm in Sources */, 35C6DF851E214C0400ACA483 /* MGLDistanceFormatter.m in Sources */, DAE6C39B1CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.m in Sources */, + DAA998FC1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm in Sources */, DAE6C38F1CC31E2A00DB3429 /* MGLOfflineStorage.mm in Sources */, DAED38601D62CED700D7640F /* NSURL+MGLAdditions.m in Sources */, 35C5D84A1D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.mm in Sources */, @@ -1449,6 +1460,7 @@ 4031ACFC1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift in Sources */, 4031AD031E9FD6AA00A3EA26 /* MGLSDKTestHelpers.swift in Sources */, DA87A9A71DCACC5000810D09 /* MGLBackgroundStyleLayerTests.mm in Sources */, + DAA999011E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm in Sources */, DA29875A1E1A4290002299F5 /* MGLDocumentationExampleTests.swift in Sources */, DAE6C3D31CC34C9900DB3429 /* MGLOfflinePackTests.m in Sources */, DA87A9A51DCACC5000810D09 /* MGLLineStyleLayerTests.mm in Sources */, diff --git a/platform/macos/src/Mapbox.h b/platform/macos/src/Mapbox.h index 79ecfb21dc..dcb5b50b8f 100644 --- a/platform/macos/src/Mapbox.h +++ b/platform/macos/src/Mapbox.h @@ -37,6 +37,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLForegroundStyleLayer.h" #import "MGLVectorStyleLayer.h" #import "MGLFillStyleLayer.h" +#import "MGLFillExtrusionStyleLayer.h" #import "MGLLineStyleLayer.h" #import "MGLSymbolStyleLayer.h" #import "MGLRasterStyleLayer.h" diff --git a/scripts/generate-shaders.js b/scripts/generate-shaders.js index cffe9d3854..2f1f4c2f9c 100755 --- a/scripts/generate-shaders.js +++ b/scripts/generate-shaders.js @@ -47,7 +47,10 @@ ${fragmentPrelude} 'circle', 'collision_box', 'debug', + 'extrusion_texture', 'fill', + 'fill_extrusion', + 'fill_extrusion_pattern', 'fill_outline', 'fill_outline_pattern', 'fill_pattern', diff --git a/scripts/style-code.js b/scripts/style-code.js index 156934a240..70914c5fb6 100644 --- a/scripts/style-code.js +++ b/scripts/style-code.js @@ -22,6 +22,10 @@ global.snakeCaseUpper = function snakeCaseUpper(str) { return str.replace(/-/g, "_").toUpperCase(); }; +global.unhyphenate = function (str) { + return str.replace(/-/g, " "); +}; + global.writeIfModified = function(filename, newContent) { try { const oldContent = fs.readFileSync(filename, 'utf8'); diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index 565fe33771..1a5a6a2289 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -250,6 +250,7 @@ UniqueRenderbuffer Context::createRenderbuffer(const RenderbufferType type, cons bindRenderbuffer = renderbuffer; MBGL_CHECK_ERROR( glRenderbufferStorage(GL_RENDERBUFFER, static_cast<GLenum>(type), size.width, size.height)); + bindRenderbuffer = 0; return renderbuffer; } @@ -384,6 +385,17 @@ Framebuffer Context::createFramebuffer(const Texture& color) { return { color.size, std::move(fbo) }; } +Framebuffer +Context::createFramebuffer(const Texture& color, + const Renderbuffer<RenderbufferType::DepthComponent>& depthTarget) { + auto fbo = createFramebuffer(); + bindFramebuffer = fbo; + MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color.texture, 0)); + MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthTarget.renderbuffer)); + checkFramebuffer(); + return { depthTarget.size, std::move(fbo) }; +} + UniqueTexture Context::createTexture(const Size size, const void* data, TextureFormat format, TextureUnit unit) { auto obj = createTexture(); diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 14af299baa..56c0618989 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -81,7 +81,9 @@ public: template <RenderbufferType type> Renderbuffer<type> createRenderbuffer(const Size size) { - static_assert(type == RenderbufferType::RGBA || type == RenderbufferType::DepthStencil, + static_assert(type == RenderbufferType::RGBA || + type == RenderbufferType::DepthStencil || + type == RenderbufferType::DepthComponent, "invalid renderbuffer type"); return { size, createRenderbuffer(type, size) }; } @@ -92,6 +94,8 @@ public: Framebuffer createFramebuffer(const Texture&, const Renderbuffer<RenderbufferType::DepthStencil>&); Framebuffer createFramebuffer(const Texture&); + Framebuffer createFramebuffer(const Texture&, + const Renderbuffer<RenderbufferType::DepthComponent>&); template <typename Image, TextureFormat format = Image::channels == 4 ? TextureFormat::RGBA diff --git a/src/mbgl/gl/types.hpp b/src/mbgl/gl/types.hpp index 7d436693c9..0595419674 100644 --- a/src/mbgl/gl/types.hpp +++ b/src/mbgl/gl/types.hpp @@ -37,6 +37,11 @@ enum class DataType : uint32_t { enum class RenderbufferType : uint32_t { RGBA = 0x8058, DepthStencil = 0x88F0, +#if not MBGL_USE_GLES2 + DepthComponent = 0x1902, // GL_DEPTH_COMPONENT +#else + DepthComponent = 0x81A5, // GL_DEPTH_COMPONENT16 +#endif // MBGL_USE_GLES2 }; enum class TextureMipMap : bool { No = false, Yes = true }; diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index dd33d0b170..b5a1af172a 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -9,6 +9,7 @@ #include <mbgl/style/style.hpp> #include <mbgl/style/source.hpp> #include <mbgl/style/layer.hpp> +#include <mbgl/style/light.hpp> #include <mbgl/style/observer.hpp> #include <mbgl/style/transition_options.hpp> #include <mbgl/style/update_parameters.hpp> @@ -978,6 +979,22 @@ const style::Image* Map::getImage(const std::string& id) { return nullptr; } +void Map::setLight(std::unique_ptr<style::Light> light) { + if (!impl->style) { + return; + } + + impl->style->light = std::move(light); +} + +style::Light* Map::getLight() { + if (!impl->style) { + return nullptr; + } + + return impl->style->light.get(); +} + #pragma mark - Defaults std::string Map::getStyleName() const { diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index fe90d4b2e4..8c3c70feec 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -2,6 +2,7 @@ #include <mbgl/tile/tile_id.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/interpolate.hpp> +#include <mbgl/util/projection.hpp> #include <mbgl/math/log2.hpp> #include <mbgl/math/clamp.hpp> @@ -26,7 +27,7 @@ void TransformState::matrixFor(mat4& matrix, const UnwrappedTileID& tileID) cons matrix::scale(matrix, matrix, s / util::EXTENT, s / util::EXTENT, 1); } -void TransformState::getProjMatrix(mat4& projMatrix) const { +void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ) const { if (size.isEmpty()) { return; } @@ -45,7 +46,7 @@ void TransformState::getProjMatrix(mat4& projMatrix) const { // Add a bit extra to avoid precision problems when a fragment's distance is exactly `furthestDistance` const double farZ = furthestDistance * 1.01; - matrix::perspective(projMatrix, getFieldOfView(), double(size.width) / size.height, 1, farZ); + matrix::perspective(projMatrix, getFieldOfView(), double(size.width) / size.height, nearZ, farZ); const bool flippedY = viewportMode == ViewportMode::FlippedY; matrix::scale(projMatrix, projMatrix, 1, flippedY ? 1 : -1, 1); @@ -64,6 +65,9 @@ void TransformState::getProjMatrix(mat4& projMatrix) const { matrix::translate(projMatrix, projMatrix, pixel_x() - size.width / 2.0f, pixel_y() - size.height / 2.0f, 0); + + matrix::scale(projMatrix, projMatrix, 1, 1, + 1.0 / Projection::getMetersPerPixelAtLatitude(getLatLng(LatLng::Unwrapped).latitude(), getZoom())); } #pragma mark - Dimensions @@ -233,7 +237,6 @@ bool TransformState::isGestureInProgress() const { return gestureInProgress; } - #pragma mark - Projection double TransformState::zoomScale(double zoom) const { diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp index 85d5d96700..d0bf455ead 100644 --- a/src/mbgl/map/transform_state.hpp +++ b/src/mbgl/map/transform_state.hpp @@ -24,7 +24,7 @@ public: // Matrix void matrixFor(mat4&, const UnwrappedTileID&) const; - void getProjMatrix(mat4& matrix) const; + void getProjMatrix(mat4& matrix, uint16_t nearZ = 1) const; // Dimensions Size getSize() const; diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp index e9ca18927e..cfd6a629de 100644 --- a/src/mbgl/programs/attributes.hpp +++ b/src/mbgl/programs/attributes.hpp @@ -24,6 +24,8 @@ MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_pos); MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_extrude); MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_pos_offset); MBGL_DEFINE_ATTRIBUTE(uint16_t, 2, a_texture_pos); +MBGL_DEFINE_ATTRIBUTE(int16_t, 3, a_normal); +MBGL_DEFINE_ATTRIBUTE(uint16_t, 1, a_edgedistance); template <typename T, std::size_t N> struct a_data { diff --git a/src/mbgl/programs/extrusion_texture_program.cpp b/src/mbgl/programs/extrusion_texture_program.cpp new file mode 100644 index 0000000000..afda4384b7 --- /dev/null +++ b/src/mbgl/programs/extrusion_texture_program.cpp @@ -0,0 +1,7 @@ +#include <mbgl/programs/extrusion_texture_program.hpp> + +namespace mbgl { + +static_assert(sizeof(ExtrusionTextureLayoutVertex) == 4, "expected ExtrusionTextureLayoutVertex size"); + +} // namespace mbgl diff --git a/src/mbgl/programs/extrusion_texture_program.hpp b/src/mbgl/programs/extrusion_texture_program.hpp new file mode 100644 index 0000000000..1519aa095d --- /dev/null +++ b/src/mbgl/programs/extrusion_texture_program.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include <mbgl/programs/program.hpp> +#include <mbgl/programs/attributes.hpp> +#include <mbgl/programs/uniforms.hpp> +#include <mbgl/shaders/extrusion_texture.hpp> +#include <mbgl/util/geometry.hpp> + +namespace mbgl { + +class ExtrusionTextureProgram : public Program< + shaders::extrusion_texture, + gl::Triangle, + gl::Attributes<attributes::a_pos>, + gl::Uniforms< + uniforms::u_matrix, + uniforms::u_world, + uniforms::u_image, + uniforms::u_opacity>, + style::PaintProperties<>> { +public: + using Program::Program; + + static LayoutVertex layoutVertex(Point<int16_t> p) { + return LayoutVertex{ + {{ + p.x, + p.y + }} + }; + } +}; + +using ExtrusionTextureLayoutVertex = ExtrusionTextureProgram::LayoutVertex; +using ExtrusionTextureAttributes = ExtrusionTextureProgram::Attributes; + +} // namespace mbgl diff --git a/src/mbgl/programs/fill_extrusion_program.cpp b/src/mbgl/programs/fill_extrusion_program.cpp new file mode 100644 index 0000000000..67426c8d9d --- /dev/null +++ b/src/mbgl/programs/fill_extrusion_program.cpp @@ -0,0 +1,81 @@ +#include <mbgl/programs/fill_extrusion_program.hpp> +#include <mbgl/sprite/sprite_atlas.hpp> +#include <mbgl/style/cross_faded_property_evaluator.hpp> +#include <mbgl/tile/tile_id.hpp> +#include <mbgl/map/transform_state.hpp> +#include <mbgl/util/mat3.hpp> + +namespace mbgl { + +using namespace style; + +static_assert(sizeof(FillExtrusionLayoutVertex) == 12, "expected FillExtrusionLayoutVertex size"); + +std::array<float, 3> lightColor(const EvaluatedLight& light) { + const auto color = light.get<LightColor>(); + return {{ color.r, color.g, color.b }}; +} + +std::array<float, 3> lightPosition(const EvaluatedLight& light, const TransformState& state) { + auto lightPos = light.get<LightPosition>().getCartesian(); + mat3 lightMat; + matrix::identity(lightMat); + if (light.get<LightAnchor>() == LightAnchorType::Viewport) { + matrix::rotate(lightMat, lightMat, -state.getAngle()); + } + matrix::transformMat3f(lightPos, lightPos, lightMat); + return lightPos; +} + +float lightIntensity(const EvaluatedLight& light) { + return light.get<LightIntensity>(); +} + +FillExtrusionUniforms::Values +FillExtrusionUniforms::values(mat4 matrix, + const TransformState& state, + const EvaluatedLight& light) { + return FillExtrusionUniforms::Values{ + uniforms::u_matrix::Value{ matrix }, + uniforms::u_lightcolor::Value{ lightColor(light) }, + uniforms::u_lightpos::Value{ lightPosition(light, state) }, + uniforms::u_lightintensity::Value{ lightIntensity(light) } + }; +} + +FillExtrusionPatternUniforms::Values +FillExtrusionPatternUniforms::values(mat4 matrix, + const SpriteAtlasElement& a, + const SpriteAtlasElement& b, + const Faded<std::string>& fading, + const UnwrappedTileID& tileID, + const TransformState& state, + const float heightFactor, + const EvaluatedLight& light) { + int32_t tileSizeAtNearestZoom = util::tileSize * state.zoomScale(state.getIntegerZoom() - tileID.canonical.z); + int32_t pixelX = tileSizeAtNearestZoom * (tileID.canonical.x + tileID.wrap * state.zoomScale(tileID.canonical.z)); + int32_t pixelY = tileSizeAtNearestZoom * tileID.canonical.y; + + return FillExtrusionPatternUniforms::Values{ + uniforms::u_matrix::Value{ matrix }, + uniforms::u_pattern_tl_a::Value{ a.tl }, + uniforms::u_pattern_br_a::Value{ a.br }, + uniforms::u_pattern_tl_b::Value{ b.tl }, + uniforms::u_pattern_br_b::Value{ b.br }, + uniforms::u_pattern_size_a::Value{ a.size }, + uniforms::u_pattern_size_b::Value{ b.size }, + uniforms::u_scale_a::Value{ fading.fromScale }, + uniforms::u_scale_b::Value{ fading.toScale }, + uniforms::u_mix::Value{ fading.t }, + uniforms::u_image::Value{ 0 }, + uniforms::u_pixel_coord_upper::Value{ std::array<float, 2>{{ float(pixelX >> 16), float(pixelY >> 16) }} }, + uniforms::u_pixel_coord_lower::Value{ std::array<float, 2>{{ float(pixelX & 0xFFFF), float(pixelY & 0xFFFF) }} }, + uniforms::u_tile_units_to_pixels::Value{ 1.0f / tileID.pixelsToTileUnits(1.0f, state.getIntegerZoom()) }, + uniforms::u_height_factor::Value{ heightFactor }, + uniforms::u_lightcolor::Value{ lightColor(light) }, + uniforms::u_lightpos::Value{ lightPosition(light, state) }, + uniforms::u_lightintensity::Value{ lightIntensity(light) }, + }; +} + +} // namespace mbgl diff --git a/src/mbgl/programs/fill_extrusion_program.hpp b/src/mbgl/programs/fill_extrusion_program.hpp new file mode 100644 index 0000000000..b84e50298c --- /dev/null +++ b/src/mbgl/programs/fill_extrusion_program.hpp @@ -0,0 +1,129 @@ +#pragma once + +#include <mbgl/programs/program.hpp> +#include <mbgl/programs/attributes.hpp> +#include <mbgl/programs/uniforms.hpp> +#include <mbgl/shaders/fill_extrusion.hpp> +#include <mbgl/shaders/fill_extrusion_pattern.hpp> +#include <mbgl/util/geometry.hpp> +#include <mbgl/util/mat4.hpp> +#include <mbgl/util/size.hpp> +#include <mbgl/style/layers/fill_extrusion_layer_properties.hpp> +#include <mbgl/style/style.hpp> +#include <mbgl/style/light_impl.hpp> + +#include <string> + +namespace mbgl { + +class SpriteAtlasElement; +class UnwrappedTileID; +class TransformState; + +namespace style { +template <class> class Faded; +} // namespace style + +namespace uniforms { +MBGL_DEFINE_UNIFORM_VECTOR(float, 3, u_lightpos); +MBGL_DEFINE_UNIFORM_VECTOR(float, 3, u_lightcolor); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_lightintensity); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_height_factor); +} // namespace uniforms + +struct FillExtrusionLayoutAttributes : gl::Attributes< + attributes::a_pos, + attributes::a_normal, + attributes::a_edgedistance> +{}; + +struct FillExtrusionUniforms : gl::Uniforms< + uniforms::u_matrix, + uniforms::u_lightcolor, + uniforms::u_lightpos, + uniforms::u_lightintensity> +{ + static Values values(mat4, + const TransformState&, + const style::EvaluatedLight&); +}; + +struct FillExtrusionPatternUniforms : gl::Uniforms< + uniforms::u_matrix, + uniforms::u_pattern_tl_a, + uniforms::u_pattern_br_a, + uniforms::u_pattern_tl_b, + uniforms::u_pattern_br_b, + uniforms::u_pattern_size_a, + uniforms::u_pattern_size_b, + uniforms::u_scale_a, + uniforms::u_scale_b, + uniforms::u_mix, + uniforms::u_image, + uniforms::u_pixel_coord_upper, + uniforms::u_pixel_coord_lower, + uniforms::u_tile_units_to_pixels, + uniforms::u_height_factor, + uniforms::u_lightcolor, + uniforms::u_lightpos, + uniforms::u_lightintensity> +{ + static Values values(mat4, + const SpriteAtlasElement&, + const SpriteAtlasElement&, + const style::Faded<std::string>&, + const UnwrappedTileID&, + const TransformState&, + const float, + const style::EvaluatedLight&); +}; + +class FillExtrusionProgram : public Program< + shaders::fill_extrusion, + gl::Triangle, + FillExtrusionLayoutAttributes, + FillExtrusionUniforms, + style::FillExtrusionPaintProperties> +{ +public: + using Program::Program; + + static LayoutVertex layoutVertex(Point<int16_t> p, double nx, double ny, double nz, unsigned short t, uint16_t e) { + const auto factor = pow(2, 13); + + return LayoutVertex { + {{ + p.x, + p.y + }}, + {{ + // Multiply normal vector components by 2^14 to pack them into integers + // We pack a bool (`t`) into the x component indicating whether it is an upper or lower vertex + static_cast<int16_t>(floor(nx * factor) * 2 + t), + static_cast<int16_t>(ny * factor * 2), + static_cast<int16_t>(nz * factor * 2) + + }}, + {{ + // The edgedistance attribute is used for wrapping fill_extrusion patterns + e + }} + }; + } +}; + +class FillExtrusionPatternProgram : public Program< + shaders::fill_extrusion_pattern, + gl::Triangle, + FillExtrusionLayoutAttributes, + FillExtrusionPatternUniforms, + style::FillExtrusionPaintProperties> +{ +public: + using Program::Program; +}; + +using FillExtrusionLayoutVertex = FillExtrusionProgram::LayoutVertex; +using FillExtrusionAttributes = FillExtrusionProgram::Attributes; + +} // namespace mbgl diff --git a/src/mbgl/programs/fill_program.hpp b/src/mbgl/programs/fill_program.hpp index 84ca2748d6..bbf5c39fb9 100644 --- a/src/mbgl/programs/fill_program.hpp +++ b/src/mbgl/programs/fill_program.hpp @@ -24,15 +24,6 @@ namespace style { template <class> class Faded; } // namespace style -namespace uniforms { -MBGL_DEFINE_UNIFORM_SCALAR(Size, u_world); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_a); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_b); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_tile_units_to_pixels); -MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pixel_coord_upper); -MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pixel_coord_lower); -} // namespace uniforms - struct FillLayoutAttributes : gl::Attributes< attributes::a_pos> {}; diff --git a/src/mbgl/programs/programs.hpp b/src/mbgl/programs/programs.hpp index dbf5b9e87d..ff6b1cd505 100644 --- a/src/mbgl/programs/programs.hpp +++ b/src/mbgl/programs/programs.hpp @@ -1,7 +1,9 @@ #pragma once #include <mbgl/programs/circle_program.hpp> +#include <mbgl/programs/extrusion_texture_program.hpp> #include <mbgl/programs/fill_program.hpp> +#include <mbgl/programs/fill_extrusion_program.hpp> #include <mbgl/programs/line_program.hpp> #include <mbgl/programs/raster_program.hpp> #include <mbgl/programs/symbol_program.hpp> @@ -15,7 +17,10 @@ class Programs { public: Programs(gl::Context& context, const ProgramParameters& programParameters) : circle(context, programParameters), + extrusionTexture(context, programParameters), fill(context, programParameters), + fillExtrusion(context, programParameters), + fillExtrusionPattern(context, programParameters), fillPattern(context, programParameters), fillOutline(context, programParameters), fillOutlinePattern(context, programParameters), @@ -31,7 +36,10 @@ public: } CircleProgram circle; + ExtrusionTextureProgram extrusionTexture; FillProgram fill; + FillExtrusionProgram fillExtrusion; + FillExtrusionPatternProgram fillExtrusionPattern; FillPatternProgram fillPattern; FillOutlineProgram fillOutline; FillOutlinePatternProgram fillOutlinePattern; diff --git a/src/mbgl/programs/uniforms.hpp b/src/mbgl/programs/uniforms.hpp index e0c5a0d361..972405d5a9 100644 --- a/src/mbgl/programs/uniforms.hpp +++ b/src/mbgl/programs/uniforms.hpp @@ -17,6 +17,8 @@ MBGL_DEFINE_UNIFORM_SCALAR(float, u_zoom); MBGL_DEFINE_UNIFORM_SCALAR(float, u_pitch); MBGL_DEFINE_UNIFORM_SCALAR(float, u_bearing); +MBGL_DEFINE_UNIFORM_SCALAR(Size, u_world); + MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_extrude_scale); MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_tl_a); @@ -25,9 +27,14 @@ MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_tl_b); MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_br_b); MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_size_a); MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_size_b); +MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pixel_coord_upper); +MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pixel_coord_lower); MBGL_DEFINE_UNIFORM_SCALAR(float, u_mix); MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_image); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_a); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_b); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_tile_units_to_pixels); } // namespace uniforms } // namespace mbgl diff --git a/src/mbgl/renderer/circle_bucket.hpp b/src/mbgl/renderer/circle_bucket.hpp index 855565ebf4..b048fd7675 100644 --- a/src/mbgl/renderer/circle_bucket.hpp +++ b/src/mbgl/renderer/circle_bucket.hpp @@ -22,6 +22,7 @@ public: bool hasData() const override; void upload(gl::Context&) override; + void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; float getQueryRadius(const RenderLayer&) const override; diff --git a/src/mbgl/renderer/fill_extrusion_bucket.cpp b/src/mbgl/renderer/fill_extrusion_bucket.cpp new file mode 100644 index 0000000000..2b352ab66a --- /dev/null +++ b/src/mbgl/renderer/fill_extrusion_bucket.cpp @@ -0,0 +1,177 @@ +#include <mbgl/renderer/fill_extrusion_bucket.hpp> +#include <mbgl/renderer/painter.hpp> +#include <mbgl/programs/fill_extrusion_program.hpp> +#include <mbgl/renderer/bucket_parameters.hpp> +#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> +#include <mbgl/renderer/render_fill_extrusion_layer.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/constants.hpp> + +#include <mapbox/earcut.hpp> + +#include <cassert> + +namespace mapbox { +namespace util { +template <> +struct nth<0, mbgl::GeometryCoordinate> { + static int64_t get(const mbgl::GeometryCoordinate& t) { + return t.x; + }; +}; + +template <> +struct nth<1, mbgl::GeometryCoordinate> { + static int64_t get(const mbgl::GeometryCoordinate& t) { + return t.y; + }; +}; +} // namespace util +} // namespace mapbox + +namespace mbgl { + +using namespace style; + +struct GeometryTooLongException : std::exception {}; + +FillExtrusionBucket::FillExtrusionBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) { + for (const auto& layer : layers) { + paintPropertyBinders.emplace(std::piecewise_construct, + std::forward_as_tuple(layer->getID()), + std::forward_as_tuple( + layer->as<RenderFillExtrusionLayer>()->evaluated, + parameters.tileID.overscaledZ)); + } +} + +void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature, + const GeometryCollection& geometry) { + for (auto& polygon : classifyRings(geometry)) { + // Optimize polygons with many interior rings for earcut tesselation. + limitHoles(polygon, 500); + + std::size_t totalVertices = 0; + + for (const auto& ring : polygon) { + totalVertices += ring.size(); + if (totalVertices > std::numeric_limits<uint16_t>::max()) + throw GeometryTooLongException(); + } + + if (totalVertices == 0) continue; + + std::vector<uint32_t> flatIndices; + flatIndices.reserve(totalVertices); + + std::size_t startVertices = vertices.vertexSize(); + + if (triangleSegments.empty() || + triangleSegments.back().vertexLength + (5 * (totalVertices - 1) + 1) > + std::numeric_limits<uint16_t>::max()) { + triangleSegments.emplace_back(startVertices, triangles.indexSize()); + } + + auto& triangleSegment = triangleSegments.back(); + assert(triangleSegment.vertexLength <= std::numeric_limits<uint16_t>::max()); + uint16_t triangleIndex = triangleSegment.vertexLength; + + assert(triangleIndex + (5 * (totalVertices - 1) + 1) <= + std::numeric_limits<uint16_t>::max()); + + for (const auto& ring : polygon) { + std::size_t nVertices = ring.size(); + + if (nVertices == 0) + continue; + + auto edgeDistance = 0; + + for (uint32_t i = 0; i < nVertices; i++) { + const auto& p1 = ring[i]; + + vertices.emplace_back( + FillExtrusionProgram::layoutVertex(p1, 0, 0, 1, 1, edgeDistance)); + flatIndices.emplace_back(triangleIndex); + triangleIndex++; + + if (i != 0) { + const auto& p2 = ring[i - 1]; + + const auto d1 = convertPoint<double>(p1); + const auto d2 = convertPoint<double>(p2); + + const Point<double> perp = util::unit(util::perp(d1 - d2)); + + vertices.emplace_back( + FillExtrusionProgram::layoutVertex(p1, perp.x, perp.y, 0, 0, edgeDistance)); + vertices.emplace_back( + FillExtrusionProgram::layoutVertex(p1, perp.x, perp.y, 0, 1, edgeDistance)); + + edgeDistance += util::dist<int16_t>(d1, d2); + + vertices.emplace_back( + FillExtrusionProgram::layoutVertex(p2, perp.x, perp.y, 0, 0, edgeDistance)); + vertices.emplace_back( + FillExtrusionProgram::layoutVertex(p2, perp.x, perp.y, 0, 1, edgeDistance)); + + triangles.emplace_back(triangleIndex, triangleIndex + 1, triangleIndex + 2); + triangles.emplace_back(triangleIndex + 1, triangleIndex + 2, triangleIndex + 3); + triangleIndex += 4; + triangleSegment.vertexLength += 4; + triangleSegment.indexLength += 6; + } + } + } + + std::vector<uint32_t> indices = mapbox::earcut(polygon); + + std::size_t nIndices = indices.size(); + assert(nIndices % 3 == 0); + + for (uint32_t i = 0; i < nIndices; i += 3) { + triangles.emplace_back(flatIndices[indices[i]], flatIndices[indices[i + 1]], + flatIndices[indices[i + 2]]); + } + + triangleSegment.vertexLength += totalVertices; + triangleSegment.indexLength += nIndices; + } + + for (auto& pair : paintPropertyBinders) { + pair.second.populateVertexVectors(feature, vertices.vertexSize()); + } +} + +void FillExtrusionBucket::upload(gl::Context& context) { + vertexBuffer = context.createVertexBuffer(std::move(vertices)); + indexBuffer = context.createIndexBuffer(std::move(triangles)); + + for (auto& pair : paintPropertyBinders) { + pair.second.upload(context); + } + + uploaded = true; +} + +void FillExtrusionBucket::render(Painter& painter, + PaintParameters& parameters, + const RenderLayer& layer, + const RenderTile& tile) { + painter.renderFillExtrusion(parameters, *this, *layer.as<RenderFillExtrusionLayer>(), tile); +} + +bool FillExtrusionBucket::hasData() const { + return !triangleSegments.empty(); +} + +float FillExtrusionBucket::getQueryRadius(const RenderLayer& layer) const { + if (!layer.is<RenderFillExtrusionLayer>()) { + return 0; + } + + const std::array<float, 2>& translate = layer.as<RenderFillExtrusionLayer>()->evaluated.get<FillExtrusionTranslate>(); + return util::length(translate[0], translate[1]); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/fill_extrusion_bucket.hpp b/src/mbgl/renderer/fill_extrusion_bucket.hpp new file mode 100644 index 0000000000..c54805d743 --- /dev/null +++ b/src/mbgl/renderer/fill_extrusion_bucket.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/tile/geometry_tile_data.hpp> +#include <mbgl/gl/vertex_buffer.hpp> +#include <mbgl/gl/index_buffer.hpp> +#include <mbgl/gl/segment.hpp> +#include <mbgl/programs/fill_extrusion_program.hpp> +#include <mbgl/style/layers/fill_extrusion_layer_properties.hpp> + +namespace mbgl { + +class BucketParameters; + +class FillExtrusionBucket : public Bucket { +public: + FillExtrusionBucket(const BucketParameters&, const std::vector<const RenderLayer*>&); + + void addFeature(const GeometryTileFeature&, + const GeometryCollection&) override; + bool hasData() const override; + + void upload(gl::Context&) override; + void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; + + float getQueryRadius(const RenderLayer&) const override; + + gl::VertexVector<FillExtrusionLayoutVertex> vertices; + gl::IndexVector<gl::Triangles> triangles; + gl::SegmentVector<FillExtrusionAttributes> triangleSegments; + + optional<gl::VertexBuffer<FillExtrusionLayoutVertex>> vertexBuffer; + optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; + + std::unordered_map<std::string, FillExtrusionProgram::PaintPropertyBinders> paintPropertyBinders; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index 0c4915d3e9..36dd4a793f 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -16,6 +16,7 @@ #include <mbgl/renderer/render_background_layer.hpp> #include <mbgl/renderer/render_custom_layer.hpp> #include <mbgl/style/layers/custom_layer_impl.hpp> +#include <mbgl/renderer/render_fill_extrusion_layer.hpp> #include <mbgl/sprite/sprite_atlas.hpp> #include <mbgl/geometry/line_atlas.hpp> @@ -53,7 +54,7 @@ static gl::VertexVector<FillLayoutVertex> tileVertices() { return result; } -static gl::IndexVector<gl::Triangles> tileTriangleIndices() { +static gl::IndexVector<gl::Triangles> quadTriangleIndices() { gl::IndexVector<gl::Triangles> result; result.emplace_back(0, 1, 2); result.emplace_back(1, 2, 3); @@ -79,6 +80,16 @@ static gl::VertexVector<RasterLayoutVertex> rasterVertices() { return result; } +static gl::VertexVector<ExtrusionTextureLayoutVertex> extrusionTextureVertices() { + gl::VertexVector<ExtrusionTextureLayoutVertex> result; + result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 0 })); + result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 0 })); + result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 1 })); + result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 1 })); + return result; +} + + Painter::Painter(gl::Context& context_, const TransformState& state_, float pixelRatio, @@ -87,12 +98,14 @@ Painter::Painter(gl::Context& context_, state(state_), tileVertexBuffer(context.createVertexBuffer(tileVertices())), rasterVertexBuffer(context.createVertexBuffer(rasterVertices())), - tileTriangleIndexBuffer(context.createIndexBuffer(tileTriangleIndices())), + extrusionTextureVertexBuffer(context.createVertexBuffer(extrusionTextureVertices())), + quadTriangleIndexBuffer(context.createIndexBuffer(quadTriangleIndices())), tileBorderIndexBuffer(context.createIndexBuffer(tileLineStripIndices())) { tileTriangleSegments.emplace_back(0, 0, 4, 6); tileBorderSegments.emplace_back(0, 0, 4, 5); rasterSegments.emplace_back(0, 0, 4, 6); + extrusionTextureSegments.emplace_back(0, 0, 4, 6); programs = std::make_unique<Programs>(context, ProgramParameters{ pixelRatio, false, programCacheDir }); @@ -131,12 +144,18 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp spriteAtlas = style.spriteAtlas.get(); lineAtlas = style.lineAtlas.get(); + evaluatedLight = style.evaluatedLight; + RenderData renderData = style.getRenderData(frame.debugOptions, state.getAngle()); const std::vector<RenderItem>& order = renderData.order; const std::unordered_set<Source*>& sources = renderData.sources; // Update the default matrices to the current viewport dimensions. state.getProjMatrix(projMatrix); + // Calculate a second projection matrix with the near plane clipped to 100 so as + // not to waste lots of depth buffer precision on very close empty space, for layer + // types (fill-extrusion) that use the depth buffer to emulate real-world space. + state.getProjMatrix(nearClippedProjMatrix, 100); pixelsToGLUnits = {{ 2.0f / state.getSize().width, -2.0f / state.getSize().height }}; if (state.getViewportMode() == ViewportMode::FlippedY) { @@ -160,8 +179,11 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp annotationSpriteAtlas.upload(context, 0); for (const auto& item : order) { - if (item.bucket && item.bucket->needsUpload()) { - item.bucket->upload(context); + for (const auto& tileRef : item.tiles) { + const auto& bucket = tileRef.get().tile.getBucket(item.layer); + if (bucket && bucket->needsUpload()) { + bucket->upload(context); + } } } } @@ -187,7 +209,7 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp // Update all clipping IDs. algorithm::ClipIDGenerator generator; for (const auto& source : sources) { - source->baseImpl->startRender(generator, projMatrix, state); + source->baseImpl->startRender(generator, projMatrix, nearClippedProjMatrix, state); } MBGL_DEBUG_GROUP(context, "clipping masks"); @@ -208,7 +230,6 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp // Actually render the layers if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; } - // TODO: Correctly compute the number of layers recursively beforehand. depthRangeSize = 1 - (order.size() + 2) * numSublayers * depthEpsilon; // - OPAQUE PASS ------------------------------------------------------------------------------- @@ -302,9 +323,50 @@ void Painter::renderPass(PaintParameters& parameters, // the viewport or Framebuffer. parameters.view.bind(); context.setDirtyState(); + } else if (layer.is<RenderFillExtrusionLayer>()) { + const auto size = context.viewport.getCurrentValue().size; + + OffscreenTexture texture(context, size); + texture.bindRenderbuffers(1); + + context.setStencilMode(gl::StencilMode::disabled()); + context.setDepthMode(depthModeForSublayer(0, gl::DepthMode::ReadWrite)); + context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, 1.0f, {}); + + for (auto& tileRef : item.tiles) { + auto& tile = tileRef.get(); + + MBGL_DEBUG_GROUP(context, layer.baseImpl.id + " - " + util::toString(tile.id)); + auto bucket = tile.tile.getBucket(layer); + bucket->render(*this, parameters, layer, tile); + } + + parameters.view.bind(); + + mat4 viewportMat; + matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1); + + const PaintProperties<>::Evaluated properties{}; + + parameters.programs.extrusionTexture.draw( + context, gl::Triangles(), gl::DepthMode::disabled(), gl::StencilMode::disabled(), + colorModeForRenderPass(), + ExtrusionTextureProgram::UniformValues{ + uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size }, + uniforms::u_image::Value{ 1 }, + uniforms::u_opacity::Value{ + layer.as<RenderFillExtrusionLayer>()->evaluated.get<FillExtrusionOpacity>() } }, + extrusionTextureVertexBuffer, quadTriangleIndexBuffer, + extrusionTextureSegments, + ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, properties, + state.getZoom()); } else { - MBGL_DEBUG_GROUP(context, layer.baseImpl.id + " - " + util::toString(item.tile->id)); - item.bucket->render(*this, parameters, layer, *item.tile); + for (auto& tileRef : item.tiles) { + auto& tile = tileRef.get(); + MBGL_DEBUG_GROUP(context, layer.baseImpl.id + " - " + util::toString(tile.id)); + auto bucket = tile.tile.getBucket(layer); + bucket->render(*this, parameters, layer, tile); + } } } diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp index 9c6dd4505f..7706d2d451 100644 --- a/src/mbgl/renderer/painter.hpp +++ b/src/mbgl/renderer/painter.hpp @@ -12,6 +12,7 @@ #include <mbgl/programs/debug_program.hpp> #include <mbgl/programs/program_parameters.hpp> #include <mbgl/programs/fill_program.hpp> +#include <mbgl/programs/extrusion_texture_program.hpp> #include <mbgl/programs/raster_program.hpp> #include <mbgl/style/style.hpp> @@ -37,12 +38,14 @@ class Tile; class DebugBucket; class FillBucket; +class FillExtrusionBucket; class LineBucket; class CircleBucket; class SymbolBucket; class RasterBucket; class RenderFillLayer; +class RenderFillExtrusionLayer; class RenderLineLayer; class RenderCircleLayer; class RenderSymbolLayer; @@ -82,6 +85,7 @@ public: void renderClippingMask(const UnwrappedTileID&, const ClipID&); void renderTileDebug(const RenderTile&); void renderFill(PaintParameters&, FillBucket&, const RenderFillLayer&, const RenderTile&); + void renderFillExtrusion(PaintParameters&, FillExtrusionBucket&, const RenderFillExtrusionLayer&, const RenderTile&); void renderLine(PaintParameters&, LineBucket&, const RenderLineLayer&, const RenderTile&); void renderCircle(PaintParameters&, CircleBucket&, const RenderCircleLayer&, const RenderTile&); void renderSymbol(PaintParameters&, SymbolBucket&, const RenderSymbolLayer&, const RenderTile&); @@ -126,6 +130,7 @@ private: gl::Context& context; mat4 projMatrix; + mat4 nearClippedProjMatrix; std::array<float, 2> pixelsToGLUnits; @@ -152,6 +157,8 @@ private: GlyphAtlas* glyphAtlas = nullptr; LineAtlas* lineAtlas = nullptr; + style::EvaluatedLight evaluatedLight; + FrameHistory frameHistory; std::unique_ptr<Programs> programs; @@ -161,13 +168,15 @@ private: gl::VertexBuffer<FillLayoutVertex> tileVertexBuffer; gl::VertexBuffer<RasterLayoutVertex> rasterVertexBuffer; + gl::VertexBuffer<ExtrusionTextureLayoutVertex> extrusionTextureVertexBuffer; - gl::IndexBuffer<gl::Triangles> tileTriangleIndexBuffer; + gl::IndexBuffer<gl::Triangles> quadTriangleIndexBuffer; gl::IndexBuffer<gl::LineStrip> tileBorderIndexBuffer; gl::SegmentVector<FillAttributes> tileTriangleSegments; gl::SegmentVector<DebugAttributes> tileBorderSegments; gl::SegmentVector<RasterAttributes> rasterSegments; + gl::SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments; }; } // namespace mbgl diff --git a/src/mbgl/renderer/painter_background.cpp b/src/mbgl/renderer/painter_background.cpp index ac16627246..7cd9cbac5f 100644 --- a/src/mbgl/renderer/painter_background.cpp +++ b/src/mbgl/renderer/painter_background.cpp @@ -49,7 +49,7 @@ void Painter::renderBackground(PaintParameters& parameters, const RenderBackgrou state ), tileVertexBuffer, - tileTriangleIndexBuffer, + quadTriangleIndexBuffer, tileTriangleSegments, paintAttibuteData, properties, @@ -69,7 +69,7 @@ void Painter::renderBackground(PaintParameters& parameters, const RenderBackgrou uniforms::u_world::Value{ context.viewport.getCurrentValue().size }, }, tileVertexBuffer, - tileTriangleIndexBuffer, + quadTriangleIndexBuffer, tileTriangleSegments, paintAttibuteData, properties, diff --git a/src/mbgl/renderer/painter_clipping.cpp b/src/mbgl/renderer/painter_clipping.cpp index 70df9837e8..0d3b5f1504 100644 --- a/src/mbgl/renderer/painter_clipping.cpp +++ b/src/mbgl/renderer/painter_clipping.cpp @@ -26,7 +26,7 @@ void Painter::renderClippingMask(const UnwrappedTileID& tileID, const ClipID& cl uniforms::u_world::Value{ context.viewport.getCurrentValue().size }, }, tileVertexBuffer, - tileTriangleIndexBuffer, + quadTriangleIndexBuffer, tileTriangleSegments, paintAttibuteData, properties, diff --git a/src/mbgl/renderer/painter_fill_extrusion.cpp b/src/mbgl/renderer/painter_fill_extrusion.cpp new file mode 100644 index 0000000000..af98cae569 --- /dev/null +++ b/src/mbgl/renderer/painter_fill_extrusion.cpp @@ -0,0 +1,87 @@ +#include <mbgl/renderer/painter.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/fill_extrusion_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/render_fill_extrusion_layer.hpp> +#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> +#include <mbgl/sprite/sprite_atlas.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/fill_extrusion_program.hpp> +#include <mbgl/util/constants.hpp> +#include <mbgl/util/convert.hpp> + +namespace mbgl { + +using namespace style; + +void Painter::renderFillExtrusion(PaintParameters& parameters, + FillExtrusionBucket& bucket, + const RenderFillExtrusionLayer& layer, + const RenderTile& tile) { + const FillExtrusionPaintProperties::Evaluated& properties = layer.evaluated; + + if (pass == RenderPass::Opaque) { + return; + } + + if (!properties.get<FillExtrusionPattern>().from.empty()) { + optional<SpriteAtlasElement> imagePosA = + spriteAtlas->getPattern(properties.get<FillExtrusionPattern>().from); + optional<SpriteAtlasElement> imagePosB = + spriteAtlas->getPattern(properties.get<FillExtrusionPattern>().to); + + if (!imagePosA || !imagePosB) { + return; + } + + spriteAtlas->bind(true, context, 0); + + parameters.programs.fillExtrusionPattern.draw( + context, + gl::Triangles(), + depthModeForSublayer(0, gl::DepthMode::ReadWrite), + gl::StencilMode::disabled(), + colorModeForRenderPass(), + FillExtrusionPatternUniforms::values( + tile.translatedClipMatrix(properties.get<FillExtrusionTranslate>(), + properties.get<FillExtrusionTranslateAnchor>(), + state), + *imagePosA, + *imagePosB, + properties.get<FillExtrusionPattern>(), + tile.id, + state, + -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f, + evaluatedLight + ), + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.triangleSegments, + bucket.paintPropertyBinders.at(layer.getID()), + properties, + state.getZoom()); + + } else { + parameters.programs.fillExtrusion.draw( + context, + gl::Triangles(), + depthModeForSublayer(0, gl::DepthMode::ReadWrite), + gl::StencilMode::disabled(), + colorModeForRenderPass(), + FillExtrusionUniforms::values( + tile.translatedClipMatrix(properties.get<FillExtrusionTranslate>(), + properties.get<FillExtrusionTranslateAnchor>(), + state), + state, + evaluatedLight + ), + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.triangleSegments, + bucket.paintPropertyBinders.at(layer.getID()), + properties, + state.getZoom()); + }; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/painter_raster.cpp b/src/mbgl/renderer/painter_raster.cpp index 31f10ed3ba..fbe025b5b0 100644 --- a/src/mbgl/renderer/painter_raster.cpp +++ b/src/mbgl/renderer/painter_raster.cpp @@ -77,7 +77,7 @@ void Painter::renderRaster(PaintParameters& parameters, uniforms::u_tl_parent::Value{ std::array<float, 2> {{ 0.0f, 0.0f }} }, }, rasterVertexBuffer, - tileTriangleIndexBuffer, + quadTriangleIndexBuffer, rasterSegments, paintAttributeData, properties, diff --git a/src/mbgl/renderer/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/render_fill_extrusion_layer.cpp index a378f12cf0..8df0d36900 100644 --- a/src/mbgl/renderer/render_fill_extrusion_layer.cpp +++ b/src/mbgl/renderer/render_fill_extrusion_layer.cpp @@ -1,26 +1,53 @@ #include <mbgl/renderer/render_fill_extrusion_layer.hpp> -#include <mbgl/renderer/bucket.hpp> +#include <mbgl/renderer/fill_extrusion_bucket.hpp> #include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> +#include <mbgl/geometry/feature_index.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/intersection_tests.hpp> namespace mbgl { RenderFillExtrusionLayer::RenderFillExtrusionLayer(const style::FillExtrusionLayer::Impl& _impl) - : RenderLayer(style::LayerType::FillExtrusion, _impl) { + : RenderLayer(style::LayerType::FillExtrusion, _impl), + impl(&_impl) { } std::unique_ptr<RenderLayer> RenderFillExtrusionLayer::clone() const { return std::make_unique<RenderFillExtrusionLayer>(*this); } -std::unique_ptr<Bucket> RenderFillExtrusionLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { - return nullptr; +std::unique_ptr<Bucket> RenderFillExtrusionLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { + return std::make_unique<FillExtrusionBucket>(parameters, layers); } -void RenderFillExtrusionLayer::cascade(const style::CascadeParameters&) { +void RenderFillExtrusionLayer::cascade(const style::CascadeParameters& parameters) { + unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated)); } -bool RenderFillExtrusionLayer::evaluate(const style::PropertyEvaluationParameters&) { - return false; +bool RenderFillExtrusionLayer::evaluate(const style::PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + passes = (evaluated.get<style::FillExtrusionOpacity>() > 0) ? RenderPass::Translucent + : RenderPass::None; + + return unevaluated.hasTransition(); +} + +bool RenderFillExtrusionLayer::queryIntersectsFeature( + const GeometryCoordinates& queryGeometry, + const GeometryTileFeature& feature, + const float, + const float bearing, + const float pixelsToTileUnits) const { + + auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( + queryGeometry, + evaluated.get<style::FillExtrusionTranslate>(), + evaluated.get<style::FillExtrusionTranslateAnchor>(), + bearing, + pixelsToTileUnits); + + return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); } } // namespace mbgl diff --git a/src/mbgl/renderer/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/render_fill_extrusion_layer.hpp index 596ba02fee..87b6ad6071 100644 --- a/src/mbgl/renderer/render_fill_extrusion_layer.hpp +++ b/src/mbgl/renderer/render_fill_extrusion_layer.hpp @@ -17,11 +17,20 @@ public: void cascade(const style::CascadeParameters&) override; bool evaluate(const style::PropertyEvaluationParameters&) override; + bool queryIntersectsFeature( + const GeometryCoordinates&, + const GeometryTileFeature&, + const float, + const float, + const float) const override; + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; // Paint properties style::FillExtrusionPaintProperties::Unevaluated unevaluated; style::FillExtrusionPaintProperties::Evaluated evaluated; + + const style::FillExtrusionLayer::Impl* const impl; }; template <> diff --git a/src/mbgl/renderer/render_item.hpp b/src/mbgl/renderer/render_item.hpp index 575287d9c6..01bb263d1e 100644 --- a/src/mbgl/renderer/render_item.hpp +++ b/src/mbgl/renderer/render_item.hpp @@ -18,14 +18,12 @@ class Source; class RenderItem { public: RenderItem(const RenderLayer& layer_, - const RenderTile* tile_ = nullptr, - Bucket* bucket_ = nullptr) - : tile(tile_), bucket(bucket_), layer(layer_) { + std::vector<std::reference_wrapper<RenderTile>> tiles_ = {}) + : layer(layer_), tiles(std::move(tiles_)) { } - const RenderTile* const tile; - Bucket* const bucket; const RenderLayer& layer; + std::vector<std::reference_wrapper<RenderTile>> tiles; }; class RenderData { diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp index 5c7c491be0..ce59186e61 100644 --- a/src/mbgl/renderer/render_tile.cpp +++ b/src/mbgl/renderer/render_tile.cpp @@ -5,11 +5,12 @@ namespace mbgl { using namespace style; -mat4 RenderTile::translatedMatrix(const std::array<float, 2>& translation, - TranslateAnchorType anchor, - const TransformState& state) const { +mat4 RenderTile::translateVtxMatrix(const mat4& tileMatrix, + const std::array<float, 2>& translation, + TranslateAnchorType anchor, + const TransformState& state) const { if (translation[0] == 0 && translation[1] == 0) { - return matrix; + return tileMatrix; } mat4 vtxMatrix; @@ -17,18 +18,41 @@ mat4 RenderTile::translatedMatrix(const std::array<float, 2>& translation, if (anchor == TranslateAnchorType::Viewport) { const double sin_a = std::sin(-state.getAngle()); const double cos_a = std::cos(-state.getAngle()); - matrix::translate(vtxMatrix, matrix, - id.pixelsToTileUnits(translation[0] * cos_a - translation[1] * sin_a, state.getZoom()), - id.pixelsToTileUnits(translation[0] * sin_a + translation[1] * cos_a, state.getZoom()), - 0); + matrix::translate(vtxMatrix, tileMatrix, + id.pixelsToTileUnits(translation[0] * cos_a - translation[1] * sin_a, state.getZoom()), + id.pixelsToTileUnits(translation[0] * sin_a + translation[1] * cos_a, state.getZoom()), + 0); } else { - matrix::translate(vtxMatrix, matrix, - id.pixelsToTileUnits(translation[0], state.getZoom()), - id.pixelsToTileUnits(translation[1], state.getZoom()), - 0); + matrix::translate(vtxMatrix, tileMatrix, + id.pixelsToTileUnits(translation[0], state.getZoom()), + id.pixelsToTileUnits(translation[1], state.getZoom()), + 0); } return vtxMatrix; } +mat4 RenderTile::translatedMatrix(const std::array<float, 2>& translation, + TranslateAnchorType anchor, + const TransformState& state) const { + return translateVtxMatrix(matrix, translation, anchor, state); +} + +mat4 RenderTile::translatedClipMatrix(const std::array<float, 2>& translation, + TranslateAnchorType anchor, + const TransformState& state) const { + return translateVtxMatrix(nearClippedMatrix, translation, anchor, state); +} + +void RenderTile::calculateMatrices(const mat4& projMatrix, + const mat4& projClipMatrix, + const TransformState& transform) { + // Calculate two matrices for this tile: matrix is the standard tile matrix; nearClippedMatrix + // clips the near plane to 100 to save depth buffer precision + transform.matrixFor(matrix, id); + transform.matrixFor(nearClippedMatrix, id); + matrix::multiply(matrix, projMatrix, matrix); + matrix::multiply(nearClippedMatrix, projClipMatrix, nearClippedMatrix); +} + } // namespace mbgl diff --git a/src/mbgl/renderer/render_tile.hpp b/src/mbgl/renderer/render_tile.hpp index e2e0c3d656..02e8667eec 100644 --- a/src/mbgl/renderer/render_tile.hpp +++ b/src/mbgl/renderer/render_tile.hpp @@ -24,11 +24,25 @@ public: Tile& tile; ClipID clip; mat4 matrix; + mat4 nearClippedMatrix; bool used = false; mat4 translatedMatrix(const std::array<float, 2>& translate, style::TranslateAnchorType anchor, const TransformState&) const; + + mat4 translatedClipMatrix(const std::array<float, 2>& translate, + style::TranslateAnchorType anchor, + const TransformState&) const; + + void calculateMatrices(const mat4& projMatrix, + const mat4& projClipMatrix, + const TransformState&); +private: + mat4 translateVtxMatrix(const mat4& tileMatrix, + const std::array<float, 2>& translation, + style::TranslateAnchorType anchor, + const TransformState& state) const; }; } // namespace mbgl diff --git a/src/mbgl/shaders/extrusion_texture.cpp b/src/mbgl/shaders/extrusion_texture.cpp new file mode 100644 index 0000000000..c756db181c --- /dev/null +++ b/src/mbgl/shaders/extrusion_texture.cpp @@ -0,0 +1,39 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/extrusion_texture.hpp> + +namespace mbgl { +namespace shaders { + +const char* extrusion_texture::name = "extrusion_texture"; +const char* extrusion_texture::vertexSource = R"MBGL_SHADER( +uniform mat4 u_matrix; +uniform vec2 u_world; +attribute vec2 a_pos; +varying vec2 v_pos; + +void main() { + gl_Position = u_matrix * vec4(a_pos * u_world, 0, 1); + + v_pos.x = a_pos.x; + v_pos.y = 1.0 - a_pos.y; +} + +)MBGL_SHADER"; +const char* extrusion_texture::fragmentSource = R"MBGL_SHADER( +uniform sampler2D u_image; +uniform float u_opacity; +varying vec2 v_pos; + +void main() { + gl_FragColor = texture2D(u_image, v_pos) * u_opacity; + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(0.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/extrusion_texture.hpp b/src/mbgl/shaders/extrusion_texture.hpp new file mode 100644 index 0000000000..05e54c5499 --- /dev/null +++ b/src/mbgl/shaders/extrusion_texture.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class extrusion_texture { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/fill_extrusion.cpp b/src/mbgl/shaders/fill_extrusion.cpp new file mode 100644 index 0000000000..ea1c80545e --- /dev/null +++ b/src/mbgl/shaders/fill_extrusion.cpp @@ -0,0 +1,98 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/fill_extrusion.hpp> + +namespace mbgl { +namespace shaders { + +const char* fill_extrusion::name = "fill_extrusion"; +const char* fill_extrusion::vertexSource = R"MBGL_SHADER( +uniform mat4 u_matrix; +uniform vec3 u_lightcolor; +uniform lowp vec3 u_lightpos; +uniform lowp float u_lightintensity; + +attribute vec2 a_pos; +attribute vec3 a_normal; +attribute float a_edgedistance; + +varying vec4 v_color; + +uniform lowp float a_base_t; +attribute lowp vec2 a_base; +varying lowp float base; +uniform lowp float a_height_t; +attribute lowp vec2 a_height; +varying lowp float height; + +uniform lowp float a_color_t; +attribute highp vec4 a_color; +varying highp vec4 color; + +void main() { + base = unpack_mix_vec2(a_base, a_base_t); + height = unpack_mix_vec2(a_height, a_height_t); + color = unpack_mix_vec4(a_color, a_color_t); + + base = max(0.0, base); + height = max(0.0, height); + + float ed = a_edgedistance; // use each attrib in order to not trip a VAO assert + float t = mod(a_normal.x, 2.0); + + gl_Position = u_matrix * vec4(a_pos, t > 0.0 ? height : base, 1); + + // Relative luminance (how dark/bright is the surface color?) + float colorvalue = color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722; + + v_color = vec4(0.0, 0.0, 0.0, 1.0); + + // Add slight ambient lighting so no extrusions are totally black + vec4 ambientlight = vec4(0.03, 0.03, 0.03, 1.0); + color += ambientlight; + + // Calculate cos(theta), where theta is the angle between surface normal and diffuse light ray + float directional = clamp(dot(a_normal / 16384.0, u_lightpos), 0.0, 1.0); + + // Adjust directional so that + // the range of values for highlight/shading is narrower + // with lower light intensity + // and with lighter/brighter surface colors + directional = mix((1.0 - u_lightintensity), max((1.0 - colorvalue + u_lightintensity), 1.0), directional); + + // Add gradient along z axis of side surfaces + if (a_normal.y != 0.0) { + directional *= clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0); + } + + // Assign final color based on surface + ambient light color, diffuse light directional, and light color + // with lower bounds adjusted to hue of light + // so that shading is tinted with the complementary (opposite) color to the light color + v_color.r += clamp(color.r * directional * u_lightcolor.r, mix(0.0, 0.3, 1.0 - u_lightcolor.r), 1.0); + v_color.g += clamp(color.g * directional * u_lightcolor.g, mix(0.0, 0.3, 1.0 - u_lightcolor.g), 1.0); + v_color.b += clamp(color.b * directional * u_lightcolor.b, mix(0.0, 0.3, 1.0 - u_lightcolor.b), 1.0); +} + +)MBGL_SHADER"; +const char* fill_extrusion::fragmentSource = R"MBGL_SHADER( +varying vec4 v_color; +varying lowp float base; +varying lowp float height; +varying highp vec4 color; + +void main() { + + + + + gl_FragColor = v_color; + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/fill_extrusion.hpp b/src/mbgl/shaders/fill_extrusion.hpp new file mode 100644 index 0000000000..949ae57f12 --- /dev/null +++ b/src/mbgl/shaders/fill_extrusion.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class fill_extrusion { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/fill_extrusion_pattern.cpp b/src/mbgl/shaders/fill_extrusion_pattern.cpp new file mode 100644 index 0000000000..e2de5c20b2 --- /dev/null +++ b/src/mbgl/shaders/fill_extrusion_pattern.cpp @@ -0,0 +1,111 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/fill_extrusion_pattern.hpp> + +namespace mbgl { +namespace shaders { + +const char* fill_extrusion_pattern::name = "fill_extrusion_pattern"; +const char* fill_extrusion_pattern::vertexSource = R"MBGL_SHADER( +uniform mat4 u_matrix; +uniform vec2 u_pattern_size_a; +uniform vec2 u_pattern_size_b; +uniform vec2 u_pixel_coord_upper; +uniform vec2 u_pixel_coord_lower; +uniform float u_scale_a; +uniform float u_scale_b; +uniform float u_tile_units_to_pixels; +uniform float u_height_factor; + +uniform vec3 u_lightcolor; +uniform lowp vec3 u_lightpos; +uniform lowp float u_lightintensity; + +attribute vec2 a_pos; +attribute vec3 a_normal; +attribute float a_edgedistance; + +varying vec2 v_pos_a; +varying vec2 v_pos_b; +varying vec4 v_lighting; +varying float v_directional; + +uniform lowp float a_base_t; +attribute lowp vec2 a_base; +varying lowp float base; +uniform lowp float a_height_t; +attribute lowp vec2 a_height; +varying lowp float height; + +void main() { + base = unpack_mix_vec2(a_base, a_base_t); + height = unpack_mix_vec2(a_height, a_height_t); + + base = max(0.0, base); + height = max(0.0, height); + + float t = mod(a_normal.x, 2.0); + float z = t > 0.0 ? height : base; + + gl_Position = u_matrix * vec4(a_pos, z, 1); + + vec2 pos = a_normal.x == 1.0 && a_normal.y == 0.0 && a_normal.z == 16384.0 + ? a_pos // extrusion top + : vec2(a_edgedistance, z * u_height_factor); // extrusion side + + v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_a * u_pattern_size_a, u_tile_units_to_pixels, pos); + v_pos_b = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_b * u_pattern_size_b, u_tile_units_to_pixels, pos); + + v_lighting = vec4(0.0, 0.0, 0.0, 1.0); + float directional = clamp(dot(a_normal / 16383.0, u_lightpos), 0.0, 1.0); + directional = mix((1.0 - u_lightintensity), max((0.5 + u_lightintensity), 1.0), directional); + + if (a_normal.y != 0.0) { + directional *= clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0); + } + + v_lighting.rgb += clamp(directional * u_lightcolor, mix(vec3(0.0), vec3(0.3), 1.0 - u_lightcolor), vec3(1.0)); +} + +)MBGL_SHADER"; +const char* fill_extrusion_pattern::fragmentSource = R"MBGL_SHADER( +uniform vec2 u_pattern_tl_a; +uniform vec2 u_pattern_br_a; +uniform vec2 u_pattern_tl_b; +uniform vec2 u_pattern_br_b; +uniform float u_mix; + +uniform sampler2D u_image; + +varying vec2 v_pos_a; +varying vec2 v_pos_b; +varying vec4 v_lighting; + +varying lowp float base; +varying lowp float height; + +void main() { + + + + vec2 imagecoord = mod(v_pos_a, 1.0); + vec2 pos = mix(u_pattern_tl_a, u_pattern_br_a, imagecoord); + vec4 color1 = texture2D(u_image, pos); + + vec2 imagecoord_b = mod(v_pos_b, 1.0); + vec2 pos2 = mix(u_pattern_tl_b, u_pattern_br_b, imagecoord_b); + vec4 color2 = texture2D(u_image, pos2); + + vec4 mixedColor = mix(color1, color2, u_mix); + + gl_FragColor = mixedColor * v_lighting; + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/fill_extrusion_pattern.hpp b/src/mbgl/shaders/fill_extrusion_pattern.hpp new file mode 100644 index 0000000000..e9ae001d4e --- /dev/null +++ b/src/mbgl/shaders/fill_extrusion_pattern.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class fill_extrusion_pattern { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/preludes.cpp b/src/mbgl/shaders/preludes.cpp index cca0f3e3f1..95fa624e8d 100644 --- a/src/mbgl/shaders/preludes.cpp +++ b/src/mbgl/shaders/preludes.cpp @@ -48,8 +48,9 @@ vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 v // packed like so: // packedValue = floor(input[0]) * 256 + input[1], vec2 unpack_float(const float packedValue) { - float v0 = floor(packedValue / 256.0); - return vec2(v0, packedValue - v0 * 256.0); + int packedIntValue = int(packedValue); + int v0 = packedIntValue / 256; + return vec2(v0, packedIntValue - v0 * 256); } diff --git a/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp b/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp index f6f9753460..2353bd99fe 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp +++ b/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp @@ -16,8 +16,6 @@ public: std::unique_ptr<RenderLayer> createRenderLayer() const override; FillExtrusionPaintProperties::Cascading cascading; - - //style::FillExtrusionLayer::Impl* const impl; }; } // namespace style diff --git a/src/mbgl/style/light_impl.hpp b/src/mbgl/style/light_impl.hpp new file mode 100644 index 0000000000..d1825090fc --- /dev/null +++ b/src/mbgl/style/light_impl.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include <mbgl/style/light.hpp> +#include <mbgl/style/transitioning_property.hpp> +#include <mbgl/style/cascade_parameters.hpp> +#include <mbgl/style/property_evaluator.hpp> +#include <mbgl/style/property_evaluation_parameters.hpp> +#include <mbgl/util/ignore.hpp> + +namespace mbgl { +namespace style { + +template <class TypeList> +class Transitioning; + +template <class... Ps> +class Transitioning<TypeList<Ps...>> : public IndexedTuple< + TypeList<Ps...>, + TypeList<TransitioningProperty<typename Ps::ValueType>...>> +{ +private: + using Properties = TypeList<Ps...>; + using Raw = IndexedTuple<Properties, Properties>; + using Super = IndexedTuple< + TypeList<Ps...>, + TypeList<TransitioningProperty<typename Ps::ValueType>...>>; + +public: + Transitioning() = default; + Transitioning(const Raw& raw, Transitioning&& prior, const CascadeParameters& params) + : Super { + TransitioningProperty<typename Ps::ValueType>( + raw.template get<Ps>().value, + std::move(prior.template get<Ps>()), + raw.template get<Ps>().transition.reverseMerge(params.transition), + params.now)... + } {} + + bool hasTransition() const { + bool result = false; + util::ignore({ result |= this->template get<Ps>().hasTransition()... }); + return result; + } +}; + +template <class TypeList> +class Evaluated; + +template <class... Ps> +class Evaluated<TypeList<Ps...>> : public IndexedTuple< + TypeList<Ps...>, + TypeList<typename Ps::Type...>> +{ +private: + using Properties = TypeList<Ps...>; + using TransitioningPs = Transitioning<Properties>; + using Super = IndexedTuple< + TypeList<Ps...>, + TypeList<typename Ps::Type...>>; + +public: + Evaluated() = default; + Evaluated(TransitioningPs& transitioning, const PropertyEvaluationParameters& params) + : Super { + transitioning.template get<Ps>() + .evaluate(PropertyEvaluator<typename Ps::Type>(params, Ps::defaultValue()), params.now)... + } {} +}; + +using TransitioningLight = Transitioning<LightProperties>; +using EvaluatedLight = Evaluated<LightProperties>; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/paint_property.hpp b/src/mbgl/style/paint_property.hpp index 7f7018748c..dd8d6c0b50 100644 --- a/src/mbgl/style/paint_property.hpp +++ b/src/mbgl/style/paint_property.hpp @@ -1,5 +1,6 @@ #pragma once +#include <mbgl/style/transitioning_property.hpp> #include <mbgl/style/class_dictionary.hpp> #include <mbgl/style/property_value.hpp> #include <mbgl/style/data_driven_property_value.hpp> @@ -24,68 +25,6 @@ class GeometryTileFeature; namespace style { template <class Value> -class UnevaluatedPaintProperty { -public: - UnevaluatedPaintProperty() = default; - - UnevaluatedPaintProperty(Value value_, - UnevaluatedPaintProperty<Value> prior_, - TransitionOptions transition, - TimePoint now) - : begin(now + transition.delay.value_or(Duration::zero())), - end(begin + transition.duration.value_or(Duration::zero())), - value(std::move(value_)) { - if (transition.isDefined()) { - prior = { std::move(prior_) }; - } - } - - template <class Evaluator> - auto evaluate(const Evaluator& evaluator, TimePoint now) { - auto finalValue = value.evaluate(evaluator); - if (!prior) { - // No prior value. - return finalValue; - } else if (now >= end) { - // Transition from prior value is now complete. - prior = {}; - return finalValue; - } else if (value.isDataDriven()) { - // Transitions to data-driven properties are not supported. - // We snap immediately to the data-driven value so that, when we perform layout, - // we see the data-driven function and can use it to populate vertex buffers. - prior = {}; - return finalValue; - } else if (now < begin) { - // Transition hasn't started yet. - return prior->get().evaluate(evaluator, now); - } else { - // Interpolate between recursively-calculated prior value and final. - float t = std::chrono::duration<float>(now - begin) / (end - begin); - return util::interpolate(prior->get().evaluate(evaluator, now), finalValue, util::DEFAULT_TRANSITION_EASE.solve(t, 0.001)); - } - } - - bool hasTransition() const { - return bool(prior); - } - - bool isUndefined() const { - return value.isUndefined(); - } - - const Value& getValue() const { - return value; - } - -private: - optional<mapbox::util::recursive_wrapper<UnevaluatedPaintProperty<Value>>> prior; - TimePoint begin; - TimePoint end; - Value value; -}; - -template <class Value> class CascadingPaintProperty { public: bool isUndefined() const { @@ -112,8 +51,8 @@ public: transitions[klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default] = transition; } - template <class UnevaluatedPaintProperty> - UnevaluatedPaintProperty cascade(const CascadeParameters& params, UnevaluatedPaintProperty prior) const { + template <class TransitioningProperty> + TransitioningProperty cascade(const CascadeParameters& params, TransitioningProperty prior) const { TransitionOptions transition; Value value; @@ -131,7 +70,7 @@ public: } } - return UnevaluatedPaintProperty(std::move(value), + return TransitioningProperty(std::move(value), std::move(prior), transition.reverseMerge(params.transition), params.now); @@ -147,7 +86,7 @@ class PaintProperty { public: using ValueType = PropertyValue<T>; using CascadingType = CascadingPaintProperty<ValueType>; - using UnevaluatedType = UnevaluatedPaintProperty<ValueType>; + using UnevaluatedType = TransitioningProperty<ValueType>; using EvaluatorType = PropertyEvaluator<T>; using EvaluatedType = T; static constexpr bool IsDataDriven = false; @@ -158,7 +97,7 @@ class DataDrivenPaintProperty { public: using ValueType = DataDrivenPropertyValue<T>; using CascadingType = CascadingPaintProperty<ValueType>; - using UnevaluatedType = UnevaluatedPaintProperty<ValueType>; + using UnevaluatedType = TransitioningProperty<ValueType>; using EvaluatorType = DataDrivenPropertyEvaluator<T>; using EvaluatedType = PossiblyEvaluatedPropertyValue<T>; static constexpr bool IsDataDriven = true; @@ -172,7 +111,7 @@ class CrossFadedPaintProperty { public: using ValueType = PropertyValue<T>; using CascadingType = CascadingPaintProperty<ValueType>; - using UnevaluatedType = UnevaluatedPaintProperty<ValueType>; + using UnevaluatedType = TransitioningProperty<ValueType>; using EvaluatorType = CrossFadedPropertyEvaluator<T>; using EvaluatedType = Faded<T>; static constexpr bool IsDataDriven = false; diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp index 19ea8cefee..c407e438fb 100644 --- a/src/mbgl/style/parser.cpp +++ b/src/mbgl/style/parser.cpp @@ -4,6 +4,7 @@ #include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/source.hpp> #include <mbgl/style/conversion/layer.hpp> +#include <mbgl/style/conversion/light.hpp> #include <mbgl/util/logging.hpp> @@ -82,6 +83,10 @@ StyleParseResult Parser::parse(const std::string& json) { } } + if (document.HasMember("light")) { + parseLight(document["light"]); + } + if (document.HasMember("sources")) { parseSources(document["sources"]); } @@ -107,6 +112,17 @@ StyleParseResult Parser::parse(const std::string& json) { return nullptr; } +void Parser::parseLight(const JSValue& value) { + conversion::Error error; + optional<Light> converted = conversion::convert<Light>(value, error); + if (!converted) { + Log::Warning(Event::ParseStyle, error.message); + return; + } + + light = *converted; +} + void Parser::parseSources(const JSValue& value) { if (!value.IsObject()) { Log::Warning(Event::ParseStyle, "sources must be an object"); diff --git a/src/mbgl/style/parser.hpp b/src/mbgl/style/parser.hpp index a05a0b316a..32b8a7a8bc 100644 --- a/src/mbgl/style/parser.hpp +++ b/src/mbgl/style/parser.hpp @@ -2,6 +2,7 @@ #include <mbgl/style/layer.hpp> #include <mbgl/style/source.hpp> +#include <mbgl/style/light.hpp> #include <mbgl/util/rapidjson.hpp> #include <mbgl/util/font_stack.hpp> @@ -31,6 +32,8 @@ public: std::vector<std::unique_ptr<Source>> sources; std::vector<std::unique_ptr<Layer>> layers; + Light light; + std::string name; LatLng latLng; double zoom = 0; @@ -41,6 +44,7 @@ public: std::vector<FontStack> fontStacks() const; private: + void parseLight(const JSValue&); void parseSources(const JSValue&); void parseLayers(const JSValue&); void parseLayer(const std::string& id, const JSValue&, std::unique_ptr<Layer>&); diff --git a/src/mbgl/style/source_impl.cpp b/src/mbgl/style/source_impl.cpp index 9fabc54f7d..7a65dd1ded 100644 --- a/src/mbgl/style/source_impl.cpp +++ b/src/mbgl/style/source_impl.cpp @@ -57,8 +57,9 @@ void Source::Impl::invalidateTiles() { } void Source::Impl::startRender(algorithm::ClipIDGenerator& generator, - const mat4& projMatrix, - const TransformState& transform) { + const mat4& projMatrix, + const mat4& clipMatrix, + const TransformState& transform) { if (type == SourceType::Vector || type == SourceType::GeoJSON || type == SourceType::Annotations) { @@ -67,8 +68,7 @@ void Source::Impl::startRender(algorithm::ClipIDGenerator& generator, for (auto& pair : renderTiles) { auto& tile = pair.second; - transform.matrixFor(tile.matrix, tile.id); - matrix::multiply(tile.matrix, projMatrix, tile.matrix); + tile.calculateMatrices(projMatrix, clipMatrix, transform); } } diff --git a/src/mbgl/style/source_impl.hpp b/src/mbgl/style/source_impl.hpp index b4473cbc84..387428ec59 100644 --- a/src/mbgl/style/source_impl.hpp +++ b/src/mbgl/style/source_impl.hpp @@ -60,6 +60,7 @@ public: void startRender(algorithm::ClipIDGenerator&, const mat4& projMatrix, + const mat4& clipMatrix, const TransformState&); void finishRender(Painter&); diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp index e8141c3c6b..8dfe7eac06 100644 --- a/src/mbgl/style/style.cpp +++ b/src/mbgl/style/style.cpp @@ -54,6 +54,7 @@ Style::Style(Scheduler& scheduler_, FileSource& fileSource_, float pixelRatio) glyphAtlas(std::make_unique<GlyphAtlas>(Size{ 2048, 2048 }, fileSource)), spriteAtlas(std::make_unique<SpriteAtlas>(Size{ 1024, 1024 }, pixelRatio)), lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 })), + light(std::make_unique<Light>()), observer(&nullObserver) { glyphAtlas->setObserver(this); spriteAtlas->setObserver(this); @@ -141,6 +142,7 @@ void Style::setJSON(const std::string& json) { defaultZoom = parser.zoom; defaultBearing = parser.bearing; defaultPitch = parser.pitch; + light = std::make_unique<Light>(parser.light); glyphAtlas->setURL(parser.glyphURL); spriteAtlas->load(parser.spriteURL, scheduler, fileSource); @@ -352,6 +354,8 @@ void Style::cascade(const TimePoint& timePoint, MapMode mode) { for (const auto& layer : renderLayers) { layer->cascade(parameters); } + + transitioningLight = TransitioningLight(*light, std::move(transitioningLight), parameters); } void Style::recalculate(float z, const TimePoint& timePoint, MapMode mode) { @@ -370,7 +374,7 @@ void Style::recalculate(float z, const TimePoint& timePoint, MapMode mode) { mode == MapMode::Continuous ? util::DEFAULT_FADE_DURATION : Duration::zero() }; - hasPendingTransitions = false; + hasPendingTransitions = transitioningLight.hasTransition(); for (const auto& layer : renderLayers) { hasPendingTransitions |= layer->evaluate(parameters); @@ -389,6 +393,8 @@ void Style::recalculate(float z, const TimePoint& timePoint, MapMode mode) { } } + evaluatedLight = EvaluatedLight(transitioningLight, parameters); + // Remove the existing tiles if we didn't end up re-enabling the source. for (const auto& source : sources) { if (!source->baseImpl->enabled) { @@ -509,8 +515,9 @@ RenderData Style::getRenderData(MapDebugOptions debugOptions, float angle) const }); } - for (auto& tileRef : sortedTiles) { - auto& tile = tileRef.get(); + std::vector<std::reference_wrapper<RenderTile>> sortedTilesForInsertion; + for (auto tileIt = sortedTiles.begin(); tileIt != sortedTiles.end(); ++tileIt) { + auto& tile = tileIt->get(); if (!tile.tile.isRenderable()) { continue; } @@ -523,8 +530,9 @@ RenderData Style::getRenderData(MapDebugOptions debugOptions, float angle) const // Look back through the buckets we decided to render to find out whether there is // already a bucket from this layer that is a parent of this tile. Tiles are ordered // by zoom level when we obtain them from getTiles(). - for (auto it = result.order.rbegin(); it != result.order.rend() && (&it->layer == layer.get()); ++it) { - if (tile.tile.id.isChildOf(it->tile->tile.id)) { + for (auto it = sortedTilesForInsertion.rbegin(); + it != sortedTilesForInsertion.rend(); ++it) { + if (tile.tile.id.isChildOf(it->get().tile.id)) { skip = true; break; } @@ -536,10 +544,12 @@ RenderData Style::getRenderData(MapDebugOptions debugOptions, float angle) const auto bucket = tile.tile.getBucket(*layer); if (bucket) { - result.order.emplace_back(*layer, &tile, bucket); + sortedTilesForInsertion.emplace_back(tile); tile.used = true; } } + + result.order.emplace_back(*layer, std::move(sortedTilesForInsertion)); } return result; diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp index 127430a89f..26959c9fbf 100644 --- a/src/mbgl/style/style.hpp +++ b/src/mbgl/style/style.hpp @@ -6,6 +6,7 @@ #include <mbgl/style/layer_observer.hpp> #include <mbgl/style/update_batch.hpp> #include <mbgl/renderer/render_layer.hpp> +#include <mbgl/style/light_impl.hpp> #include <mbgl/text/glyph_atlas_observer.hpp> #include <mbgl/sprite/sprite_atlas_observer.hpp> #include <mbgl/map/mode.hpp> @@ -120,6 +121,10 @@ public: std::unique_ptr<SpriteAtlas> spriteAtlas; std::unique_ptr<LineAtlas> lineAtlas; + std::unique_ptr<Light> light; + TransitioningLight transitioningLight; + EvaluatedLight evaluatedLight; + private: std::vector<std::unique_ptr<Source>> sources; std::vector<std::unique_ptr<Layer>> layers; diff --git a/src/mbgl/style/transitioning_property.hpp b/src/mbgl/style/transitioning_property.hpp new file mode 100644 index 0000000000..5456322b33 --- /dev/null +++ b/src/mbgl/style/transitioning_property.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> +#include <mbgl/style/transition_options.hpp> +#include <mbgl/util/interpolate.hpp> + +#include <utility> + +namespace mbgl { +namespace style { + +template <class Value> +class TransitioningProperty { +public: + TransitioningProperty() = default; + + TransitioningProperty(Value value_, + TransitioningProperty<Value> prior_, + TransitionOptions transition, + TimePoint now) + : begin(now + transition.delay.value_or(Duration::zero())), + end(begin + transition.duration.value_or(Duration::zero())), + value(std::move(value_)) { + if (transition.isDefined()) { + prior = { std::move(prior_) }; + } + } + + template <class Evaluator> + auto evaluate(const Evaluator& evaluator, TimePoint now) { + auto finalValue = value.evaluate(evaluator); + if (!prior) { + // No prior value. + return finalValue; + } else if (now >= end) { + // Transition from prior value is now complete. + prior = {}; + return finalValue; + } else if (value.isDataDriven()) { + // Transitions to data-driven properties are not supported. + // We snap immediately to the data-driven value so that, when we perform layout, + // we see the data-driven function and can use it to populate vertex buffers. + prior = {}; + return finalValue; + } else if (now < begin) { + // Transition hasn't started yet. + return prior->get().evaluate(evaluator, now); + } else { + // Interpolate between recursively-calculated prior value and final. + float t = std::chrono::duration<float>(now - begin) / (end - begin); + return util::interpolate(prior->get().evaluate(evaluator, now), finalValue, + util::DEFAULT_TRANSITION_EASE.solve(t, 0.001)); + } + } + + bool hasTransition() const { + return bool(prior); + } + + bool isUndefined() const { + return value.isUndefined(); + } + + const Value& getValue() const { + return value; + } + +private: + optional<mapbox::util::recursive_wrapper<TransitioningProperty<Value>>> prior; + TimePoint begin; + TimePoint end; + Value value; +}; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/types.cpp b/src/mbgl/style/types.cpp index bf2ba8a793..b37e73ffb1 100644 --- a/src/mbgl/style/types.cpp +++ b/src/mbgl/style/types.cpp @@ -89,4 +89,9 @@ MBGL_DEFINE_ENUM(IconTextFitType, { { IconTextFitType::Height, "height" }, }); +MBGL_DEFINE_ENUM(LightAnchorType, { + { LightAnchorType::Map, "map" }, + { LightAnchorType::Viewport, "viewport" } +}); + } // namespace mbgl diff --git a/src/mbgl/util/mat3.cpp b/src/mbgl/util/mat3.cpp index e2200867ce..107be81985 100644 --- a/src/mbgl/util/mat3.cpp +++ b/src/mbgl/util/mat3.cpp @@ -25,8 +25,9 @@ #include <cmath> namespace mbgl { +namespace matrix { -void matrix::identity(mat3& out) { +void identity(mat3& out) { out[0] = 1.0f; out[1] = 0.0f; out[2] = 0.0f; @@ -38,7 +39,7 @@ void matrix::identity(mat3& out) { out[8] = 1.0f; } -void matrix::translate(mat3& out, const mat3& a, double x, double y) { +void translate(mat3& out, const mat3& a, double x, double y) { double a00 = a[0], a01 = a[1], a02 = a[2], a10 = a[3], a11 = a[4], a12 = a[5], a20 = a[6], a21 = a[7], a22 = a[8]; @@ -56,7 +57,7 @@ void matrix::translate(mat3& out, const mat3& a, double x, double y) { out[8] = x * a02 + y * a12 + a22; } -void matrix::rotate(mat3& out, const mat3& a, double rad) { +void rotate(mat3& out, const mat3& a, double rad) { double s = std::sin(rad), c = std::cos(rad), a00 = a[0], @@ -82,7 +83,7 @@ void matrix::rotate(mat3& out, const mat3& a, double rad) { out[8] = a22; } -void matrix::scale(mat3& out, const mat3& a, double x, double y) { +void scale(mat3& out, const mat3& a, double x, double y) { out[0] = x * a[0]; out[1] = x * a[1]; out[2] = x * a[2]; @@ -94,4 +95,11 @@ void matrix::scale(mat3& out, const mat3& a, double x, double y) { out[8] = a[8]; } +void transformMat3f(vec3f& out, const vec3f& a, const mat3& m) { + out[0] = m[0] * a[0] + m[3] * a[1] + m[6] * a[2]; + out[1] = m[1] * a[0] + m[4] * a[1] + m[7] * a[2]; + out[2] = m[2] * a[0] + m[5] * a[1] + m[8] * a[2]; +} + +} // namespace matrix } // namespace mbgl diff --git a/src/mbgl/util/mat3.hpp b/src/mbgl/util/mat3.hpp index ca4955fe5f..c4203c940b 100644 --- a/src/mbgl/util/mat3.hpp +++ b/src/mbgl/util/mat3.hpp @@ -26,7 +26,9 @@ namespace mbgl { -typedef std::array<double, 9> mat3; +using vec3 = std::array<double, 3>; +using vec3f = std::array<float, 3>; +using mat3 = std::array<double, 9>; namespace matrix { @@ -35,5 +37,7 @@ void translate(mat3& out, const mat3& a, double x, double y); void rotate(mat3& out, const mat3& a, double rad); void scale(mat3& out, const mat3& a, double x, double y); +void transformMat3f(vec3f& out, const vec3f& a, const mat3& m); + } // namespace matrix } // namespace mbgl diff --git a/src/mbgl/util/offscreen_texture.cpp b/src/mbgl/util/offscreen_texture.cpp index 56ef60b15c..e719ac566e 100644 --- a/src/mbgl/util/offscreen_texture.cpp +++ b/src/mbgl/util/offscreen_texture.cpp @@ -14,12 +14,13 @@ public: void bind() { if (!framebuffer) { - texture = context.createTexture(size); + texture = context.createTexture(size, gl::TextureFormat::RGBA); framebuffer = context.createFramebuffer(*texture); } else { context.bindFramebuffer = framebuffer->framebuffer; } + context.activeTexture = 0; context.viewport = { 0, 0, size }; } @@ -36,6 +37,20 @@ public: return size; } + void bindRenderbuffers(gl::TextureUnit unit) { + if (!framebuffer) { + texture = context.createTexture(size, gl::TextureFormat::RGBA, unit); + gl::Renderbuffer<gl::RenderbufferType::DepthComponent> depthTarget = context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(size); + framebuffer = context.createFramebuffer(*texture, depthTarget); + + } else { + context.bindFramebuffer = framebuffer->framebuffer; + } + + context.activeTexture = unit; + context.viewport = { 0, 0, size }; + } + private: gl::Context& context; const Size size; @@ -66,4 +81,8 @@ const Size& OffscreenTexture::getSize() const { return impl->getSize(); } +void OffscreenTexture::bindRenderbuffers(gl::TextureUnit unit) { + impl->bindRenderbuffers(unit); +} + } // namespace mbgl diff --git a/src/mbgl/util/offscreen_texture.hpp b/src/mbgl/util/offscreen_texture.hpp index b8bfabf7d3..4e9e936114 100644 --- a/src/mbgl/util/offscreen_texture.hpp +++ b/src/mbgl/util/offscreen_texture.hpp @@ -16,6 +16,7 @@ public: ~OffscreenTexture(); void bind() override; + void bindRenderbuffers(gl::TextureUnit unit = 0); PremultipliedImage readStillImage(); diff --git a/test/fixtures/style_parser/line-translate.info.json b/test/fixtures/style_parser/line-translate.info.json index 16df094290..26df77609b 100644 --- a/test/fixtures/style_parser/line-translate.info.json +++ b/test/fixtures/style_parser/line-translate.info.json @@ -1,7 +1,7 @@ { "default": { "log": [ - [1, "WARNING", "ParseStyle", "value must be an array of two numbers"] + [1, "WARNING", "ParseStyle", "value must be an array of 2 numbers"] ] } } diff --git a/test/style/conversion/light.test.cpp b/test/style/conversion/light.test.cpp new file mode 100644 index 0000000000..da0fd3054a --- /dev/null +++ b/test/style/conversion/light.test.cpp @@ -0,0 +1,101 @@ +#include <mbgl/test/util.hpp> + +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/rapidjson_conversion.hpp> +#include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion/light.hpp> +#include <mbgl/style/position.hpp> +#include <mbgl/util/rapidjson.hpp> +#include <mbgl/util/color.hpp> +#include <mbgl/util/chrono.hpp> + +#include <array> + +using namespace mbgl; +using namespace mbgl::style; +using namespace mbgl::style::conversion; + +TEST(StyleConversion, Light) { + Error error; + + auto parseLight = [&](const std::string& src) { + JSDocument doc; + doc.Parse<0>(src); + return convert<Light>(doc, error); + }; + + { + auto light = parseLight("{}"); + ASSERT_TRUE((bool) light); + } + + { + auto light = parseLight("{\"color\":{\"stops\":[[14,\"blue\"],[16,\"red\"]]},\"intensity\":0.3,\"position\":[3,90,90]}"); + ASSERT_TRUE((bool) light); + + ASSERT_TRUE(light->get<LightAnchor>().value.isUndefined()); + ASSERT_FALSE(light->get<LightAnchor>().value.isConstant()); + ASSERT_FALSE(light->get<LightAnchor>().value.isCameraFunction()); + + ASSERT_FALSE(light->get<LightIntensity>().value.isUndefined()); + ASSERT_TRUE(light->get<LightIntensity>().value.isConstant()); + ASSERT_EQ(light->get<LightIntensity>().value.asConstant(), 0.3f); + ASSERT_FALSE(light->get<LightAnchor>().value.isCameraFunction()); + + ASSERT_FALSE(light->get<LightColor>().value.isUndefined()); + ASSERT_FALSE(light->get<LightColor>().value.isConstant()); + ASSERT_TRUE(light->get<LightColor>().value.isCameraFunction()); + + ASSERT_FALSE(light->get<LightPosition>().value.isUndefined()); + ASSERT_TRUE(light->get<LightPosition>().value.isConstant()); + std::array<float, 3> expected{{ 3, 90, 90 }}; + ASSERT_EQ(light->get<LightPosition>().value.asConstant(), mbgl::style::Position({ expected })); + ASSERT_FALSE(light->get<LightPosition>().value.isCameraFunction()); + } + + { + auto light = parseLight("{\"color\":\"blue\",\"intensity\":0.3,\"color-transition\":{\"duration\":1000}}"); + ASSERT_TRUE((bool) light); + + ASSERT_FALSE(light->get<LightColor>().value.isUndefined()); + ASSERT_TRUE(light->get<LightColor>().value.isConstant()); + ASSERT_FALSE(light->get<LightColor>().value.isCameraFunction()); + ASSERT_EQ(light->get<LightColor>().transition.duration, mbgl::Duration(mbgl::Milliseconds(1000))); + ASSERT_FALSE((bool) light->get<LightColor>().transition.delay); + } + + { + auto light = parseLight("{\"intensity\":false}"); + + ASSERT_FALSE((bool) light); + ASSERT_EQ("value must be a number", error.message); + } + + { + auto light = parseLight("{\"intensity\":{\"stops\":[[15,\"red\"],[17,\"blue\"]]}}"); + + ASSERT_FALSE((bool) light); + ASSERT_EQ("value must be a number", error.message); + } + + { + auto light = parseLight("{\"color\":5}"); + + ASSERT_FALSE((bool) light); + ASSERT_EQ("value must be a string", error.message); + } + + { + auto light = parseLight("{\"position\":[0,5]}"); + + ASSERT_FALSE((bool) light); + ASSERT_EQ("value must be an array of 3 numbers", error.message); + } + + { + auto light = parseLight("{\"anchor\":\"something\"}"); + + ASSERT_FALSE((bool) light); + ASSERT_EQ("value must be a valid enumeration value", error.message); + } +} diff --git a/test/style/paint_property.test.cpp b/test/style/paint_property.test.cpp index 39d31068c1..15a0796f21 100644 --- a/test/style/paint_property.test.cpp +++ b/test/style/paint_property.test.cpp @@ -1,12 +1,13 @@ #include <mbgl/test/util.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/style/transitioning_property.hpp> using namespace mbgl; using namespace mbgl::style; using namespace std::literals::chrono_literals; -float evaluate(UnevaluatedPaintProperty<PropertyValue<float>>& property, Duration delta = Duration::zero()) { +float evaluate(TransitioningProperty<PropertyValue<float>>& property, Duration delta = Duration::zero()) { PropertyEvaluationParameters parameters { 0, TimePoint::min() + delta, @@ -22,7 +23,7 @@ float evaluate(UnevaluatedPaintProperty<PropertyValue<float>>& property, Duratio return property.evaluate(evaluator, parameters.now); } -PossiblyEvaluatedPropertyValue<float> evaluate(UnevaluatedPaintProperty<DataDrivenPropertyValue<float>>& property, Duration delta = Duration::zero()) { +PossiblyEvaluatedPropertyValue<float> evaluate(TransitioningProperty<DataDrivenPropertyValue<float>>& property, Duration delta = Duration::zero()) { PropertyEvaluationParameters parameters { 0, TimePoint::min() + delta, @@ -38,15 +39,15 @@ PossiblyEvaluatedPropertyValue<float> evaluate(UnevaluatedPaintProperty<DataDriv return property.evaluate(evaluator, parameters.now); } -TEST(UnevaluatedPaintProperty, EvaluateDefaultValue) { - UnevaluatedPaintProperty<PropertyValue<float>> property; +TEST(TransitioningProperty, EvaluateDefaultValue) { + TransitioningProperty<PropertyValue<float>> property; ASSERT_EQ(0.0f, evaluate(property)); } -TEST(UnevaluatedPaintProperty, EvaluateUntransitionedConstant) { - UnevaluatedPaintProperty<PropertyValue<float>> property { +TEST(TransitioningProperty, EvaluateUntransitionedConstant) { + TransitioningProperty<PropertyValue<float>> property { PropertyValue<float>(1.0f), - UnevaluatedPaintProperty<PropertyValue<float>>(), + TransitioningProperty<PropertyValue<float>>(), TransitionOptions(), TimePoint::min() }; @@ -54,18 +55,18 @@ TEST(UnevaluatedPaintProperty, EvaluateUntransitionedConstant) { ASSERT_EQ(1.0f, evaluate(property)); } -TEST(UnevaluatedPaintProperty, EvaluateTransitionedConstantWithoutDelay) { +TEST(TransitioningProperty, EvaluateTransitionedConstantWithoutDelay) { TransitionOptions transition; transition.duration = { 1000ms }; - UnevaluatedPaintProperty<PropertyValue<float>> t0 { + TransitioningProperty<PropertyValue<float>> t0 { PropertyValue<float>(0.0f), - UnevaluatedPaintProperty<PropertyValue<float>>(), + TransitioningProperty<PropertyValue<float>>(), TransitionOptions(), TimePoint::min() }; - UnevaluatedPaintProperty<PropertyValue<float>> t1 { + TransitioningProperty<PropertyValue<float>> t1 { PropertyValue<float>(1.0f), t0, transition, @@ -77,19 +78,19 @@ TEST(UnevaluatedPaintProperty, EvaluateTransitionedConstantWithoutDelay) { ASSERT_FLOAT_EQ(1.0f, evaluate(t1, 1500ms)); } -TEST(UnevaluatedPaintProperty, EvaluateTransitionedConstantWithDelay) { +TEST(TransitioningProperty, EvaluateTransitionedConstantWithDelay) { TransitionOptions transition; transition.delay = { 1000ms }; transition.duration = { 1000ms }; - UnevaluatedPaintProperty<PropertyValue<float>> t0 { + TransitioningProperty<PropertyValue<float>> t0 { PropertyValue<float>(0.0f), - UnevaluatedPaintProperty<PropertyValue<float>>(), + TransitioningProperty<PropertyValue<float>>(), TransitionOptions(), TimePoint::min() }; - UnevaluatedPaintProperty<PropertyValue<float>> t1 { + TransitioningProperty<PropertyValue<float>> t1 { PropertyValue<float>(1.0f), t0, transition, @@ -103,14 +104,14 @@ TEST(UnevaluatedPaintProperty, EvaluateTransitionedConstantWithDelay) { ASSERT_FLOAT_EQ(1.0f, evaluate(t1, 2500ms)); } -TEST(UnevaluatedPaintProperty, EvaluateDataDrivenValue) { +TEST(TransitioningProperty, EvaluateDataDrivenValue) { TransitionOptions transition; transition.delay = { 1000ms }; transition.duration = { 1000ms }; - UnevaluatedPaintProperty<DataDrivenPropertyValue<float>> t0 { + TransitioningProperty<DataDrivenPropertyValue<float>> t0 { DataDrivenPropertyValue<float>(0.0f), - UnevaluatedPaintProperty<DataDrivenPropertyValue<float>>(), + TransitioningProperty<DataDrivenPropertyValue<float>>(), TransitionOptions(), TimePoint::min() }; @@ -120,7 +121,7 @@ TEST(UnevaluatedPaintProperty, EvaluateDataDrivenValue) { IdentityStops<float>() }; - UnevaluatedPaintProperty<DataDrivenPropertyValue<float>> t1 { + TransitioningProperty<DataDrivenPropertyValue<float>> t1 { DataDrivenPropertyValue<float>(sourceFunction), t0, transition, diff --git a/test/util/position.test.cpp b/test/util/position.test.cpp new file mode 100644 index 0000000000..938a08dded --- /dev/null +++ b/test/util/position.test.cpp @@ -0,0 +1,49 @@ +#include <mbgl/test/util.hpp> + +#include <mbgl/style/types.hpp> +#include <mbgl/style/position.hpp> +#include <mbgl/util/constants.hpp> + +using namespace mbgl; +using namespace style; + +void expectArrayEQ(std::array<float, 3> got, std::array<float, 3> expected) { + for (int i = 0; i < 3; i++) { + EXPECT_NEAR(got[i], expected[i], 0.00001); + } +}; + +void expectArrayNE(std::array<float, 3> got, std::array<float, 3> expected) { + short eq = 0; + for (int i = 0; i < 3; i++) { + if (got[i] == expected[i]) { + eq++; + } + } + EXPECT_NE(eq, 3); +}; + +Position createPosition(std::array<float, 3> pos) { + return Position(pos); +} + +TEST(Position, Calculations) { + std::array<float, 3> spherical{{ 2, 10, 270 }}; + + Position position(spherical); + + expectArrayNE(position.getCartesian(), spherical); + expectArrayEQ(position.getSpherical(), spherical); + + expectArrayEQ(position.getCartesian(), {{ 0.34729638695716858, -1.9696154594421387, 2.384976127700611e-08 }}); + + expectArrayNE(createPosition({{ 2, 30, 10 }}).getSpherical(), createPosition({{ 2, 30, 370 }}).getSpherical()); + expectArrayEQ(createPosition({{ 2, 30, 10 }}).getCartesian(), createPosition({{ 2, 30, 370 }}).getCartesian()); + + std::array<float, 3> newSpherical = {{ 1, 80, 270 }}; + position.set(newSpherical); + + expectArrayNE(position.getSpherical(), spherical); + expectArrayNE(position.getCartesian(), {{ 0.34729638695716858, -1.9696154594421387, 2.384976127700611e-08 }}); + expectArrayEQ(position.getCartesian(), {{ 0.98480772972106934, -0.17364829778671265, 1.1924880638503055e-08 }}); +} diff --git a/test/util/tile_cover.test.cpp b/test/util/tile_cover.test.cpp index 809b7df9f2..c746e6dab5 100644 --- a/test/util/tile_cover.test.cpp +++ b/test/util/tile_cover.test.cpp @@ -31,12 +31,13 @@ TEST(TileCover, Pitch) { Transform transform; transform.resize({ 512, 512 }); // slightly offset center so that tile order is better defined - transform.setLatLng({ 0.01, -0.01 }); + transform.setLatLng({ 0.1, -0.1 }); transform.setZoom(2); + transform.setAngle(5.0); transform.setPitch(40.0 * M_PI / 180.0); EXPECT_EQ((std::vector<UnwrappedTileID>{ - { 2, 1, 1 }, { 2, 1, 2 }, { 2, 2, 1 }, { 2, 2, 2 }, + { 2, 1, 2 }, { 2, 1, 1 }, { 2, 2, 2 }, { 2, 2, 1 }, { 2, 3, 2 } }), util::tileCover(transform.getState(), 2)); } |