]> git.sesse.net Git - casparcg/commitdiff
Added support for animated bindings in scene_producer.
authorHelge Norberg <helge.norberg@svt.se>
Tue, 22 Dec 2015 19:20:42 +0000 (20:20 +0100)
committerHelge Norberg <helge.norberg@svt.se>
Tue, 22 Dec 2015 19:20:42 +0000 (20:20 +0100)
core/producer/binding.h
core/producer/scene/expression_parser.cpp

index 20196fb344da632c5c2c4969c1d18d8c26e749a1..6912efe2d4529e367f7f87bbf0c54ea063955df9 100644 (file)
@@ -459,7 +459,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_;
@@ -471,6 +471,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();
index 5bc20be650f7c79a8f14be85eedf5351d62f4d6d..ab40286c3fb62123d96a0f518764429e25df2000 100644 (file)
@@ -35,6 +35,7 @@
 #include <common/log.h>
 #include <common/except.h>
 #include <common/utf.h>
+#include <common/tweener.h>
 
 namespace caspar { namespace core { namespace scene {
 
@@ -69,6 +70,19 @@ std::wstring at_position(
                        + L" in " + str;
 }
 
+template<typename T>
+binding<T> require(const boost::any& value)
+{
+       auto b = as_binding(value);
+
+       if (is<binding<T>>(b))
+               return as<binding<T>>(b);
+       else
+               CASPAR_THROW_EXCEPTION(user_error() << msg_info(
+                       L"Required binding of type " + u16(typeid(T).name())
+                       + L" but got " + u16(value.type().name())));
+}
+
 boost::any parse_expression(
                std::wstring::const_iterator& cursor,
                const std::wstring& str,
@@ -85,13 +99,76 @@ boost::any parse_parenthesis(
 
        auto expr = parse_expression(cursor, str, var_repo);
 
-       if (*cursor++ != L')')
+       if (next_non_whitespace(cursor, str, L"Expected )") != L')')
                CASPAR_THROW_EXCEPTION(user_error()
                                << msg_info(L"Expected )" + at_position(cursor, str)));
 
+       ++cursor;
+
        return expr;
 }
 
+boost::any create_animate_function(const std::vector<boost::any>& params, const variable_repository& var_repo)
+{
+       if (params.size() != 3)
+               CASPAR_THROW_EXCEPTION(user_error()
+                       << msg_info(L"animate() function requires three parameters: to_animate, duration, tweener"));
+
+       auto to_animate         = require<double>(params.at(0));
+       auto frame_counter      = var_repo(L"frame").as<int64_t>().as<double>();
+       auto duration           = require<double>(params.at(1));
+       auto tw                         = require<std::wstring>(params.at(2)).transformed([](const std::wstring& s) { return tweener(s); });
+
+       return to_animate.animated(frame_counter, duration, tw);
+}
+
+boost::any parse_function(
+               const std::wstring& function_name,
+               std::wstring::const_iterator& cursor,
+               const std::wstring& str,
+               const variable_repository& var_repo)
+{
+       static std::map<std::wstring, std::function<boost::any (const std::vector<boost::any>& params, const variable_repository& var_repo)>> FUNCTIONS
+       {
+               {L"animate", create_animate_function }
+       };
+
+       auto function = FUNCTIONS.find(function_name);
+
+       if (function == FUNCTIONS.end())
+               CASPAR_THROW_EXCEPTION(user_error()
+                               << msg_info(function_name + L"() is an unknown function" + at_position(cursor, str)));
+
+       if (*cursor++ != L'(')
+               CASPAR_THROW_EXCEPTION(user_error()
+                       << msg_info(L"Expected (" + at_position(cursor, str)));
+
+       std::vector<boost::any> params;
+
+       while (cursor != str.end())
+       {
+               params.push_back(parse_expression(cursor, str, var_repo));
+
+               auto next = next_non_whitespace(cursor, str, L"Expected , or )");
+
+               if (next == L')')
+                       break;
+               else if (next != L',')
+                       CASPAR_THROW_EXCEPTION(user_error()
+                               << msg_info(L"Expected ) or ," + at_position(cursor, str)));
+
+               ++cursor;
+       }
+
+       if (next_non_whitespace(cursor, str, L"Expected , or )") != L')')
+               CASPAR_THROW_EXCEPTION(user_error()
+                       << msg_info(L"Expected ) " + at_position(cursor, str)));
+
+       ++cursor;
+
+       return function->second(params, var_repo);
+}
+
 double parse_constant(
                std::wstring::const_iterator& cursor, const std::wstring& str)
 {
@@ -193,6 +270,9 @@ boost::any parse_variable(
                ++cursor;
        }
 
+       if (cursor != str.end() && *cursor == L'(')
+               return variable_name;
+
        if (variable_name == L"true")
                return true;
        else if (variable_name == L"false")
@@ -407,19 +487,6 @@ boost::any as_binding(const boost::any& value)
                                L"Couldn't detect type of " + u16(value.type().name())));
 }
 
-template<typename T>
-binding<T> require(const boost::any& value)
-{
-       auto b = as_binding(value);
-
-       if (is<binding<T>>(b))
-               return as<binding<T>>(b);
-       else
-               CASPAR_THROW_EXCEPTION(user_error() << msg_info(
-                               L"Required binding of type " + u16(typeid(T).name())
-                               + L" but got " + u16(value.type().name())));
-}
-
 boost::any negative(const boost::any& to_create_negative_of)
 {
        return -require<double>(to_create_negative_of);
@@ -698,9 +765,17 @@ boost::any parse_expression(
                        tokens.push_back(parse_string_literal(cursor, str));
                        break;
                case L'(':
-                       tokens.push_back(parse_parenthesis(cursor, str, var_repo));
+                       if (!tokens.empty() && is<std::wstring>(tokens.back()))
+                       {
+                               auto function_name = as<std::wstring>(tokens.back());
+                               tokens.pop_back();
+                               tokens.push_back(parse_function(function_name, cursor, str, var_repo));
+                       }
+                       else
+                               tokens.push_back(parse_parenthesis(cursor, str, var_repo));
                        break;
                case L')':
+               case L',':
                        stop = true;
                        break;
                default:
@@ -714,7 +789,7 @@ boost::any parse_expression(
 
        if (tokens.empty())
                CASPAR_THROW_EXCEPTION(user_error()
-                               << msg_info(L"Expected expression"));
+                               << msg_info(L"Expected expression" + at_position(cursor, str)));
 
        int precedence = 1;