2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
\r
4 * This file is part of CasparCG (www.casparcg.com).
\r
6 * CasparCG is free software: you can redistribute it and/or modify
\r
7 * it under the terms of the GNU General Public License as published by
\r
8 * the Free Software Foundation, either version 3 of the License, or
\r
9 * (at your option) any later version.
\r
11 * CasparCG is distributed in the hope that it will be useful,
\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 * GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License
\r
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
\r
19 * Author: Robert Nagy, ronag89@gmail.com
\r
23 // The following code is based on Tweener for actionscript, http://code.google.com/p/tweener/
\r
25 //Disclaimer for Robert Penner's Easing Equations license:
\r
27 //TERMS OF USE - EASING EQUATIONS
\r
29 //Open source under the BSD License.
\r
31 //Copyright © 2001 Robert Penner
\r
32 //All rights reserved.
\r
34 //Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
\r
36 // * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
\r
37 // * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
\r
38 // * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission.
\r
40 //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\r
43 #include "tweener.h"
\r
47 #include <boost/assign/list_of.hpp>
\r
48 #include <boost/regex.hpp>
\r
49 #include <boost/lexical_cast.hpp>
\r
51 #include <unordered_map>
\r
54 #include <functional>
\r
57 namespace caspar { namespace core {
\r
59 typedef std::function<double(double, double, double, double)> tweener_t;
\r
61 static const double PI = std::atan(1.0)*4.0;
\r
62 static const double H_PI = std::atan(1.0)*2.0;
\r
64 double ease_none (double t, double b, double c, double d, const std::vector<double>& params)
\r
69 double ease_in_quad (double t, double b, double c, double d, const std::vector<double>& params)
\r
71 return c*(t/=d)*t + b;
\r
74 double ease_out_quad (double t, double b, double c, double d, const std::vector<double>& params)
\r
76 return -c *(t/=d)*(t-2) + b;
\r
79 double ease_in_out_quad (double t, double b, double c, double d, const std::vector<double>& params)
\r
84 return -c/2 * ((--t)*(t-2) - 1) + b;
\r
87 double ease_out_in_quad (double t, double b, double c, double d, const std::vector<double>& params)
\r
90 return ease_out_quad (t*2, b, c/2, d, params);
\r
92 return ease_in_quad((t*2)-d, b+c/2, c/2, d, params);
\r
95 double ease_in_cubic (double t, double b, double c, double d, const std::vector<double>& params)
\r
97 return c*(t/=d)*t*t + b;
\r
100 double ease_out_cubic (double t, double b, double c, double d, const std::vector<double>& params)
\r
102 return c*((t=t/d-1)*t*t + 1) + b;
\r
105 double ease_in_out_cubic (double t, double b, double c, double d, const std::vector<double>& params)
\r
108 return c/2*t*t*t + b;
\r
110 return c/2*((t-=2)*t*t + 2) + b;
\r
113 double ease_out_in_cubic (double t, double b, double c, double d, const std::vector<double>& params)
\r
115 if (t < d/2) return ease_out_cubic (t*2, b, c/2, d, params);
\r
116 return ease_in_cubic((t*2)-d, b+c/2, c/2, d, params);
\r
119 double ease_in_quart (double t, double b, double c, double d, const std::vector<double>& params)
\r
121 return c*(t/=d)*t*t*t + b;
\r
124 double ease_out_quart (double t, double b, double c, double d, const std::vector<double>& params)
\r
126 return -c * ((t=t/d-1)*t*t*t - 1) + b;
\r
129 double ease_in_out_quart (double t, double b, double c, double d, const std::vector<double>& params)
\r
132 return c/2*t*t*t*t + b;
\r
134 return -c/2 * ((t-=2)*t*t*t - 2) + b;
\r
137 double ease_out_in_quart (double t, double b, double c, double d, const std::vector<double>& params)
\r
140 return ease_out_quart (t*2, b, c/2, d, params);
\r
142 return ease_in_quart((t*2)-d, b+c/2, c/2, d, params);
\r
145 double ease_in_quint (double t, double b, double c, double d, const std::vector<double>& params)
\r
147 return c*(t/=d)*t*t*t*t + b;
\r
150 double ease_out_quint (double t, double b, double c, double d, const std::vector<double>& params)
\r
152 return c*((t=t/d-1)*t*t*t*t + 1) + b;
\r
155 double ease_in_out_quint (double t, double b, double c, double d, const std::vector<double>& params)
\r
158 return c/2*t*t*t*t*t + b;
\r
160 return c/2*((t-=2)*t*t*t*t + 2) + b;
\r
163 double ease_out_in_quint (double t, double b, double c, double d, const std::vector<double>& params)
\r
166 return ease_out_quint (t*2, b, c/2, d, params);
\r
168 return ease_in_quint((t*2)-d, b+c/2, c/2, d, params);
\r
171 double ease_in_sine (double t, double b, double c, double d, const std::vector<double>& params)
\r
173 return -c * std::cos(t/d * (PI/2)) + c + b;
\r
176 double ease_out_sine (double t, double b, double c, double d, const std::vector<double>& params)
\r
178 return c * std::sin(t/d * (PI/2)) + b;
\r
181 double ease_in_out_sine (double t, double b, double c, double d, const std::vector<double>& params)
\r
183 return -c/2 * (std::cos(PI*t/d) - 1) + b;
\r
186 double ease_out_in_sine (double t, double b, double c, double d, const std::vector<double>& params)
\r
189 return ease_out_sine (t*2, b, c/2, d, params);
\r
191 return ease_in_sine((t*2)-d, b+c/2, c/2, d, params);
\r
194 double ease_in_expo (double t, double b, double c, double d, const std::vector<double>& params)
\r
196 return (t==0) ? b : c * std::pow(2, 10 * (t/d - 1)) + b - c * 0.001;
\r
199 double ease_out_expo (double t, double b, double c, double d, const std::vector<double>& params)
\r
201 return (t==d) ? b+c : c * 1.001 * (-std::pow(2, -10 * t/d) + 1) + b;
\r
204 double ease_in_out_expo (double t, double b, double c, double d, const std::vector<double>& params)
\r
211 return c/2 * std::pow(2, 10 * (t - 1)) + b - c * 0.0005;
\r
213 return c/2 * 1.0005 * (-std::pow(2, -10 * --t) + 2) + b;
\r
216 double ease_out_in_expo (double t, double b, double c, double d, const std::vector<double>& params)
\r
219 return ease_out_expo (t*2, b, c/2, d, params);
\r
221 return ease_in_expo((t*2)-d, b+c/2, c/2, d, params);
\r
224 double ease_in_circ (double t, double b, double c, double d, const std::vector<double>& params)
\r
226 return -c * (std::sqrt(1 - (t/=d)*t) - 1) + b;
\r
229 double ease_out_circ (double t, double b, double c, double d, const std::vector<double>& params)
\r
231 return c * std::sqrt(1 - (t=t/d-1)*t) + b;
\r
234 double ease_in_out_circ (double t, double b, double c, double d, const std::vector<double>& params)
\r
237 return -c/2 * (std::sqrt(1 - t*t) - 1) + b;
\r
239 return c/2 * (std::sqrt(1 - (t-=2)*t) + 1) + b;
\r
242 double ease_out_in_circ (double t, double b, double c, double d, const std::vector<double>& params)
\r
244 if (t < d/2) return ease_out_circ(t*2, b, c/2, d, params);
\r
245 return ease_in_circ((t*2)-d, b+c/2, c/2, d, params);
\r
248 double ease_in_elastic (double t, double b, double c, double d, const std::vector<double>& params)
\r
250 if (t==0) return b;
\r
251 if ((t/=d)==1) return b+c;
\r
252 //var p:Number = !Boolean(p_params) || isNaN(p_params.period) ? d*.3 : p_params.period;
\r
254 //var a:Number = !Boolean(p_params) || isNaN(p_params.amplitude) ? 0 : p_params.amplitude;
\r
255 double p = params.size() > 0 ? params[0] : d*0.3;
\r
257 double a = params.size() > 1 ? params[1] : 0.0;
\r
258 if (a == 0.0 || a < std::abs(c))
\r
264 s = p/(2*PI) * std::asin (c/a);
\r
266 return -(a*std::pow(2,10*(t-=1)) * std::sin( (t*d-s)*(2*PI)/p )) + b;
\r
269 double ease_out_elastic (double t, double b, double c, double d, const std::vector<double>& params)
\r
275 //var p:Number = !Boolean(p_params) || isNaN(p_params.period) ? d*.3 : p_params.period;
\r
277 //var a:Number = !Boolean(p_params) || isNaN(p_params.amplitude) ? 0 : p_params.amplitude;
\r
278 double p = params.size() > 0 ? params[0] : d*0.3;
\r
280 double a = params.size() > 1 ? params[1] : 0.0;
\r
281 if (a == 0.0 || a < std::abs(c))
\r
287 s = p/(2*PI) * std::asin (c/a);
\r
289 return (a*std::pow(2,-10*t) * std::sin( (t*d-s)*(2*PI)/p ) + c + b);
\r
292 double ease_in_out_elastic (double t, double b, double c, double d, const std::vector<double>& params)
\r
298 //var p:Number = !Boolean(p_params) || isNaN(p_params.period) ? d*(.3*1.5) : p_params.period;
\r
300 //var a:Number = !Boolean(p_params) || isNaN(p_params.amplitude) ? 0 : p_params.amplitude;
\r
301 double p = params.size() > 0 ? params[0] : d*0.3*1.5;
\r
303 double a = params.size() > 1 ? params[1] : 0.0;
\r
304 if (a == 0.0 || a < std::abs(c))
\r
310 s = p/(2*PI) * std::asin (c/a);
\r
313 return -.5*(a*std::pow(2,10*(t-=1)) * std::sin( (t*d-s)*(2*PI)/p )) + b;
\r
315 return a*std::pow(2,-10*(t-=1)) * std::sin( (t*d-s)*(2*PI)/p )*.5 + c + b;
\r
318 double ease_out_in_elastic (double t, double b, double c, double d, const std::vector<double>& params)
\r
320 if (t < d/2) return ease_out_elastic (t*2, b, c/2, d, params);
\r
321 return ease_in_elastic((t*2)-d, b+c/2, c/2, d, params);
\r
324 double ease_in_back (double t, double b, double c, double d, const std::vector<double>& params)
\r
326 //var s:Number = !Boolean(p_params) || isNaN(p_params.overshoot) ? 1.70158 : p_params.overshoot;
\r
327 double s = params.size() > 0 ? params[0] : 1.70158;
\r
328 return c*(t/=d)*t*((s+1)*t - s) + b;
\r
331 double ease_out_back (double t, double b, double c, double d, const std::vector<double>& params)
\r
333 //var s:Number = !Boolean(p_params) || isNaN(p_params.overshoot) ? 1.70158 : p_params.overshoot;
\r
334 double s = params.size() > 0 ? params[0] : 1.70158;
\r
335 return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
\r
338 double ease_in_out_back (double t, double b, double c, double d, const std::vector<double>& params)
\r
340 //var s:Number = !Boolean(p_params) || isNaN(p_params.overshoot) ? 1.70158 : p_params.overshoot;
\r
341 double s = params.size() > 0 ? params[0] : 1.70158;
\r
342 if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
\r
343 return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
\r
346 double ease_out_int_back (double t, double b, double c, double d, const std::vector<double>& params)
\r
348 if (t < d/2) return ease_out_back (t*2, b, c/2, d, params);
\r
349 return ease_in_back((t*2)-d, b+c/2, c/2, d, params);
\r
352 double ease_out_bounce (double t, double b, double c, double d, const std::vector<double>& params)
\r
354 if ((t/=d) < (1/2.75))
\r
355 return c*(7.5625*t*t) + b;
\r
356 else if (t < (2/2.75))
\r
357 return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
\r
358 else if (t < (2.5/2.75))
\r
359 return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
\r
361 return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
\r
364 double ease_in_bounce (double t, double b, double c, double d, const std::vector<double>& params)
\r
366 return c - ease_out_bounce (d-t, 0, c, d, params) + b;
\r
369 double ease_in_out_bounce (double t, double b, double c, double d, const std::vector<double>& params)
\r
371 if (t < d/2) return ease_in_bounce (t*2, 0, c, d, params) * .5 + b;
\r
372 else return ease_out_bounce (t*2-d, 0, c, d, params) * .5 + c*.5 + b;
\r
376 double ease_out_in_bounce (double t, double b, double c, double d, const std::vector<double>& params)
\r
378 if (t < d/2) return ease_out_bounce (t*2, b, c/2, d, params);
\r
379 return ease_in_bounce((t*2)-d, b+c/2, c/2, d, params);
\r
382 tweener_t get_tweener(std::wstring name)
\r
384 std::transform(name.begin(), name.end(), name.begin(), std::tolower);
\r
386 if(name == L"linear")
\r
387 return [](double t, double b, double c, double d){return ease_none(t, b, c, d, std::vector<double>());};
\r
389 std::vector<double> params;
\r
391 static const boost::wregex expr(L"(?<NAME>\\w*)(:(?<V0>\\d+\\.?\\d?))?(:(?<V1>\\d+\\.?\\d?))?"); // boost::regex has no repeated captures?
\r
392 boost::wsmatch what;
\r
393 if(boost::regex_match(name, what, expr))
\r
395 name = what["NAME"].str();
\r
396 if(what["V0"].matched)
\r
397 params.push_back(boost::lexical_cast<double>(what["V0"].str()));
\r
398 if(what["V1"].matched)
\r
399 params.push_back(boost::lexical_cast<double>(what["V1"].str()));
\r
402 typedef std::function<double(double, double, double, double, const std::vector<double>&)> tween_t;
\r
403 static const std::unordered_map<std::wstring, tween_t> tweens = boost::assign::map_list_of
\r
405 (L"linear", ease_none )
\r
406 (L"easenone", ease_none )
\r
407 (L"easeinquad", ease_in_quad )
\r
408 (L"easeoutquad", ease_out_quad )
\r
409 (L"easeinoutquad", ease_in_out_quad )
\r
410 (L"easeoutinquad", ease_out_in_quad )
\r
411 (L"easeincubic", ease_in_cubic )
\r
412 (L"easeoutcubic", ease_out_cubic )
\r
413 (L"easeinoutcubic", ease_in_out_cubic )
\r
414 (L"easeoutincubic", ease_out_in_cubic )
\r
415 (L"easeinquart", ease_in_quart )
\r
416 (L"easeoutquart", ease_out_quart )
\r
417 (L"easeinoutquart", ease_in_out_quart )
\r
418 (L"easeoutinquart", ease_out_in_quart )
\r
419 (L"easeinquint", ease_in_quint )
\r
420 (L"easeoutquint", ease_out_quint )
\r
421 (L"easeinoutquint", ease_in_out_quint )
\r
422 (L"easeoutinquint", ease_out_in_quint )
\r
423 (L"easeinsine", ease_in_sine )
\r
424 (L"easeoutsine", ease_out_sine )
\r
425 (L"easeinoutsine", ease_in_out_sine )
\r
426 (L"easeoutinsine", ease_out_in_sine )
\r
427 (L"easeinexpo", ease_in_expo )
\r
428 (L"easeoutexpo", ease_out_expo )
\r
429 (L"easeinoutexpo", ease_in_out_expo )
\r
430 (L"easeoutinexpo", ease_out_in_expo )
\r
431 (L"easeincirc", ease_in_circ )
\r
432 (L"easeoutcirc", ease_out_circ )
\r
433 (L"easeinoutcirc", ease_in_out_circ )
\r
434 (L"easeoutincirc", ease_out_in_circ )
\r
435 (L"easeinelastic", ease_in_elastic )
\r
436 (L"easeoutelastic", ease_out_elastic )
\r
437 (L"easeinoutelastic", ease_in_out_elastic)
\r
438 (L"easeoutinelastic", ease_out_in_elastic)
\r
439 (L"easeinback", ease_in_back )
\r
440 (L"easeoutback", ease_out_back )
\r
441 (L"easeinoutback", ease_in_out_back )
\r
442 (L"easeoutintback", ease_out_int_back )
\r
443 (L"easeoutbounce", ease_out_bounce )
\r
444 (L"easeinbounce", ease_in_bounce )
\r
445 (L"easeinoutbounce", ease_in_out_bounce )
\r
446 (L"easeoutinbounce", ease_out_in_bounce );
\r
448 auto it = tweens.find(name);
\r
449 if(it == tweens.end())
\r
450 CASPAR_THROW_EXCEPTION(invalid_argument() << msg_info("Could not find tween.") << arg_value_info(name));
\r
452 auto tween = it->second;
\r
453 return [=](double t, double b, double c, double d)
\r
455 return tween(t, b, c, d, params);
\r
459 tweener::tweener(const std::wstring& name)
\r
460 : func_(get_tweener(name))
\r
464 tweener::tweener(const wchar_t* name)
\r
465 : func_(get_tweener(name))
\r
469 double tweener::operator()(double t, double b , double c, double d) const
\r
471 return func_(t, b, c, d);
\r