]> git.sesse.net Git - casparcg/blobdiff - core/producer/binding.h
[scene] Fixed double evaluation of expressions
[casparcg] / core / producer / binding.h
index d87576ece6098a2f5f621e0566214a76670a85fd..17c9cff199a40a306d3125b24d6bbd67d44acab7 100644 (file)
 #include <map>
 #include <algorithm>
 #include <type_traits>
+#include <stdexcept>
 
 #include <boost/lexical_cast.hpp>
+#include <boost/utility/value_init.hpp>
+
+#include <common/tweener.h>
+#include <common/except.h>
 
 namespace caspar { namespace core {
 
@@ -46,14 +51,14 @@ struct impl_base : std::enable_shared_from_this<impl_base>
        {
        }
 
-       virtual void evaluate() = 0;
+       virtual void evaluate() const = 0;
 
        void depend_on(const std::shared_ptr<impl_base>& dependency)
        {
                auto self = shared_from_this();
 
                if (dependency->depends_on(self))
-                       throw std::exception("Can't have circular dependencies between bindings");
+                       CASPAR_THROW_EXCEPTION(invalid_argument() << msg_info("Can't have circular dependencies between bindings"));
 
                dependency->on_change(self, [=] { evaluate(); });
                dependencies_.push_back(dependency);
@@ -65,9 +70,9 @@ struct impl_base : std::enable_shared_from_this<impl_base>
                {
                        if (dependency == other)
                                return true;
-                       
+
                        if (dependency->depends_on(other))
-                               return true;            
+                               return true;
                }
 
                return false;
@@ -90,11 +95,12 @@ private:
 
        struct impl : public detail::impl_base
        {
-               T value_;
+               mutable T                       value_;
                std::function<T ()> expression_;
+               mutable bool            evaluated_              = false;
 
                impl()
-                       : value_()
+                       : value_(boost::value_initialized<T>())
                {
                }
 
@@ -105,12 +111,16 @@ private:
 
                template<typename Expr>
                impl(const Expr& expression)
-                       : expression_(expression)
+                       : value_(boost::value_initialized<T>())
+                       , expression_(expression)
                {
                }
 
                T get() const
                {
+                       if (!evaluated_)
+                               evaluate();
+
                        return value_;
                }
 
@@ -122,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;
@@ -134,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_;
 
@@ -174,7 +186,7 @@ private:
 
                void unbind()
                {
-                       if (expression_)
+                       if (bound())
                        {
                                expression_ = std::function<T ()>();
                                dependencies_.clear();
@@ -201,7 +213,7 @@ public:
        explicit binding(const Expr& expression)
                : impl_(new impl(expression))
        {
-               impl_->evaluate();
+               //impl_->evaluate();
        }
 
        // Expr -> T ()
@@ -210,7 +222,7 @@ public:
                : impl_(new impl(expression))
        {
                depend_on(dep);
-               impl_->evaluate();
+               //impl_->evaluate();
        }
 
        // Expr -> T ()
@@ -223,7 +235,7 @@ public:
        {
                depend_on(dep1);
                depend_on(dep2);
-               impl_->evaluate();
+               //impl_->evaluate();
        }
 
        void* identity() const
@@ -451,7 +463,7 @@ public:
        // Func -> R (T self_val, T2 other_val)
        // Returns binding<R>
        template<typename Func, typename T2>
-       auto composed(const binding<T2> other, const Func& func) const -> binding<decltype(func(impl_->value_, other.impl_->value_))>
+       auto composed(const binding<T2>& other, const Func& func) const -> binding<decltype(func(impl_->value_, other.impl_->value_))>
        {
                typedef decltype(func(impl_->value_, other.impl_->value_)) R;
                auto self = impl_;
@@ -463,6 +475,62 @@ public:
                                other);
        }
 
+       template<typename TweenerFunc, typename FrameCounter>
+       binding<T> animated(
+                       const binding<FrameCounter>& frame_counter,
+                       const binding<FrameCounter>& duration,
+                       const binding<TweenerFunc>& 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<T>(
+                       [=] () 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();
@@ -481,7 +549,7 @@ public:
                std::shared_ptr<void> subscription(new char);
 
                on_change(subscription, listener);
-               
+
                return subscription;
        }
 private:
@@ -536,7 +604,7 @@ public:
 
                binding<T> 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);
@@ -574,7 +642,7 @@ public:
        }
 };
 
-template<typename T, typename T2>
+/*template<typename T, typename T2>
 binding<T> add_tween(
                const binding<T>& to_tween,
                const binding<T2>& counter,
@@ -583,7 +651,7 @@ binding<T> add_tween(
                const std::wstring& easing)
 {
        tweener tween(easing);
-       
+
        double start_val = to_tween.as<double>().get();
        double destination_val = static_cast<double>(destination_value);
        double start_time = counter.as<double>().get();
@@ -595,7 +663,7 @@ binding<T> add_tween(
                        return tween(t - start_time, start_val, destination_val, dur);
                }).as<T>())
                .otherwise(destination_value);
-}
+}*/
 
 template<typename T, typename T2>
 binding<T> delay(