summaryrefslogtreecommitdiff
path: root/include/mbgl/util/interpolate.hpp
blob: 566f2b0a6bf7f67a033ed8604648cc20efc009c3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#pragma once

#include <mbgl/style/expression/value.hpp>
#include <mbgl/style/position.hpp>
#include <mbgl/style/rotation.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/util/range.hpp>

#include <array>
#include <vector>
#include <string>
#include <type_traits>
#include <utility>

namespace mbgl {
namespace util {

float interpolationFactor(float base, Range<float> range, float z);

template <class T, class Enabled = void>
struct Interpolator;

template <typename T>
T interpolate(const T& a, const T& b, const double t) {
    return Interpolator<T>()(a, b, t);
}


template <class T, class Enabled>
struct Interpolator {
    T operator()(const T& a, const T& b, const double t) const {
        return a * (1.0 - t) + b * t;
    }
};

template <class T, std::size_t N>
struct Interpolator<std::array<T, N>> {
private:
    using Array = std::array<T, N>;

    template <std::size_t... I>
    Array operator()(const Array& a, const Array& b, const double t, std::index_sequence<I...>) {
        return {{ interpolate(a[I], b[I], t)... }};
    }

public:
    Array operator()(const Array& a, const Array& b, const double t) {
        return operator()(a, b, t, std::make_index_sequence<N>());
    }
};


// In order to accept Array<Number, N> as an output value for Curve
// expressions, we need to have an interpolatable std::vector type.
// However, style properties like line-dasharray are represented using
// std::vector<float>, and should NOT be considered interpolatable.
// So, we use std::vector<Value> to represent expression array values,
// asserting that (a) the vectors are the same size, and (b) they contain
// only numeric values.  (These invariants should be relatively safe,
// being enforced by the expression type system.)
template<>
struct Interpolator<std::vector<style::expression::Value>> {
    std::vector<style::expression::Value> operator()(const std::vector<style::expression::Value>& a,
                                  const std::vector<style::expression::Value>& b,
                                  const double t) const {
        assert(a.size() == b.size());
        if (a.size() == 0) return {};
        std::vector<style::expression::Value> result;
        for (std::size_t i = 0; i < a.size(); i++) {
            assert(a[i].template is<double>());
            assert(b[i].template is<double>());
            style::expression::Value item = interpolate(
                a[i].template get<double>(),
                b[i].template get<double>(),
                t);
            result.push_back(item);
        }
        return result;
    }
};

template <>
struct Interpolator<style::Position> {
public:
    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) {
        return {
            interpolate(a.r, b.r, t),
            interpolate(a.g, b.g, t),
            interpolate(a.b, b.b, t),
            interpolate(a.a, b.a, t)
        };
    }
};

template <>
struct Interpolator<style::Rotation> {
public:
    style::Rotation operator()(const style::Rotation& a, const style::Rotation& b, const double t) {
        assert(a.period() == b.period());
        auto period = a.period();
        auto aAngle = std::fmod(a.getAngle(), period);
        auto bAngle = std::fmod(b.getAngle(), period);

        if (aAngle - bAngle > period * 0.5) {
            return {std::fmod(aAngle * (1.0 - t) + (bAngle + period) * t, period)};
        }

        if (aAngle - bAngle < period * -0.5) {
            return {std::fmod((aAngle + period) * (1.0 - t) + bAngle * t, period)};
        }

        return {aAngle * (1.0 - t) + bAngle * t};
    }
};

struct Uninterpolated {
    template <class T>
    T operator()(const T& a, const T&, const double) const {
        return a;
    }
};

template <>
struct Interpolator<bool>
    : Uninterpolated {};

template <class T>
struct Interpolator<T, typename std::enable_if_t<std::is_enum<T>::value>>
    : Uninterpolated {};

template <>
struct Interpolator<std::string>
    : Uninterpolated {};

template <class T>
struct Interpolator<std::vector<T>>
    : Uninterpolated {};

template <class T>
struct Interpolatable
    : std::conditional_t<
      !std::is_base_of<Uninterpolated, Interpolator<T>>::value,
      std::true_type,
      std::false_type> {};



} // namespace util
} // namespace mbgl