#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 {
{
}
- 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::runtime_error("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);
{
if (dependency == other)
return true;
-
+
if (dependency->depends_on(other))
- return true;
+ return true;
}
return false;
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>())
{
}
template<typename Expr>
impl(const Expr& expression)
- : expression_(expression)
+ : value_(boost::value_initialized<T>())
+ , expression_(expression)
{
}
T get() const
{
+ if (!evaluated_)
+ evaluate();
+
return value_;
}
void set(T value)
{
if (bound())
- {
- throw std::runtime_error("Bound value cannot be set");
- }
+ CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info("Bound value cannot be set"));
if (value == value_)
return;
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_;
void unbind()
{
- if (expression_)
+ if (bound())
{
expression_ = std::function<T ()>();
dependencies_.clear();
explicit binding(const Expr& expression)
: impl_(new impl(expression))
{
- impl_->evaluate();
+ //impl_->evaluate();
}
// Expr -> T ()
: impl_(new impl(expression))
{
depend_on(dep);
- impl_->evaluate();
+ //impl_->evaluate();
}
// Expr -> T ()
{
depend_on(dep1);
depend_on(dep2);
- impl_->evaluate();
+ //impl_->evaluate();
}
void* identity() const
// 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_;
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();
std::shared_ptr<void> subscription(new char);
on_change(subscription, listener);
-
+
return subscription;
}
private:
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);
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();