summaryrefslogtreecommitdiff
path: root/src/mbgl/style/paint_properties_map.cpp
blob: 4d7755ec3c2d68f1e07e8a2a841e57fe201c1c6a (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
#include <mbgl/style/paint_properties_map.hpp>
#include <mbgl/style/property_transition.hpp>
#include <mbgl/style/property_fallback.hpp>
#include <mbgl/style/class_properties.hpp>
#include <mbgl/style/style_cascade_parameters.hpp>

namespace mbgl {

void PaintPropertiesMap::parseEach(const JSVal& layer, std::function<void (ClassProperties &, const JSVal &)> parsePaint) {
    paints.clear();

    rapidjson::Value::ConstMemberIterator itr = layer.MemberBegin();
    for (; itr != layer.MemberEnd(); ++itr) {
        const std::string name { itr->name.GetString(), itr->name.GetStringLength() };
        if (name == "paint") {
            parsePaint(paints[ClassID::Default], itr->value);
        } else if (name.compare(0, 6, "paint.") == 0 && name.length() > 6) {
            parsePaint(paints[ClassDictionary::Get().lookup(name.substr(6))], itr->value);
        }
    }
}

void PaintPropertiesMap::cascade(const StyleCascadeParameters& parameters) {
    // Stores all keys that we have already added transitions for.
    std::set<PropertyKey> alreadyApplied;

    // We only apply the default style values if there are no classes set.
    if (parameters.classes.empty()) {
        cascadeClass(ClassID::Default, alreadyApplied, parameters);
        return;
    }

    // Reverse iterate through all class names and apply them last to first.
    for (auto it = parameters.classes.rbegin(); it != parameters.classes.rend(); ++it) {
        // From here on, we're only dealing with IDs to avoid comparing strings all the time.
        cascadeClass(ClassDictionary::Get().lookup(*it), alreadyApplied, parameters);
    }

    // As the last class, apply the default class.
    cascadeClass(ClassID::Default, alreadyApplied, parameters);

    // Make sure that we also transition to the fallback value for keys that aren't changed by
    // any applied classes.
    for (auto& propertyPair : appliedStyle) {
        const PropertyKey key = propertyPair.first;
        if (alreadyApplied.find(key) != alreadyApplied.end()) {
            // This property has already been set by a previous class, so we don't need to
            // transition to the fallback.
            continue;
        }

        AppliedClassPropertyValues &appliedProperties = propertyPair.second;
        // Make sure that we don't do double transitions to the fallback value.
        if (appliedProperties.mostRecent() == ClassID::Fallback) {
            continue;
        }

        // This property key hasn't been set by a previous class, so we need to add a transition
        // to the fallback value for that key.
        const TimePoint begin = parameters.now + *parameters.defaultTransition.delay;
        const TimePoint end = begin + *parameters.defaultTransition.duration;
        const PropertyValue &value = PropertyFallbackValue::Get(key);
        appliedProperties.add(ClassID::Fallback, begin, end, value);
    }
}

void PaintPropertiesMap::cascadeClass(const ClassID classID,
                                      std::set<PropertyKey>& alreadyApplied,
                                      const StyleCascadeParameters& parameters) {
    auto styleIt = paints.find(classID);
    if (styleIt == paints.end()) {
        // There is no class in this layer with this class_name.
        return;
    }

    // Loop through all the properties in this style, and add transitions to them, if they're
    // not already the most recent transition.
    const ClassProperties& classProperties = styleIt->second;
    for (const auto& propertyPair : classProperties) {
        PropertyKey key = propertyPair.first;
        if (alreadyApplied.find(key) != alreadyApplied.end()) {
            // This property has already been set by a previous class.
            continue;
        }

        // Mark this property as written by a previous class, so that subsequent
        // classes won't override this.
        alreadyApplied.insert(key);

        // If the most recent transition is not the one with the highest priority, create
        // a transition.
        AppliedClassPropertyValues &appliedProperties = appliedStyle[key];
        if (appliedProperties.mostRecent() != classID) {
            PropertyTransition transition = classProperties.getTransition(key);
            Duration delay = transition.delay ? *transition.delay : *parameters.defaultTransition.delay;
            Duration duration = transition.duration ? *transition.duration : *parameters.defaultTransition.duration;
            const TimePoint begin = parameters.now + delay;
            const TimePoint end = begin + duration;
            const PropertyValue &value = propertyPair.second;
            appliedProperties.add(classID, begin, end, value);
        }
    }
}

bool PaintPropertiesMap::hasTransitions() const {
    for (const auto& pair : appliedStyle) {
        if (pair.second.hasTransitions()) {
            return true;
        }
    }
    return hasPendingTransitions;
}

void PaintPropertiesMap::removeExpiredTransitions(const TimePoint& now) {
    for (auto it = appliedStyle.begin(); it != appliedStyle.end();) {
        AppliedClassPropertyValues& values = it->second;
        values.cleanup(now);
        // If the current properties object is empty, remove it from the map entirely.
        values.empty() ? appliedStyle.erase(it++) : ++it;
    }

    // Clear the pending transitions flag upon each update.
    hasPendingTransitions = false;
}

}