summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin R. Miller <incanus@codesorcery.net>2014-05-22 12:46:53 -0700
committerJustin R. Miller <incanus@codesorcery.net>2014-05-22 12:46:53 -0700
commitaa92e92595c385c5d7dcc88a361213818d715e73 (patch)
treed31f57debbe5495bc23ec5ca253fbe953acdd896
parentcbe84299ed1a3e52483c38ef279f6bf29d65e476 (diff)
downloadqtlocation-mapboxgl-aa92e92595c385c5d7dcc88a361213818d715e73.tar.gz
support for style transitions
-rw-r--r--include/llmr/style/properties.hpp59
-rw-r--r--include/llmr/style/style.hpp51
-rw-r--r--include/llmr/style/style_parser.hpp1
-rw-r--r--include/llmr/util/transition.hpp31
-rw-r--r--src/map/map.cpp16
-rw-r--r--src/map/transform.cpp12
-rw-r--r--src/style/style.cpp970
-rw-r--r--src/style/style_parser.cpp99
-rw-r--r--src/util/raster.cpp2
-rw-r--r--src/util/transition.cpp63
10 files changed, 1189 insertions, 115 deletions
diff --git a/include/llmr/style/properties.hpp b/include/llmr/style/properties.hpp
index 04f1819b36..37c26b4700 100644
--- a/include/llmr/style/properties.hpp
+++ b/include/llmr/style/properties.hpp
@@ -5,12 +5,34 @@
#include <vector>
#include <string>
#include <limits>
+#include <set>
namespace llmr {
// Stores a premultiplied color, with all four channels ranging from 0..1
typedef std::array<float, 4> Color;
+struct PropertyTransition {
+ uint16_t duration = 0;
+ uint16_t delay = 0;
+};
+
+enum class TransitionablePropertyKey {
+ Translate = 1,
+ FillColor = 2,
+ StrokeColor = 3,
+ Opacity = 4,
+ Width = 5,
+ Offset = 6,
+ Color = 7,
+ DashArray = 8,
+ Radius = 9,
+ Blur = 10,
+ Halo = 11,
+ HaloRadius = 12,
+ HaloBlur = 13,
+};
+
enum class Winding {
EvenOdd = 1,
NonZero = 2
@@ -63,13 +85,19 @@ struct FunctionProperty {
struct IconClass {
FunctionProperty enabled = true;
std::array<FunctionProperty, 2> translate = {{ 0, 0 }};
+ PropertyTransition translate_transition;
TranslateAnchor translateAnchor = TranslateAnchor::Map;
FunctionProperty size;
Color color = {{ 1, 1, 1, 1 }};
+ PropertyTransition color_transition;
FunctionProperty opacity = 1;
+ PropertyTransition opacity_transition;
std::string image;
FunctionProperty radius = 0;
+ PropertyTransition radius_transition;
FunctionProperty blur = 0;
+ PropertyTransition blur_transition;
+ std::set<std::string> specifiers;
};
struct IconProperties {
@@ -87,12 +115,19 @@ struct IconProperties {
struct LineClass {
FunctionProperty enabled = true;
std::array<FunctionProperty, 2> translate = {{ 0, 0 }};
+ PropertyTransition translate_transition;
TranslateAnchor translateAnchor = TranslateAnchor::Map;
FunctionProperty width;
+ PropertyTransition width_transition;
FunctionProperty offset;
+ PropertyTransition offset_transition;
Color color = {{ 0, 0, 0, 1 }};
+ PropertyTransition color_transition;
std::array<FunctionProperty, 2> dash_array = {{ 1, -1 }};
+ PropertyTransition dash_array_transition;
FunctionProperty opacity = 1;
+ PropertyTransition opacity_transition;
+ std::set<std::string> specifiers;
};
struct LineProperties {
@@ -109,13 +144,18 @@ struct LineProperties {
struct FillClass {
FunctionProperty enabled = true;
std::array<FunctionProperty, 2> translate = {{ 0, 0 }};
+ PropertyTransition translate_transition;
TranslateAnchor translateAnchor = TranslateAnchor::Map;
Winding winding = Winding::NonZero;
FunctionProperty antialias = true;
Color fill_color = {{ 0, 0, 0, 1 }};
+ PropertyTransition fill_color_transition;
Color stroke_color = {{ 0, 0, 0, std::numeric_limits<float>::infinity() }};
+ PropertyTransition stroke_color_transition;
FunctionProperty opacity = 1;
+ PropertyTransition opacity_transition;
std::string image;
+ std::set<std::string> specifiers;
};
struct FillProperties {
@@ -133,15 +173,22 @@ struct FillProperties {
struct TextClass {
FunctionProperty enabled = true;
std::array<FunctionProperty, 2> translate = {{ 0, 0 }};
+ PropertyTransition translate_transition;
TranslateAnchor translateAnchor = TranslateAnchor::Map;
Color color = {{ 0, 0, 0, 1 }};
+ PropertyTransition color_transition;
Color halo = {{ 1, 1, 1, 0.75 }};
+ PropertyTransition halo_transition;
FunctionProperty halo_radius = 0.25f;
+ PropertyTransition halo_radius_transition;
FunctionProperty halo_blur = 1.0f;
+ PropertyTransition halo_blur_transition;
FunctionProperty size = 12.0f;
FunctionProperty rotate = 0.0f;
- FunctionProperty alwaysVisible = false;
+ FunctionProperty always_visible = false;
FunctionProperty opacity = 1;
+ PropertyTransition opacity_transition;
+ std::set<std::string> specifiers;
};
struct TextProperties {
@@ -154,13 +201,16 @@ struct TextProperties {
float halo_blur = 1.0f;
float size = 12.0f;
float rotate = 0.0f;
- bool alwaysVisible = false;
+ bool always_visible = false;
float opacity = 1.0;
};
struct BackgroundClass {
Color color = {{ 1, 1, 1, 1 }};
+ PropertyTransition color_transition;
FunctionProperty opacity = 1;
+ PropertyTransition opacity_transition;
+ std::set<std::string> specifiers;
};
struct BackgroundProperties {
@@ -171,7 +221,10 @@ struct BackgroundProperties {
struct RasterClass {
FunctionProperty enabled = true;
std::array<FunctionProperty, 2> translate = {{ 0, 0 }};
+ PropertyTransition translate_transition;
FunctionProperty opacity = 1;
+ PropertyTransition opacity_transition;
+ std::set<std::string> specifiers;
};
struct RasterProperties {
@@ -183,6 +236,8 @@ struct RasterProperties {
struct CompositeClass {
FunctionProperty enabled = true;
FunctionProperty opacity = 1;
+ PropertyTransition opacity_transition;
+ std::set<std::string> specifiers;
};
struct CompositeProperties {
diff --git a/include/llmr/style/style.hpp b/include/llmr/style/style.hpp
index 920d0c6c1c..256ff4d6be 100644
--- a/include/llmr/style/style.hpp
+++ b/include/llmr/style/style.hpp
@@ -8,6 +8,8 @@
#include <llmr/style/layer_description.hpp>
#include <llmr/style/class_description.hpp>
#include <llmr/geometry/sprite_atlas.hpp>
+#include <llmr/util/transition.hpp>
+#include <llmr/util/uv.hpp>
#include <map>
#include <vector>
@@ -32,6 +34,12 @@ public:
size_t layerCount() const;
void cascade(float z);
+ bool needsTransition() const;
+ void updateTransitions(time now);
+ void cancelTransitions();
+
+ void setDefaultTransitionDuration(uint64_t duration = 0);
+
public:
std::shared_ptr<Sprite> sprite;
@@ -40,8 +48,7 @@ public:
std::vector<LayerDescription> layers;
std::map<std::string, ClassDescription> classes;
-
- // This are applied settings.
+ // Currently applied settings.
std::set<std::string> appliedClasses;
struct {
BackgroundProperties background;
@@ -51,7 +58,47 @@ public:
std::map<std::string, TextProperties> texts;
std::map<std::string, RasterProperties> rasters;
std::map<std::string, CompositeProperties> composites;
+ std::map<std::string, std::map<TransitionablePropertyKey, std::string>> effective_classes;
} computed;
+
+private:
+ bool transitionInProgress(std::string layer_name, TransitionablePropertyKey key);
+ bool transitionExists(std::string layer_name, TransitionablePropertyKey key);
+ bool inNeedOfTransition(std::string layer_name, TransitionablePropertyKey key);
+ uint64_t transitionDuration(std::string layer_name, TransitionablePropertyKey key);
+ uint64_t transitionDelay(std::string layer_name, TransitionablePropertyKey key);
+
+private:
+ // Last applied settings.
+ struct {
+ BackgroundProperties background;
+ std::map<std::string, FillProperties> fills;
+ std::map<std::string, LineProperties> lines;
+ std::map<std::string, IconProperties> icons;
+ std::map<std::string, TextProperties> texts;
+ std::map<std::string, RasterProperties> rasters;
+ std::map<std::string, CompositeProperties> composites;
+ std::map<std::string, std::map<TransitionablePropertyKey, std::string>> effective_classes;
+ } previous;
+
+ // Settings values currently being transitioned.
+ struct {
+ BackgroundProperties background;
+ std::map<std::string, FillProperties> fills;
+ std::map<std::string, LineProperties> lines;
+ std::map<std::string, IconProperties> icons;
+ std::map<std::string, TextProperties> texts;
+ std::map<std::string, RasterProperties> rasters;
+ std::map<std::string, CompositeProperties> composites;
+ } transitioning;
+
+ std::map<std::string, std::map<TransitionablePropertyKey, PropertyTransition>> properties_to_transition;
+ std::map<std::string, std::map<TransitionablePropertyKey, std::shared_ptr<util::transition>>> transitions;
+ uint64_t default_transition_duration = 0;
+ bool initial_render_complete = false;
+
+ mutable uv::rwlock mtx;
+
};
}
diff --git a/include/llmr/style/style_parser.hpp b/include/llmr/style/style_parser.hpp
index 82dd7e9b59..6668048ff8 100644
--- a/include/llmr/style/style_parser.hpp
+++ b/include/llmr/style/style_parser.hpp
@@ -46,6 +46,7 @@ private:
Value parseValue(JSVal value);
FunctionProperty::fn parseFunctionType(JSVal type);
FunctionProperty parseFunction(JSVal value);
+ PropertyTransition parseTransition(JSVal value, std::string property_name);
private:
std::map<std::string, const rapidjson::Value *> constants;
diff --git a/include/llmr/util/transition.hpp b/include/llmr/util/transition.hpp
index cab73aab58..a981f6e83d 100644
--- a/include/llmr/util/transition.hpp
+++ b/include/llmr/util/transition.hpp
@@ -3,6 +3,7 @@
#include <llmr/util/noncopyable.hpp>
#include <llmr/util/time.hpp>
+#include <llmr/style/properties.hpp>
namespace llmr {
namespace util {
@@ -19,6 +20,9 @@ public:
duration(duration) {}
inline float progress(time now) const {
+ if (duration == 0) return 1;
+ if (start > now) return 0;
+
return (float)(now - start) / duration;
}
@@ -26,32 +30,35 @@ public:
virtual ~transition();
protected:
+ double interpolateDouble(double from, double to, double t) const;
+ float interpolateFloat(float from, float to, double t) const;
+ Color interpolateColor(Color from, Color to, double t) const;
+ std::array<float, 2> interpolateFloatArray(std::array<float, 2> from, std::array<float, 2> to, double t) const;
+
+protected:
const time start, duration;
};
+template <typename T>
class ease_transition : public transition {
public:
- // Disable automatic casts.
- template <typename T1, typename T2>
- inline ease_transition(double from, double to, double& value, T1 start, T2 duration) = delete;
+ ease_transition(T from, T to, T& value, time start, time duration)
+ : transition(start, duration),
+ from(from),
+ to(to),
+ value(value) {}
- // Actual constructor.
- ease_transition(double from, double to, double& value, time start, time duration);
state update(time now) const;
private:
- const double from, to;
- double& value;
+ const T from, to;
+ T& value;
+
};
template <typename T>
class timeout : public transition {
public:
- // Disable automatic casts.
- template <typename T1, typename T2>
- inline timeout(T final_value, T& value, T1 start, T2 duration) = delete;
-
- // Actual constructor.
timeout(T final_value, T& value, time start, time duration)
: transition(start, duration),
final_value(final_value),
diff --git a/src/map/map.cpp b/src/map/map.cpp
index 9df6ecb4b8..d70821380b 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -374,17 +374,15 @@ bool Map::getDebug() const {
}
void Map::toggleRaster() {
+ style.setDefaultTransitionDuration(300);
+ style.cancelTransitions();
+
auto it = sources.find("satellite");
if (it != sources.end()) {
Source &satellite_source = *it->second;
-
if (satellite_source.enabled) {
satellite_source.enabled = false;
-
- auto style_class = style.appliedClasses.find("satellite");
- if (style_class != style.appliedClasses.end()) {
- style.appliedClasses.erase(style_class);
- }
+ style.appliedClasses.erase("satellite");
} else {
satellite_source.enabled = true;
style.appliedClasses.insert("satellite");
@@ -458,6 +456,12 @@ void Map::prepare() {
style.cascade(state.getNormalizedZoom());
}
+ animationTime = util::now();
+ if (style.needsTransition()) {
+ style.updateTransitions(animationTime);
+ update();
+ }
+
// Allow the sprite atlas to potentially pull new sprite images if needed.
if (style.sprite && style.sprite->isLoaded()) {
spriteAtlas.update(*style.sprite);
diff --git a/src/map/transform.cpp b/src/map/transform.cpp
index fb3458adef..93942edadd 100644
--- a/src/map/transform.cpp
+++ b/src/map/transform.cpp
@@ -59,9 +59,9 @@ void Transform::_moveBy(const double dx, const double dy, const time duration) {
// Use a common start time for all of the transitions to avoid divergent transitions.
time start = util::now();
transitions.emplace_front(
- std::make_shared<util::ease_transition>(current.x, final.x, current.x, start, duration));
+ std::make_shared<util::ease_transition<double>>(current.x, final.x, current.x, start, duration));
transitions.emplace_front(
- std::make_shared<util::ease_transition>(current.y, final.y, current.y, start, duration));
+ std::make_shared<util::ease_transition<double>>(current.y, final.y, current.y, start, duration));
}
}
@@ -250,12 +250,12 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl
} else {
// Use a common start time for all of the transitions to avoid divergent transitions.
time start = util::now();
- transitions.emplace_front(std::make_shared<util::ease_transition>(
+ transitions.emplace_front(std::make_shared<util::ease_transition<double>>(
current.scale, final.scale, current.scale, start, duration));
transitions.emplace_front(
- std::make_shared<util::ease_transition>(current.x, final.x, current.x, start, duration));
+ std::make_shared<util::ease_transition<double>>(current.x, final.x, current.x, start, duration));
transitions.emplace_front(
- std::make_shared<util::ease_transition>(current.y, final.y, current.y, start, duration));
+ std::make_shared<util::ease_transition<double>>(current.y, final.y, current.y, start, duration));
}
const double s = final.scale * util::tileSize;
@@ -335,7 +335,7 @@ void Transform::_setAngle(double new_angle, const time duration) {
current.angle = final.angle;
} else {
time start = util::now();
- transitions.emplace_front(std::make_shared<util::ease_transition>(
+ transitions.emplace_front(std::make_shared<util::ease_transition<double>>(
current.angle, final.angle, current.angle, start, duration));
}
}
diff --git a/src/style/style.cpp b/src/style/style.cpp
index 54528f5271..b39a0bf5d5 100644
--- a/src/style/style.cpp
+++ b/src/style/style.cpp
@@ -19,135 +19,945 @@ void Style::reset() {
computed.texts.clear();
computed.rasters.clear();
computed.composites.clear();
+ computed.background.color = {{ 1, 1, 1, 1 }};
+ computed.background.opacity = 1.0;
+
+ properties_to_transition.clear();
}
void Style::cascade(float z) {
+ uv::writelock lock(mtx);
+
+ time start = util::now();
+
+ previous.fills = computed.fills;
+ previous.lines = computed.lines;
+ previous.icons = computed.icons;
+ previous.texts = computed.texts;
+ previous.rasters = computed.rasters;
+ previous.background = computed.background;
+
+ previous.effective_classes = computed.effective_classes;
+
reset();
// Accomodate for different tile size.
// TODO: Make this per-layer once individual layers have a specific tile size.
z += std::log(util::tileSize / 256.0f) / M_LN2;
- // Recalculate style
- // Basic cascading
+ // Recalculate style with basic cascading. Also store the last applied class
+ // for each property to assist in determining transitions.
for (const auto& class_pair : classes) {
const std::string& class_name = class_pair.first;
const ClassDescription& sheetClass = class_pair.second;
- // Not enabled
+ // Skip if not enabled.
if (appliedClasses.find(class_name) == appliedClasses.end()) continue;
- // Cascade fill classes
+ // Cascade fill classes.
for (const auto& fill_pair : sheetClass.fill) {
const std::string& layer_name = fill_pair.first;
const llmr::FillClass& layer = fill_pair.second;
- // TODO: This should be restricted to fill styles that have actual
- // values so as to not override with default values.
llmr::FillProperties& fill = computed.fills[layer_name];
- fill.enabled = layer.enabled.evaluate<bool>(z);
- fill.translate = {{ layer.translate[0].evaluate<float>(z),
- layer.translate[1].evaluate<float>(z) }};
- fill.translateAnchor = layer.translateAnchor;
- fill.winding = layer.winding;
- fill.antialias = layer.antialias.evaluate<bool>(z);
- fill.fill_color = layer.fill_color;
- fill.stroke_color = layer.stroke_color;
- fill.opacity = layer.opacity.evaluate<float>(z);
- fill.image = layer.image;
- }
-
- // Cascade line classes
+
+ if (layer.specifiers.count("enabled")) {
+ fill.enabled = layer.enabled.evaluate<bool>(z);
+ }
+
+ if (layer.specifiers.count("translate")) {
+ fill.translate = {{ layer.translate[0].evaluate<float>(z),
+ layer.translate[1].evaluate<float>(z) }};
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Translate] = class_name;
+ if (layer.translate_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Translate] = layer.translate_transition;
+ }
+ }
+
+ if (layer.specifiers.count("translate-anchor")) {
+ fill.translateAnchor = layer.translateAnchor;
+ }
+
+ if (layer.specifiers.count("winding")) {
+ fill.winding = layer.winding;
+ }
+
+ if (layer.specifiers.count("antialias")) {
+ fill.antialias = layer.antialias.evaluate<bool>(z);
+ }
+
+ if (layer.specifiers.count("color")) {
+ fill.fill_color = layer.fill_color;
+ computed.effective_classes[layer_name][TransitionablePropertyKey::FillColor] = class_name;
+ if (layer.fill_color_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::FillColor] = layer.fill_color_transition;
+ }
+ }
+
+ if (layer.specifiers.count("stroke")) {
+ fill.stroke_color = layer.stroke_color;
+ computed.effective_classes[layer_name][TransitionablePropertyKey::StrokeColor] = class_name;
+ if (layer.stroke_color_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::StrokeColor] = layer.stroke_color_transition;
+ }
+ }
+
+ if (layer.specifiers.count("opacity")) {
+ fill.opacity = layer.opacity.evaluate<float>(z);
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Opacity] = class_name;
+ if (layer.opacity_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Opacity] = layer.opacity_transition;
+ }
+ }
+
+ if (layer.specifiers.count("image")) {
+ fill.image = layer.image;
+ }
+ }
+
+ // Cascade line classes.
for (const auto& line_pair : sheetClass.line) {
const std::string& layer_name = line_pair.first;
const llmr::LineClass& layer = line_pair.second;
- // TODO: This should be restricted to line styles that have actual
- // values so as to not override with default values.
llmr::LineProperties& stroke = computed.lines[layer_name];
- stroke.enabled = layer.enabled.evaluate<bool>(z);
- stroke.translate = {{ layer.translate[0].evaluate<float>(z),
- layer.translate[1].evaluate<float>(z) }};
- stroke.translateAnchor = layer.translateAnchor;
- stroke.width = layer.width.evaluate<float>(z);
- stroke.offset = layer.offset.evaluate<float>(z);
- stroke.color = layer.color;
- stroke.dash_array = {{ layer.dash_array[0].evaluate<float>(z),
- layer.dash_array[1].evaluate<float>(z) }};
- stroke.opacity = layer.opacity.evaluate<float>(z);
- }
-
- // Cascade icon classes
+
+ if (layer.specifiers.count("enabled")) {
+ stroke.enabled = layer.enabled.evaluate<bool>(z);
+ }
+
+ if (layer.specifiers.count("translate")) {
+ stroke.translate = {{ layer.translate[0].evaluate<float>(z),
+ layer.translate[1].evaluate<float>(z) }};
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Translate] = class_name;
+ if (layer.translate_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Translate] = layer.translate_transition;
+ }
+ }
+
+ if (layer.specifiers.count("translate-anchor")) {
+ stroke.translateAnchor = layer.translateAnchor;
+ }
+
+ if (layer.specifiers.count("width")) {
+ stroke.width = layer.width.evaluate<float>(z);
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Width] = class_name;
+ if (layer.width_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Width] = layer.width_transition;
+ }
+
+ }
+
+ if (layer.specifiers.count("offset")) {
+ stroke.offset = layer.offset.evaluate<float>(z);
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Offset] = class_name;
+ if (layer.offset_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Offset] = layer.offset_transition;
+ }
+ }
+
+ if (layer.specifiers.count("color")) {
+ stroke.color = layer.color;
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Color] = class_name;
+ if (layer.color_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Color] = layer.color_transition;
+ }
+ }
+
+ if (layer.specifiers.count("dasharray")) {
+ stroke.dash_array = {{ layer.dash_array[0].evaluate<float>(z),
+ layer.dash_array[1].evaluate<float>(z) }};
+ computed.effective_classes[layer_name][TransitionablePropertyKey::DashArray] = class_name;
+ if (layer.dash_array_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::DashArray] = layer.dash_array_transition;
+ }
+ }
+
+ if (layer.specifiers.count("opacity")) {
+ stroke.opacity = layer.opacity.evaluate<float>(z);
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Opacity] = class_name;
+ if (layer.opacity_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Opacity] = layer.opacity_transition;
+ }
+ }
+ }
+
+ // Cascade icon classes.
for (const auto& icon_pair : sheetClass.icon) {
const std::string& layer_name = icon_pair.first;
const llmr::IconClass& layer = icon_pair.second;
- // TODO: This should be restricted to icon styles that have actual
- // values so as to not override with default values.
llmr::IconProperties& icon = computed.icons[layer_name];
- icon.enabled = layer.enabled.evaluate<bool>(z);
- icon.translate = {{ layer.translate[0].evaluate<float>(z),
- layer.translate[1].evaluate<float>(z) }};
- icon.translateAnchor = layer.translateAnchor;
- icon.color = layer.color;
- icon.size = layer.size.evaluate<float>(z);
- icon.opacity = layer.opacity.evaluate<float>(z);
- icon.image = layer.image;
- icon.radius = layer.radius.evaluate<float>(z);
- icon.blur = layer.blur.evaluate<float>(z);
- }
-
- // Cascade text classes
+
+ if (layer.specifiers.count("enabled")) {
+ icon.enabled = layer.enabled.evaluate<bool>(z);
+ }
+
+ if (layer.specifiers.count("translate")) {
+ icon.translate = {{ layer.translate[0].evaluate<float>(z),
+ layer.translate[1].evaluate<float>(z) }};
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Translate] = class_name;
+ if (layer.translate_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Translate] = layer.translate_transition;
+ }
+ }
+
+ if (layer.specifiers.count("translate-anchor")) {
+ icon.translateAnchor = layer.translateAnchor;
+ }
+
+ if (layer.specifiers.count("color")) {
+ icon.color = layer.color;
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Color] = class_name;
+ if (layer.color_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Color] = layer.color_transition;
+ }
+ }
+
+ if (layer.specifiers.count("size")) {
+ icon.size = layer.size.evaluate<float>(z);
+ }
+
+ if (layer.specifiers.count("opacity")) {
+ icon.opacity = layer.opacity.evaluate<float>(z);
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Opacity] = class_name;
+ if (layer.opacity_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Opacity] = layer.opacity_transition;
+ }
+ }
+
+ if (layer.specifiers.count("image")) {
+ icon.image = layer.image;
+ }
+
+ if (layer.specifiers.count("radius")) {
+ icon.radius = layer.radius.evaluate<float>(z);
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Radius] = class_name;
+ if (layer.radius_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Radius] = layer.radius_transition;
+ }
+ }
+
+ if (layer.specifiers.count("blur")) {
+ icon.blur = layer.blur.evaluate<float>(z);
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Blur] = class_name;
+ if (layer.blur_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Blur] = layer.blur_transition;
+ }
+ }
+ }
+
+ // Cascade text classes.
for (const auto& text_pair : sheetClass.text) {
const std::string& layer_name = text_pair.first;
const llmr::TextClass& layer = text_pair.second;
- // TODO: This should be restricted to text styles that have actual
- // values so as to not override with default values.
llmr::TextProperties& text = computed.texts[layer_name];
- text.enabled = layer.enabled.evaluate<bool>(z);
- text.translate = {{ layer.translate[0].evaluate<float>(z),
- layer.translate[1].evaluate<float>(z) }};
- text.translateAnchor = layer.translateAnchor;
- text.color = layer.color;
- text.size = layer.size.evaluate<float>(z);
- text.halo = layer.halo;
- text.halo_radius = layer.halo_radius.evaluate<float>(z);
- text.halo_blur = layer.halo_blur.evaluate<float>(z);
- text.rotate = layer.rotate.evaluate<float>(z);
- text.alwaysVisible = layer.alwaysVisible.evaluate<bool>(z);
- text.opacity = layer.opacity.evaluate<float>(z);
- }
-
- // Cascade raster classes
+
+ if (layer.specifiers.count("enabled")) {
+ text.enabled = layer.enabled.evaluate<bool>(z);
+ }
+
+ if (layer.specifiers.count("translate")) {
+ text.translate = {{ layer.translate[0].evaluate<float>(z),
+ layer.translate[1].evaluate<float>(z) }};
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Translate] = class_name;
+ if (layer.translate_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Translate] = layer.translate_transition;
+ }
+ }
+
+ if (layer.specifiers.count("translate-anchor")) {
+ text.translateAnchor = layer.translateAnchor;
+ }
+
+ if (layer.specifiers.count("color")) {
+ text.color = layer.color;
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Color] = class_name;
+ if (layer.color_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Color] = layer.color_transition;
+ }
+ }
+
+ if (layer.specifiers.count("size")) {
+ text.size = layer.size.evaluate<float>(z);
+ }
+
+ if (layer.specifiers.count("stroke")) {
+ text.halo = layer.halo;
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Halo] = class_name;
+ if (layer.halo_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Halo] = layer.halo_transition;
+ }
+ }
+
+ if (layer.specifiers.count("strokeWidth")) {
+ text.halo_radius = layer.halo_radius.evaluate<float>(z);
+ computed.effective_classes[layer_name][TransitionablePropertyKey::HaloRadius] = class_name;
+ if (layer.halo_radius_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::HaloRadius] = layer.halo_radius_transition;
+ }
+ }
+
+ if (layer.specifiers.count("strokeBlur")) {
+ text.halo_blur = layer.halo_blur.evaluate<float>(z);
+ computed.effective_classes[layer_name][TransitionablePropertyKey::HaloBlur] = class_name;
+ if (layer.halo_blur_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::HaloBlur] = layer.halo_blur_transition;
+ }
+ }
+
+ if (layer.specifiers.count("rotate")) {
+ text.rotate = layer.rotate.evaluate<float>(z);
+ }
+
+ if (layer.specifiers.count("alwaysVisible")) {
+ text.always_visible = layer.always_visible.evaluate<bool>(z);
+ }
+
+ if (layer.specifiers.count("opacity")) {
+ text.opacity = layer.opacity.evaluate<float>(z);
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Opacity] = class_name;
+ if (layer.opacity_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Opacity] = layer.opacity_transition;
+ }
+ }
+ }
+
+ // Cascade raster classes.
for (const auto& raster_pair : sheetClass.raster) {
const std::string& layer_name = raster_pair.first;
const llmr::RasterClass& layer = raster_pair.second;
- // TODO: This should be restricted to raster styles that have actual
- // values so as to not override with default values.
llmr::RasterProperties& raster = computed.rasters[layer_name];
- raster.enabled = layer.enabled.evaluate<bool>(z);
- raster.opacity = layer.opacity.evaluate<float>(z);
+
+ if (layer.specifiers.count("enabled")) {
+ raster.enabled = layer.enabled.evaluate<bool>(z);
+ }
+
+ if (layer.specifiers.count("opacity")) {
+ raster.opacity = layer.opacity.evaluate<float>(z);
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Opacity] = class_name;
+ if (layer.opacity_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Opacity] = layer.opacity_transition;
+ }
+ }
}
- // Cascade composite classes
+ // Cascade composite classes.
for (const auto& composite_pair : sheetClass.composite) {
const std::string& layer_name = composite_pair.first;
const llmr::CompositeClass& layer = composite_pair.second;
- // TODO: This should be restricted to composite styles that have actual
- // values so as to not override with default values.
llmr::CompositeProperties& composite = computed.composites[layer_name];
- composite.enabled = layer.enabled.evaluate<bool>(z);
- composite.opacity = layer.opacity.evaluate<float>(z);
+
+ if (layer.specifiers.count("enabled")) {
+ composite.enabled = layer.enabled.evaluate<bool>(z);
+ }
+
+ if (layer.specifiers.count("opacity")) {
+ composite.opacity = layer.opacity.evaluate<float>(z);
+ computed.effective_classes[layer_name][TransitionablePropertyKey::Opacity] = class_name;
+ if (layer.opacity_transition.duration) {
+ properties_to_transition[layer_name][TransitionablePropertyKey::Opacity] = layer.opacity_transition;
+ }
+ }
+ }
+
+ // Cascade background.
+ {
+ if (sheetClass.background.specifiers.count("color")) {
+ computed.background.color = sheetClass.background.color;
+ computed.effective_classes["background"][TransitionablePropertyKey::Color] = class_name;
+ if (sheetClass.background.color_transition.duration) {
+ properties_to_transition["background"][TransitionablePropertyKey::Color] = sheetClass.background.color_transition;
+ }
+ }
+ if (sheetClass.background.specifiers.count("opacity")) {
+ computed.background.opacity = sheetClass.background.opacity.evaluate<float>(z);
+ computed.effective_classes["background"][TransitionablePropertyKey::Opacity] = class_name;
+ if (sheetClass.background.opacity_transition.duration) {
+ properties_to_transition["background"][TransitionablePropertyKey::Opacity] = sheetClass.background.opacity_transition;
+ }
+ }
+ }
+ }
+
+ // Apply transitions after the first time.
+ if (!initial_render_complete) {
+ initial_render_complete = true;
+ return;
+ }
+
+ // Fills
+ for (const auto& fill_pair : computed.fills) {
+ const std::string& layer_name = fill_pair.first;
+
+ // translate
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Translate)) {
+
+ computed.fills[layer_name].translate = transitioning.fills[layer_name].translate;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Translate)) {
+
+ transitioning.fills[layer_name].translate = previous.fills[layer_name].translate;
+
+ transitions[layer_name][TransitionablePropertyKey::Translate] =
+ std::make_shared<util::ease_transition<std::array<float, 2>>>(previous.fills[layer_name].translate,
+ computed.fills[layer_name].translate,
+ transitioning.fills[layer_name].translate,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Translate),
+ transitionDuration(layer_name, TransitionablePropertyKey::Translate));
+
+ computed.fills[layer_name].translate = transitioning.fills[layer_name].translate;
+ }
+
+ // fill color
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::FillColor)) {
+
+ computed.fills[layer_name].fill_color = transitioning.fills[layer_name].fill_color;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::FillColor)) {
+
+ transitioning.fills[layer_name].fill_color = previous.fills[layer_name].fill_color;
+
+ transitions[layer_name][TransitionablePropertyKey::FillColor] =
+ std::make_shared<util::ease_transition<Color>>(previous.fills[layer_name].fill_color,
+ computed.fills[layer_name].fill_color,
+ transitioning.fills[layer_name].fill_color,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::FillColor),
+ transitionDuration(layer_name, TransitionablePropertyKey::FillColor));
+
+ computed.fills[layer_name].fill_color = transitioning.fills[layer_name].fill_color;
+ }
+
+ // stroke color
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::StrokeColor)) {
+
+ computed.fills[layer_name].stroke_color = transitioning.fills[layer_name].stroke_color;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::StrokeColor)) {
+
+ transitioning.fills[layer_name].stroke_color = previous.fills[layer_name].stroke_color;
+
+ transitions[layer_name][TransitionablePropertyKey::StrokeColor] =
+ std::make_shared<util::ease_transition<Color>>(previous.fills[layer_name].stroke_color,
+ computed.fills[layer_name].stroke_color,
+ transitioning.fills[layer_name].stroke_color,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::StrokeColor),
+ transitionDuration(layer_name, TransitionablePropertyKey::StrokeColor));
+
+ computed.fills[layer_name].stroke_color = transitioning.fills[layer_name].stroke_color;
}
+ // opacity
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Opacity)) {
+
+ computed.fills[layer_name].opacity = transitioning.fills[layer_name].opacity;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Opacity)) {
- // Cascade background
- computed.background.color = sheetClass.background.color;
- computed.background.opacity = sheetClass.background.opacity.evaluate<float>(z);
+ transitioning.fills[layer_name].opacity = previous.fills[layer_name].opacity;
+
+ transitions[layer_name][TransitionablePropertyKey::Opacity] =
+ std::make_shared<util::ease_transition<float>>(previous.fills[layer_name].opacity,
+ computed.fills[layer_name].opacity,
+ transitioning.fills[layer_name].opacity,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Opacity),
+ transitionDuration(layer_name, TransitionablePropertyKey::Opacity));
+
+ computed.fills[layer_name].opacity = transitioning.fills[layer_name].opacity;
+ }
}
+
+ // Lines
+ for (const auto& line_pair : computed.lines) {
+ const std::string& layer_name = line_pair.first;
+
+ // translate
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Translate)) {
+
+ computed.lines[layer_name].translate = transitioning.lines[layer_name].translate;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Translate)) {
+
+ transitioning.lines[layer_name].translate = previous.lines[layer_name].translate;
+
+ transitions[layer_name][TransitionablePropertyKey::Translate] =
+ std::make_shared<util::ease_transition<std::array<float, 2>>>(previous.lines[layer_name].translate,
+ computed.lines[layer_name].translate,
+ transitioning.lines[layer_name].translate,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Translate),
+ transitionDuration(layer_name, TransitionablePropertyKey::Translate));
+
+ computed.lines[layer_name].translate = transitioning.lines[layer_name].translate;
+ }
+
+ // width
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Width)) {
+
+ computed.lines[layer_name].width = transitioning.lines[layer_name].width;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Width)) {
+
+ transitioning.lines[layer_name].width = previous.lines[layer_name].width;
+
+ transitions[layer_name][TransitionablePropertyKey::Width] =
+ std::make_shared<util::ease_transition<float>>(previous.lines[layer_name].width,
+ computed.lines[layer_name].width,
+ transitioning.lines[layer_name].width,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Width),
+ transitionDuration(layer_name, TransitionablePropertyKey::Width));
+
+ computed.lines[layer_name].width = transitioning.lines[layer_name].width;
+ }
+
+ // offset
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Offset)) {
+
+ computed.lines[layer_name].offset = transitioning.lines[layer_name].offset;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Offset)) {
+
+ transitioning.lines[layer_name].offset = previous.lines[layer_name].offset;
+
+ transitions[layer_name][TransitionablePropertyKey::Offset] =
+ std::make_shared<util::ease_transition<float>>(previous.lines[layer_name].offset,
+ computed.lines[layer_name].offset,
+ transitioning.lines[layer_name].offset,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Offset),
+ transitionDuration(layer_name, TransitionablePropertyKey::Offset));
+
+ computed.lines[layer_name].offset = transitioning.lines[layer_name].offset;
+ }
+
+ // color
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Color)) {
+
+ computed.lines[layer_name].color = transitioning.lines[layer_name].color;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Color)) {
+
+ transitioning.lines[layer_name].color = previous.lines[layer_name].color;
+
+ transitions[layer_name][TransitionablePropertyKey::Color] =
+ std::make_shared<util::ease_transition<Color>>(previous.lines[layer_name].color,
+ computed.lines[layer_name].color,
+ transitioning.lines[layer_name].color,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Color),
+ transitionDuration(layer_name, TransitionablePropertyKey::Color));
+
+ computed.lines[layer_name].color = transitioning.lines[layer_name].color;
+ }
+
+ // dasharray
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::DashArray)) {
+
+ computed.lines[layer_name].dash_array = transitioning.lines[layer_name].dash_array;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::DashArray)) {
+
+ transitioning.lines[layer_name].dash_array = previous.lines[layer_name].dash_array;
+
+ transitions[layer_name][TransitionablePropertyKey::DashArray] =
+ std::make_shared<util::ease_transition<std::array<float, 2>>>(previous.lines[layer_name].dash_array,
+ computed.lines[layer_name].dash_array,
+ transitioning.lines[layer_name].dash_array,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::DashArray),
+ transitionDuration(layer_name, TransitionablePropertyKey::DashArray));
+
+ computed.lines[layer_name].dash_array = transitioning.lines[layer_name].dash_array;
+ }
+
+ // opacity
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Opacity)) {
+
+ computed.lines[layer_name].opacity = transitioning.lines[layer_name].opacity;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Opacity)) {
+
+ transitioning.lines[layer_name].opacity = previous.lines[layer_name].opacity;
+
+ transitions[layer_name][TransitionablePropertyKey::Opacity] =
+ std::make_shared<util::ease_transition<float>>(previous.lines[layer_name].opacity,
+ computed.lines[layer_name].opacity,
+ transitioning.lines[layer_name].opacity,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Opacity),
+ transitionDuration(layer_name, TransitionablePropertyKey::Opacity));
+
+ computed.lines[layer_name].opacity = transitioning.lines[layer_name].opacity;
+ }
+ }
+
+ // Icons
+ for (const auto& icon_pair : computed.icons) {
+ const std::string& layer_name = icon_pair.first;
+
+ // translate
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Translate)) {
+
+ computed.icons[layer_name].translate = transitioning.icons[layer_name].translate;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Translate)) {
+
+ transitioning.icons[layer_name].translate = previous.icons[layer_name].translate;
+
+ transitions[layer_name][TransitionablePropertyKey::Translate] =
+ std::make_shared<util::ease_transition<std::array<float, 2>>>(previous.icons[layer_name].translate,
+ computed.icons[layer_name].translate,
+ transitioning.icons[layer_name].translate,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Translate),
+ transitionDuration(layer_name, TransitionablePropertyKey::Translate));
+
+ computed.icons[layer_name].translate = transitioning.icons[layer_name].translate;
+ }
+
+ // color
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Color)) {
+
+ computed.icons[layer_name].color = transitioning.icons[layer_name].color;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Color)) {
+
+ transitioning.icons[layer_name].color = previous.icons[layer_name].color;
+
+ transitions[layer_name][TransitionablePropertyKey::Color] =
+ std::make_shared<util::ease_transition<Color>>(previous.icons[layer_name].color,
+ computed.icons[layer_name].color,
+ transitioning.icons[layer_name].color,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Color),
+ transitionDuration(layer_name, TransitionablePropertyKey::Color));
+
+ computed.icons[layer_name].color = transitioning.icons[layer_name].color;
+ }
+
+ // opacity
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Opacity)) {
+
+ computed.icons[layer_name].opacity = transitioning.icons[layer_name].opacity;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Opacity)) {
+
+ transitioning.icons[layer_name].opacity = previous.icons[layer_name].opacity;
+
+ transitions[layer_name][TransitionablePropertyKey::Opacity] =
+ std::make_shared<util::ease_transition<float>>(previous.icons[layer_name].opacity,
+ computed.icons[layer_name].opacity,
+ transitioning.icons[layer_name].opacity,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Opacity),
+ transitionDuration(layer_name, TransitionablePropertyKey::Opacity));
+
+ computed.icons[layer_name].opacity = transitioning.icons[layer_name].opacity;
+ }
+
+ // radius
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Radius)) {
+
+ computed.icons[layer_name].radius = transitioning.icons[layer_name].radius;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Radius)) {
+
+ transitioning.icons[layer_name].radius = previous.icons[layer_name].radius;
+
+ transitions[layer_name][TransitionablePropertyKey::Radius] =
+ std::make_shared<util::ease_transition<float>>(previous.icons[layer_name].radius,
+ computed.icons[layer_name].radius,
+ transitioning.icons[layer_name].radius,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Radius),
+ transitionDuration(layer_name, TransitionablePropertyKey::Radius));
+
+ computed.icons[layer_name].radius = transitioning.icons[layer_name].radius;
+ }
+
+ // blur
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Blur)) {
+
+ computed.icons[layer_name].blur = transitioning.icons[layer_name].blur;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Blur)) {
+
+ transitioning.icons[layer_name].blur = previous.icons[layer_name].blur;
+
+ transitions[layer_name][TransitionablePropertyKey::Blur] =
+ std::make_shared<util::ease_transition<float>>(previous.icons[layer_name].blur,
+ computed.icons[layer_name].blur,
+ transitioning.icons[layer_name].blur,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Blur),
+ transitionDuration(layer_name, TransitionablePropertyKey::Blur));
+
+ computed.icons[layer_name].blur = transitioning.icons[layer_name].blur;
+ }
+ }
+
+ // Text
+ for (const auto& text_pair : computed.texts) {
+ const std::string& layer_name = text_pair.first;
+
+ // translate
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Translate)) {
+
+ computed.texts[layer_name].translate = transitioning.texts[layer_name].translate;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Translate)) {
+
+ transitioning.texts[layer_name].translate = previous.texts[layer_name].translate;
+
+ transitions[layer_name][TransitionablePropertyKey::Translate] =
+ std::make_shared<util::ease_transition<std::array<float, 2>>>(previous.texts[layer_name].translate,
+ computed.texts[layer_name].translate,
+ transitioning.texts[layer_name].translate,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Translate),
+ transitionDuration(layer_name, TransitionablePropertyKey::Translate));
+
+ computed.texts[layer_name].translate = transitioning.texts[layer_name].translate;
+ }
+
+ // color
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Color)) {
+
+ computed.texts[layer_name].color = transitioning.texts[layer_name].color;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Color)) {
+
+ transitioning.texts[layer_name].color = previous.texts[layer_name].color;
+
+ transitions[layer_name][TransitionablePropertyKey::Color] =
+ std::make_shared<util::ease_transition<Color>>(previous.texts[layer_name].color,
+ computed.texts[layer_name].color,
+ transitioning.texts[layer_name].color,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Color),
+ transitionDuration(layer_name, TransitionablePropertyKey::Color));
+
+ computed.texts[layer_name].color = transitioning.texts[layer_name].color;
+ }
+
+ // halo
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Halo)) {
+
+ computed.texts[layer_name].halo = transitioning.texts[layer_name].halo;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Halo)) {
+
+ transitioning.texts[layer_name].halo = previous.texts[layer_name].halo;
+
+ transitions[layer_name][TransitionablePropertyKey::Halo] =
+ std::make_shared<util::ease_transition<Color>>(previous.texts[layer_name].halo,
+ computed.texts[layer_name].halo,
+ transitioning.texts[layer_name].halo,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Halo),
+ transitionDuration(layer_name, TransitionablePropertyKey::Halo));
+
+ computed.texts[layer_name].halo = transitioning.texts[layer_name].halo;
+ }
+
+ // halo radius
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::HaloRadius)) {
+
+ computed.texts[layer_name].halo_radius = transitioning.texts[layer_name].halo_radius;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::HaloRadius)) {
+
+ transitioning.texts[layer_name].halo_radius = previous.texts[layer_name].halo_radius;
+
+ transitions[layer_name][TransitionablePropertyKey::HaloRadius] =
+ std::make_shared<util::ease_transition<float>>(previous.texts[layer_name].halo_radius,
+ computed.texts[layer_name].halo_radius,
+ transitioning.texts[layer_name].halo_radius,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::HaloRadius),
+ transitionDuration(layer_name, TransitionablePropertyKey::HaloRadius));
+
+ computed.texts[layer_name].halo_radius = transitioning.texts[layer_name].halo_radius;
+ }
+
+ // halo blur
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::HaloBlur)) {
+
+ computed.texts[layer_name].halo_blur = transitioning.texts[layer_name].halo_blur;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::HaloBlur)) {
+
+ transitioning.texts[layer_name].halo_blur = previous.texts[layer_name].halo_blur;
+
+ transitions[layer_name][TransitionablePropertyKey::HaloBlur] =
+ std::make_shared<util::ease_transition<float>>(previous.texts[layer_name].halo_blur,
+ computed.texts[layer_name].halo_blur,
+ transitioning.texts[layer_name].halo_blur,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::HaloBlur),
+ transitionDuration(layer_name, TransitionablePropertyKey::HaloBlur));
+
+ computed.texts[layer_name].halo_blur = transitioning.texts[layer_name].halo_blur;
+ }
+
+ // opacity
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Opacity)) {
+
+ computed.texts[layer_name].opacity = transitioning.texts[layer_name].opacity;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Opacity)) {
+
+ transitioning.texts[layer_name].opacity = previous.texts[layer_name].opacity;
+
+ transitions[layer_name][TransitionablePropertyKey::Opacity] =
+ std::make_shared<util::ease_transition<float>>(previous.texts[layer_name].opacity,
+ computed.texts[layer_name].opacity,
+ transitioning.texts[layer_name].opacity,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Opacity),
+ transitionDuration(layer_name, TransitionablePropertyKey::Opacity));
+
+ computed.texts[layer_name].opacity = transitioning.texts[layer_name].opacity;
+ }
+ }
+
+ // Rasters
+ for (const auto& raster_pair : computed.rasters) {
+ const std::string& layer_name = raster_pair.first;
+
+ // opacity
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Opacity)) {
+
+ computed.rasters[layer_name].opacity = transitioning.rasters[layer_name].opacity;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Opacity)) {
+
+ transitioning.rasters[layer_name].opacity = previous.rasters[layer_name].opacity;
+
+ transitions[layer_name][TransitionablePropertyKey::Opacity] =
+ std::make_shared<util::ease_transition<float>>(previous.rasters[layer_name].opacity,
+ computed.rasters[layer_name].opacity,
+ transitioning.rasters[layer_name].opacity,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Opacity),
+ transitionDuration(layer_name, TransitionablePropertyKey::Opacity));
+
+ computed.rasters[layer_name].opacity = transitioning.rasters[layer_name].opacity;
+ }
+ }
+
+ // Composites
+ for (const auto& composite_pair : computed.composites) {
+ const std::string& layer_name = composite_pair.first;
+
+ // opacity
+ if (transitionInProgress(layer_name, TransitionablePropertyKey::Opacity)) {
+
+ computed.composites[layer_name].opacity = transitioning.composites[layer_name].opacity;
+
+ } else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Opacity)) {
+
+ transitioning.composites[layer_name].opacity = previous.composites[layer_name].opacity;
+
+ transitions[layer_name][TransitionablePropertyKey::Opacity] =
+ std::make_shared<util::ease_transition<float>>(previous.composites[layer_name].opacity,
+ computed.composites[layer_name].opacity,
+ transitioning.composites[layer_name].opacity,
+ start + transitionDelay(layer_name, TransitionablePropertyKey::Opacity),
+ transitionDuration(layer_name, TransitionablePropertyKey::Opacity));
+
+ computed.composites[layer_name].opacity = transitioning.composites[layer_name].opacity;
+ }
+ }
+
+ // Background
+ {
+ // color
+ if (transitionInProgress("background", TransitionablePropertyKey::Color)) {
+
+ computed.background.color = transitioning.background.color;
+
+ } else if (inNeedOfTransition("background", TransitionablePropertyKey::Color)) {
+
+ transitioning.background.color = previous.background.color;
+
+ transitions["background"][TransitionablePropertyKey::Color] =
+ std::make_shared<util::ease_transition<Color>>(previous.background.color,
+ computed.background.color,
+ transitioning.background.color,
+ start + transitionDelay("background", TransitionablePropertyKey::Color),
+ transitionDuration("background", TransitionablePropertyKey::Color));
+
+ computed.background.color = transitioning.background.color;
+ }
+
+ // opacity
+ if (transitionInProgress("background", TransitionablePropertyKey::Opacity)) {
+
+ computed.background.opacity = transitioning.background.opacity;
+
+ } else if (inNeedOfTransition("background", TransitionablePropertyKey::Opacity)) {
+
+ transitioning.background.opacity = previous.background.opacity;
+
+ transitions["background"][TransitionablePropertyKey::Opacity] =
+ std::make_shared<util::ease_transition<float>>(previous.background.opacity,
+ computed.background.opacity,
+ transitioning.background.opacity,
+ start + transitionDelay("background", TransitionablePropertyKey::Opacity),
+ transitionDuration("background", TransitionablePropertyKey::Opacity));
+
+ computed.background.opacity = transitioning.background.opacity;
+ }
+ }
+}
+
+bool Style::transitionInProgress(std::string layer_name, TransitionablePropertyKey key) {
+ if (!transitionExists(layer_name, key)) return false;
+
+ return (transitions[layer_name].find(key)->second->update(util::now()) != util::transition::complete);
+}
+
+bool Style::transitionExists(std::string layer_name, TransitionablePropertyKey key) {
+ return (transitions[layer_name].count(key) != 0);
+}
+
+bool Style::inNeedOfTransition(std::string layer_name, TransitionablePropertyKey key) {
+ if (!transitionDuration(layer_name, key)) return false;
+ if (transitionExists(layer_name, key)) return false;
+
+ return (computed.effective_classes[layer_name][key] != previous.effective_classes[layer_name][key]);
+}
+
+uint64_t Style::transitionDuration(std::string layer_name, TransitionablePropertyKey key) {
+ return (properties_to_transition[layer_name].count(key) ?
+ properties_to_transition[layer_name][key].duration :
+ default_transition_duration) * 1_millisecond;
+}
+
+uint64_t Style::transitionDelay(std::string layer_name, TransitionablePropertyKey key) {
+ return (properties_to_transition[layer_name][key].delay * 1_millisecond);
+}
+
+bool Style::needsTransition() const {
+ uv::readlock lock(mtx);
+
+ for (auto layer_it = transitions.begin(); layer_it != transitions.end(); layer_it++) {
+ auto& layer_transitions = layer_it->second;
+ if (layer_transitions.size()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Style::updateTransitions(time now) {
+ uv::writelock lock(mtx);
+
+ for (auto layer_it = transitions.begin(); layer_it != transitions.end(); layer_it++) {
+ auto& layer_transitions = layer_it->second;
+ for (auto prop_it = layer_transitions.begin(); prop_it != layer_transitions.end();/**/) {
+ auto& transition = prop_it->second;
+ if (transition->update(now) == util::transition::complete) {
+ prop_it = layer_transitions.erase(prop_it);
+ } else {
+ prop_it++;
+ }
+ }
+ }
+}
+
+void Style::cancelTransitions() {
+ uv::writelock lock(mtx);
+
+ transitions.clear();
}
size_t Style::layerCount() const {
@@ -158,6 +968,10 @@ size_t Style::layerCount() const {
return count;
}
+void Style::setDefaultTransitionDuration(uint64_t duration) {
+ default_transition_duration = duration;
+}
+
void Style::loadJSON(const uint8_t *const data, size_t bytes) {
rapidjson::Document doc;
diff --git a/src/style/style_parser.cpp b/src/style/style_parser.cpp
index 2198d5c369..feef6e3e15 100644
--- a/src/style/style_parser.cpp
+++ b/src/style/style_parser.cpp
@@ -477,42 +477,82 @@ FunctionProperty StyleParser::parseFunction(JSVal value) {
return property;
}
+PropertyTransition StyleParser::parseTransition(JSVal value, std::string property_name) {
+ uint16_t duration = 0, delay = 0;
+ std::string transition_property = std::string("transition-").append(property_name);
+ if (value.HasMember(transition_property.c_str())) {
+ JSVal elements = value[transition_property.c_str()];
+ if (elements.IsObject()) {
+ if (elements.HasMember("duration") && elements["duration"].IsNumber()) {
+ duration = elements["duration"].GetUint();
+ }
+ if (elements.HasMember("delay") && elements["delay"].IsNumber()) {
+ delay = elements["delay"].GetUint();
+ }
+ }
+ }
+
+ PropertyTransition transition;
+
+ transition.duration = duration;
+ transition.delay = delay;
+
+ return transition;
+}
+
FillClass StyleParser::parseFillClass(JSVal value) {
FillClass klass;
if (value.HasMember("enabled")) {
klass.enabled = parseFunction(value["enabled"]);
+ klass.specifiers.insert("enabled");
}
if (value.HasMember("translate")) {
std::vector<FunctionProperty> values = parseArray(value["translate"], 2);
klass.translate = std::array<FunctionProperty, 2> {{ values[0], values[1] }};
+ klass.translate_transition = parseTransition(value, "translate");
+ klass.specifiers.insert("translate");
}
if (value.HasMember("translate-anchor")) {
klass.translateAnchor = parseTranslateAnchor(value["translate-anchor"]);
+ klass.specifiers.insert("translate-anchor");
+ }
+
+ if (value.HasMember("winding")) {
+ throw std::runtime_error("winding in stylesheets not yet supported");
}
if (value.HasMember("color")) {
klass.fill_color = parseColor(value["color"]);
+ klass.fill_color_transition = parseTransition(value, "color");
+ klass.specifiers.insert("color");
}
if (value.HasMember("stroke")) {
klass.stroke_color = parseColor(value["stroke"]);
+ klass.stroke_color_transition = parseTransition(value, "stroke");
+ klass.specifiers.insert("stroke");
} else {
klass.stroke_color = klass.fill_color;
+ klass.specifiers.insert("stroke");
}
if (value.HasMember("antialias")) {
klass.antialias = parseBoolean(value["antialias"]);
+ klass.specifiers.insert("antialias");
}
if (value.HasMember("image")) {
klass.image = parseString(value["image"]);
+ klass.specifiers.insert("image");
}
if (value.HasMember("opacity")) {
klass.opacity = parseFunction(value["opacity"]);
+ klass.opacity_transition = parseTransition(value, "opacity");
+ klass.specifiers.insert("opacity");
}
return klass;
@@ -523,32 +563,44 @@ LineClass StyleParser::parseLineClass(JSVal value) {
if (value.HasMember("enabled")) {
klass.enabled = parseFunction(value["enabled"]);
+ klass.specifiers.insert("enabled");
}
if (value.HasMember("translate")) {
std::vector<FunctionProperty> values = parseArray(value["translate"], 2);
klass.translate = std::array<FunctionProperty, 2> {{ values[0], values[1] }};
+ klass.translate_transition = parseTransition(value, "translate");
+ klass.specifiers.insert("translate");
}
if (value.HasMember("translate-anchor")) {
klass.translateAnchor = parseTranslateAnchor(value["translate-anchor"]);
+ klass.specifiers.insert("translate-anchor");
}
if (value.HasMember("color")) {
klass.color = parseColor(value["color"]);
+ klass.color_transition = parseTransition(value, "color");
+ klass.specifiers.insert("color");
}
if (value.HasMember("width")) {
klass.width = parseFunction(value["width"]);
+ klass.width_transition = parseTransition(value, "width");
+ klass.specifiers.insert("width");
}
if (value.HasMember("opacity")) {
klass.opacity = parseFunction(value["opacity"]);
+ klass.opacity_transition = parseTransition(value, "opacity");
+ klass.specifiers.insert("opacity");
}
if (value.HasMember("dasharray")) {
std::vector<FunctionProperty> values = parseArray(value["dasharray"], 2);
klass.dash_array = std::array<FunctionProperty, 2> {{ values[0], values[1] }};
+ klass.dash_array_transition = parseTransition(value, "dasharray");
+ klass.specifiers.insert("dasharray");
}
return klass;
@@ -559,39 +611,53 @@ IconClass StyleParser::parseIconClass(JSVal value) {
if (value.HasMember("enabled")) {
klass.enabled = parseFunction(value["enabled"]);
+ klass.specifiers.insert("enabled");
}
if (value.HasMember("translate")) {
std::vector<FunctionProperty> values = parseArray(value["translate"], 2);
klass.translate = std::array<FunctionProperty, 2> {{ values[0], values[1] }};
+ klass.translate_transition = parseTransition(value, "translate");
+ klass.specifiers.insert("translate");
}
if (value.HasMember("translate-anchor")) {
klass.translateAnchor = parseTranslateAnchor(value["translate-anchor"]);
+ klass.specifiers.insert("translate-anchor");
}
if (value.HasMember("color")) {
klass.color = parseColor(value["color"]);
+ klass.color_transition = parseTransition(value, "color");
+ klass.specifiers.insert("color");
}
if (value.HasMember("opacity")) {
klass.opacity = parseFunction(value["opacity"]);
+ klass.opacity_transition = parseTransition(value, "opacity");
+ klass.specifiers.insert("opacity");
}
if (value.HasMember("image")) {
klass.image = parseString(value["image"]);
+ klass.specifiers.insert("image");
}
if (value.HasMember("size")) {
klass.size = parseFunction(value["size"]);
+ klass.specifiers.insert("size");
}
if (value.HasMember("radius")) {
klass.radius = parseFunction(value["radius"]);
+ klass.radius_transition = parseTransition(value, "radius");
+ klass.specifiers.insert("radius");
}
if (value.HasMember("blur")) {
klass.blur = parseFunction(value["blur"]);
+ klass.blur_transition = parseTransition(value, "blur");
+ klass.specifiers.insert("blur");
}
return klass;
@@ -602,43 +668,64 @@ TextClass StyleParser::parseTextClass(JSVal value) {
if (value.HasMember("enabled")) {
klass.enabled = parseFunction(value["enabled"]);
+ klass.specifiers.insert("enabled");
}
if (value.HasMember("translate")) {
std::vector<FunctionProperty> values = parseArray(value["translate"], 2);
klass.translate = std::array<FunctionProperty, 2> {{ values[0], values[1] }};
+ klass.translate_transition = parseTransition(value, "translate");
+ klass.specifiers.insert("translate");
}
if (value.HasMember("translate-anchor")) {
klass.translateAnchor = parseTranslateAnchor(value["translate-anchor"]);
+ klass.specifiers.insert("translate-anchor");
}
if (value.HasMember("color")) {
klass.color = parseColor(value["color"]);
+ klass.color_transition = parseTransition(value, "color");
+ klass.specifiers.insert("color");
}
if (value.HasMember("stroke")) {
klass.halo = parseColor(value["stroke"]);
+ klass.halo_transition = parseTransition(value, "stroke");
+ klass.specifiers.insert("stroke");
}
if (value.HasMember("strokeWidth")) {
klass.halo_radius = parseFunction(value["strokeWidth"]);
+ klass.halo_radius_transition = parseTransition(value, "strokeWidth");
+ klass.specifiers.insert("strokeWidth");
}
if (value.HasMember("strokeBlur")) {
klass.halo_blur = parseFunction(value["strokeBlur"]);
+ klass.halo_blur_transition = parseTransition(value, "strokeBlur");
+ klass.specifiers.insert("strokeBlur");
}
if (value.HasMember("size")) {
klass.size = parseFunction(value["size"]);
+ klass.specifiers.insert("size");
}
if (value.HasMember("rotate")) {
klass.rotate = parseFunction(value["rotate"]);
+ klass.specifiers.insert("rotate");
}
if (value.HasMember("alwaysVisible")) {
- klass.alwaysVisible = parseFunction(value["alwaysVisible"]);
+ klass.always_visible = parseFunction(value["alwaysVisible"]);
+ klass.specifiers.insert("alwaysVisible");
+ }
+
+ if (value.HasMember("opacity")) {
+ klass.opacity = parseFunction(value["opacity"]);
+ klass.opacity_transition = parseTransition(value, "opacity");
+ klass.specifiers.insert("opacity");
}
return klass;
@@ -649,10 +736,13 @@ RasterClass StyleParser::parseRasterClass(JSVal value) {
if (value.HasMember("enabled")) {
klass.enabled = parseFunction(value["enabled"]);
+ klass.specifiers.insert("enabled");
}
if (value.HasMember("opacity")) {
klass.opacity = parseFunction(value["opacity"]);
+ klass.opacity_transition = parseTransition(value, "opacity");
+ klass.specifiers.insert("opacity");
}
return klass;
@@ -663,10 +753,13 @@ CompositeClass StyleParser::parseCompositeClass(JSVal value) {
if (value.HasMember("enabled")) {
klass.enabled = parseFunction(value["enabled"]);
+ klass.specifiers.insert("enabled");
}
if (value.HasMember("opacity")) {
klass.opacity = parseFunction(value["opacity"]);
+ klass.opacity_transition = parseTransition(value, "opacity");
+ klass.specifiers.insert("opacity");
}
return klass;
@@ -677,10 +770,14 @@ BackgroundClass StyleParser::parseBackgroundClass(JSVal value) {
if (value.HasMember("color")) {
klass.color = parseColor(value["color"]);
+ klass.color_transition = parseTransition(value, "color");
+ klass.specifiers.insert("color");
}
if (value.HasMember("opacity")) {
klass.opacity = parseFunction(value["opacity"]);
+ klass.opacity_transition = parseTransition(value, "opacity");
+ klass.specifiers.insert("opacity");
}
return klass;
diff --git a/src/util/raster.cpp b/src/util/raster.cpp
index 4f26f8b558..e08145231f 100644
--- a/src/util/raster.cpp
+++ b/src/util/raster.cpp
@@ -70,7 +70,7 @@ void Raster::bind(bool linear) {
void Raster::beginFadeInTransition() {
time start = util::now();
- fade_transition = std::make_shared<util::ease_transition>(opacity, 1.0, opacity, start, 250_milliseconds);
+ fade_transition = std::make_shared<util::ease_transition<double>>(opacity, 1.0, opacity, start, 250_milliseconds);
}
bool Raster::needsTransition() const {
diff --git a/src/util/transition.cpp b/src/util/transition.cpp
index af5b5a70ac..d15e3ef66e 100644
--- a/src/util/transition.cpp
+++ b/src/util/transition.cpp
@@ -8,20 +8,69 @@ UnitBezier ease(0.25, 0.1, 0.25, 1);
transition::~transition() {}
-ease_transition::ease_transition(double from, double to, double &value, time start, time duration)
- : transition(start, duration),
- from(from),
- to(to),
- value(value) {
+double transition::interpolateDouble(double from, double to, double t) const {
+ return from + (to - from) * t;
}
-transition::state ease_transition::update(time now) const {
+float transition::interpolateFloat(float from, float to, double t) const {
+ return from + (to - from) * (float)t;
+}
+
+llmr::Color transition::interpolateColor(llmr::Color from, llmr::Color to, double t) const {
+ return {{ interpolateFloat(from[0], to[0], t),
+ interpolateFloat(from[1], to[1], t),
+ interpolateFloat(from[2], to[2], t),
+ interpolateFloat(from[3], to[3], t) }};
+}
+
+std::array<float, 2> transition::interpolateFloatArray(std::array<float, 2> from, std::array<float, 2> to, double t) const {
+ return {{ interpolateFloat(from[0], to[0], t), interpolateFloat(from[1], to[1], t) }};
+}
+
+template <>
+transition::state ease_transition<double>::update(llmr::time now) const {
+ float t = progress(now);
+ if (t >= 1) {
+ value = to;
+ return complete;
+ } else {
+ value = interpolateDouble(from, to, ease.solve(t, 0.001));
+ return running;
+ }
+}
+
+template <>
+transition::state ease_transition<llmr::Color>::update(llmr::time now) const {
+ float t = progress(now);
+ if (t >= 1) {
+ value = to;
+ return complete;
+ } else {
+ value = interpolateColor(from, to, ease.solve(t, 0.001));
+ return running;
+ }
+}
+
+template <>
+transition::state ease_transition<float>::update(llmr::time now) const {
+ float t = progress(now);
+ if (t >= 1) {
+ value = to;
+ return complete;
+ } else {
+ value = interpolateFloat(from, to, ease.solve(t, 0.001));
+ return running;
+ }
+}
+
+template <>
+transition::state ease_transition<std::array<float, 2>>::update(llmr::time now) const {
float t = progress(now);
if (t >= 1) {
value = to;
return complete;
} else {
- value = from + (to - from) * ease.solve(t, 0.001);
+ value = interpolateFloatArray(from, to, ease.solve(t, 0.001));
return running;
}
}