X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=core%2Fproducer%2Fbinding.h;h=17c9cff199a40a306d3125b24d6bbd67d44acab7;hb=5a66feb95182b62ac3ec176ca9b05a29181bcd06;hp=2b1df5ba30955b03af884cd1627f49b3c11f0ae3;hpb=a362123326135239d1678bd921e7ffe298c9de5d;p=casparcg diff --git a/core/producer/binding.h b/core/producer/binding.h index 2b1df5ba3..17c9cff19 100644 --- a/core/producer/binding.h +++ b/core/producer/binding.h @@ -24,8 +24,17 @@ #include #include #include +#include #include #include +#include +#include + +#include +#include + +#include +#include namespace caspar { namespace core { @@ -42,14 +51,33 @@ struct impl_base : std::enable_shared_from_this { } - virtual void evaluate() = 0; + virtual void evaluate() const = 0; void depend_on(const std::shared_ptr& dependency) { - dependency->on_change(shared_from_this(), [=] { evaluate(); }); + auto self = shared_from_this(); + + if (dependency->depends_on(self)) + CASPAR_THROW_EXCEPTION(invalid_argument() << msg_info("Can't have circular dependencies between bindings")); + + dependency->on_change(self, [=] { evaluate(); }); dependencies_.push_back(dependency); } + bool depends_on(const std::shared_ptr& other) const + { + for (auto& dependency : dependencies_) + { + if (dependency == other) + return true; + + if (dependency->depends_on(other)) + return true; + } + + return false; + } + void on_change( const std::weak_ptr& dependant, const std::function& listener) const @@ -67,11 +95,12 @@ private: struct impl : public detail::impl_base { - T value_; + mutable T value_; std::function expression_; + mutable bool evaluated_ = false; impl() - : value_() + : value_(boost::value_initialized()) { } @@ -82,12 +111,16 @@ private: template impl(const Expr& expression) - : expression_(expression) + : value_(boost::value_initialized()) + , expression_(expression) { } T get() const { + if (!evaluated_) + evaluate(); + return value_; } @@ -99,9 +132,7 @@ private: void set(T value) { if (bound()) - { - throw std::exception("Bound value cannot be set"); - } + CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info("Bound value cannot be set")); if (value == value_) return; @@ -111,22 +142,26 @@ private: on_change(); } - void evaluate() override + void evaluate() const override { - if (expression_) + if (bound()) { auto new_value = expression_(); + evaluated_ = true; + if (new_value != value_) { value_ = new_value; on_change(); } } + else + evaluated_ = true; } using impl_base::on_change; - void on_change() + void on_change() const { auto copy = on_change_; @@ -151,7 +186,7 @@ private: void unbind() { - if (expression_) + if (bound()) { expression_ = std::function(); dependencies_.clear(); @@ -178,7 +213,7 @@ public: explicit binding(const Expr& expression) : impl_(new impl(expression)) { - impl_->evaluate(); + //impl_->evaluate(); } // Expr -> T () @@ -187,7 +222,7 @@ public: : impl_(new impl(expression)) { depend_on(dep); - impl_->evaluate(); + //impl_->evaluate(); } // Expr -> T () @@ -200,7 +235,7 @@ public: { depend_on(dep1); depend_on(dep2); - impl_->evaluate(); + //impl_->evaluate(); } void* identity() const @@ -386,11 +421,32 @@ public: } template - binding as() const + typename std::enable_if< + std::is_same::value, + binding + >::type as() const + { + return *this; + } + + template + typename std::enable_if< + (std::is_arithmetic::value || std::is_same::value) && (std::is_arithmetic::value || std::is_same::value) && !std::is_same::value, + binding + >::type as() const { return transformed([](T val) { return static_cast(val); }); } + template + typename std::enable_if< + (std::is_same::value || std::is_same::value) && !std::is_same::value, + binding + >::type as() const + { + return transformed([](T val) { return boost::lexical_cast(val); }); + } + // Func -> R (T self_val) // Returns binding template @@ -407,7 +463,7 @@ public: // Func -> R (T self_val, T2 other_val) // Returns binding template - auto composed(const binding other, const Func& func) const -> bindingvalue_, other.impl_->value_))> + auto composed(const binding& other, const Func& func) const -> bindingvalue_, other.impl_->value_))> { typedef decltype(func(impl_->value_, other.impl_->value_)) R; auto self = impl_; @@ -419,13 +475,69 @@ public: other); } + template + binding animated( + const binding& frame_counter, + const binding& duration, + const binding& tweener_func) const + { + auto self = impl_; + auto f = frame_counter.impl_; + auto d = duration.impl_; + auto tw = tweener_func.impl_; + FrameCounter start_frame = frame_counter.get(); + FrameCounter current_frame = start_frame; + T current_source = get(); + T current_destination = current_source; + T current_result = current_source; + + auto result = binding( + [=] () mutable + { + auto frame_diff = f->get() - current_frame; + bool new_frame = f->get() != current_frame; + + if (!new_frame) + return current_result; + + bool new_tween = current_destination != self->get(); + auto time = current_frame - start_frame + (new_tween ? frame_diff : 0) + 1; + auto dur = d->get(); + current_frame = f->get(); + + current_source = new_tween ? tw->get()(time, current_source, current_destination - current_source, dur) : current_source; + current_destination = self->get(); + + if (new_tween) + start_frame = current_frame; + + time = current_frame - start_frame; + + if (time < dur) + current_result = tw->get()(time, current_source, current_destination - current_source, dur); + else + { + current_result = current_destination; + current_source = current_destination; + } + + return current_result; + }); + + result.depend_on(*this); + result.depend_on(frame_counter); + result.depend_on(tweener_func); + + return result; + } + void unbind() { impl_->unbind(); } void on_change( - const std::weak_ptr dependant, + const std::weak_ptr& dependant, const std::function& listener) const { impl_->on_change(dependant, listener); @@ -437,7 +549,7 @@ public: std::shared_ptr subscription(new char); on_change(subscription, listener); - + return subscription; } private: @@ -452,11 +564,21 @@ static binding operator||(const binding& lhs, const binding& r return lhs.composed(rhs, [](bool lhs, bool rhs) { return lhs || rhs; }); } +static binding operator||(const binding& lhs, bool rhs) +{ + return lhs.transformed([rhs](bool lhs) { return lhs || rhs; }); +} + static binding operator&&(const binding& lhs, const binding& rhs) { return lhs.composed(rhs, [](bool lhs, bool rhs) { return lhs && rhs; }); } +static binding operator&&(const binding& lhs, bool rhs) +{ + return lhs.transformed([rhs](bool lhs) { return lhs && rhs; }); +} + static binding operator!(const binding& self) { return self.transformed([](bool self) { return !self; }); @@ -482,7 +604,7 @@ public: binding result([condition, true_result, false_result]() { - return condition.get() ? true_result.get() : false_result.get(); + return condition.get() ? true_result.get() : false_result.get(); }); result.depend_on(condition); @@ -520,7 +642,7 @@ public: } }; -template +/*template binding add_tween( const binding& to_tween, const binding& counter, @@ -529,7 +651,7 @@ binding add_tween( const std::wstring& easing) { tweener tween(easing); - + double start_val = to_tween.as().get(); double destination_val = static_cast(destination_value); double start_time = counter.as().get(); @@ -541,7 +663,7 @@ binding add_tween( return tween(t - start_time, start_val, destination_val, dur); }).as()) .otherwise(destination_value); -} +}*/ template binding delay(