From f6e79d70735361438655f279c8699a786d25458c Mon Sep 17 00:00:00 2001 From: Lauren Budorick Date: Thu, 27 Apr 2017 15:56:55 -0700 Subject: [core] Render fill-extrusion layers (#8431) --- include/mbgl/map/map.hpp | 5 ++ include/mbgl/style/conversion/constant.hpp | 49 ++++-------- include/mbgl/style/conversion/layer.hpp | 3 + include/mbgl/style/conversion/light.hpp | 122 +++++++++++++++++++++++++++++ include/mbgl/style/conversion/position.hpp | 29 +++++++ include/mbgl/style/layer.hpp | 2 +- include/mbgl/style/light.hpp | 52 ++++++++++++ include/mbgl/style/position.hpp | 68 ++++++++++++++++ include/mbgl/style/types.hpp | 5 ++ include/mbgl/util/indexed_tuple.hpp | 56 +++++++++++++ include/mbgl/util/interpolate.hpp | 12 +++ include/mbgl/util/type_list.hpp | 40 ++++++++++ 12 files changed, 408 insertions(+), 35 deletions(-) create mode 100644 include/mbgl/style/conversion/light.hpp create mode 100644 include/mbgl/style/conversion/position.hpp create mode 100644 include/mbgl/style/light.hpp create mode 100644 include/mbgl/style/position.hpp create mode 100644 include/mbgl/util/indexed_tuple.hpp create mode 100644 include/mbgl/util/type_list.hpp (limited to 'include') 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* 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 #include #include +#include #include #include @@ -92,45 +93,25 @@ struct Converter { } }; -template <> -struct Converter> { +template +struct Converter> { template - optional> operator()(const V& value, Error& error) const { - if (!isArray(value) || arrayLength(value) != 2) { - error = { "value must be an array of two numbers" }; + optional> 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 first = toNumber(arrayMember(value, 0)); - optional second = toNumber(arrayMember(value, 1)); - if (!first || !second) { - error = { "value must be an array of two numbers" }; - return {}; - } - - return std::array {{ *first, *second }}; - } -}; - -template <> -struct Converter> { - template - optional> operator()(const V& value, Error& error) const { - if (!isArray(value) || arrayLength(value) != 4) { - error = { "value must be an array of four numbers" }; - return {}; - } - - optional first = toNumber(arrayMember(value, 0)); - optional second = toNumber(arrayMember(value, 1)); - optional third = toNumber(arrayMember(value, 2)); - optional fourth = toNumber(arrayMember(value, 3)); - if (!first || !second) { - error = { "value must be an array of four numbers" }; - return {}; + std::array result; + for (size_t i = 0; i < N; i++) { + optional 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 {{ *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 #include #include +#include #include #include #include @@ -92,6 +93,8 @@ public: if (*type == "fill") { converted = convertVectorLayer(*id, value, error); + } else if (*type == "fill-extrusion") { + converted = convertVectorLayer(*id, value, error); } else if (*type == "line") { converted = convertVectorLayer(*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 +#include +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter { +public: + template + optional 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> convertedAnchor = + convert>(*anchor, error); + + if (convertedAnchor) { + light.get().value = *convertedAnchor; + } else { + return {}; + } + } + + const auto anchorTransition = objectMember(value, "anchor-transition"); + if (anchorTransition) { + optional transition = + convert(*anchorTransition, error); + if (transition) { + light.get().transition = *transition; + } else { + return {}; + } + } + + const auto color = objectMember(value, "color"); + if (color) { + optional> convertedColor = + convert>(*color, error); + + if (convertedColor) { + light.get().value = *convertedColor; + } else { + return {}; + } + } + + const auto colorTransition = objectMember(value, "color-transition"); + if (colorTransition) { + optional transition = + convert(*colorTransition, error); + if (transition) { + light.get().transition = *transition; + } else { + return {}; + } + } + + const auto position = objectMember(value, "position"); + if (position) { + optional> convertedPosition = + convert>(*position, error); + + if (convertedPosition) { + light.get().value = *convertedPosition; + } else { + return {}; + } + } + + const auto positionTransition = objectMember(value, "position-transition"); + if (positionTransition) { + optional transition = + convert(*positionTransition, error); + if (transition) { + light.get().transition = *transition; + } else { + return {}; + } + } + + const auto intensity = objectMember(value, "intensity"); + if (intensity) { + optional> convertedIntensity = + convert>(*intensity, error); + + if (convertedIntensity) { + light.get().value = *convertedIntensity; + } else { + return {}; + } + } + + const auto intensityTransition = objectMember(value, "intensity-transition"); + if (intensityTransition) { + optional transition = + convert(*intensityTransition, error); + if (transition) { + light.get().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 +#include +#include + +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter { + template + optional operator()(const V& value, Error& error) const { + optional> spherical = convert>(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 +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +template +class LightProperty { +public: + using Type = T; + using ValueType = PropertyValue; + + PropertyValue value; + TransitionOptions transition; +}; + +struct LightAnchor : LightProperty { + static LightAnchorType defaultValue() { + return LightAnchorType::Viewport; + } +}; + +struct LightPosition : LightProperty { + static Position defaultValue() { + std::array default_ = { { 1.15, 210, 30 } }; + return Position{ { default_ } }; + } +}; + +struct LightColor : LightProperty { + static Color defaultValue() { + return Color::white(); + } +}; + +struct LightIntensity : LightProperty { + static float defaultValue() { + return 0.5; + } +}; + +using LightProperties = TypeList; +class Light : public IndexedTuple {}; + +} // 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 + +#include + +namespace mbgl { +namespace style { +class Position { +public: + Position() = default; + Position(std::array& 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 getCartesian() const { + return { { x, y, z } }; + }; + + const std::array getSpherical() const { + return { { radial, azimuthal, polar } }; + }; + + void set(std::array& 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& 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/include/mbgl/util/indexed_tuple.hpp b/include/mbgl/util/indexed_tuple.hpp new file mode 100644 index 0000000000..a414639530 --- /dev/null +++ b/include/mbgl/util/indexed_tuple.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include +#include + +namespace mbgl { + +template +struct TypeIndex; + +template +struct TypeIndex : std::integral_constant {}; + +template +struct TypeIndex : std::integral_constant::value> {}; + +template class IndexedTuple; + +// A tuple of Ts, where individual members can be accessed via `t.get()` for I ∈ Is. +// +// See https://github.com/mapbox/cpp/blob/master/C%2B%2B%20Structural%20Metaprogramming.md +// for motivation. +// +template +class IndexedTuple, TypeList> : public std::tuple { +public: + static_assert(sizeof...(Is) == sizeof...(Ts), "IndexedTuple size mismatch"); + + using std::tuple::tuple; + + template + static constexpr std::size_t Index = TypeIndex::value; + + template + auto& get() { + return std::get>(*this); + } + + template + const auto& get() const { + return std::get>(*this); + } + + template + IndexedTuple, TypeList> + concat(const IndexedTuple, TypeList>& other) const { + return IndexedTuple, TypeList> { + get()..., + other.template get()... + }; + } +}; + +} // namespace mbgl 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 #include +#include #include #include @@ -46,6 +47,17 @@ public: } }; +template <> +struct Interpolator { +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 { public: diff --git a/include/mbgl/util/type_list.hpp b/include/mbgl/util/type_list.hpp new file mode 100644 index 0000000000..4a5e95c8a4 --- /dev/null +++ b/include/mbgl/util/type_list.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +namespace mbgl { + +template +class TypeList {}; + +namespace detail { + +template +struct TypeCons; + +template +struct TypeCons> { + using Type = TypeList; +}; + +template class> +struct TypeFilter; + +template