2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
4 * This file is part of CasparCG (www.casparcg.com).
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
19 * Author: Robert Nagy, ronag89@gmail.com
23 // The following code is based on Tweener for actionscript, http://code.google.com/p/tweener/
25 //Disclaimer for Robert Penner's Easing Equations license:
27 //TERMS OF USE - EASING EQUATIONS
29 //Open source under the BSD License.
31 //Copyright � 2001 Robert Penner
32 //All rights reserved.
34 //Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
36 // * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
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.
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.
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.
48 #include <boost/regex.hpp>
50 #include <unordered_map>
59 typedef std::function<double(double, double, double, double)> tweener_t;
61 static const double PI = std::atan(1.0)*4.0;
62 static const double H_PI = std::atan(1.0)*2.0;
64 double ease_none (double t, double b, double c, double d, const std::vector<double>& params)
69 double ease_in_quad (double t, double b, double c, double d, const std::vector<double>& params)
71 return c*(t/=d)*t + b;
74 double ease_out_quad (double t, double b, double c, double d, const std::vector<double>& params)
76 return -c *(t/=d)*(t-2) + b;
79 double ease_in_out_quad (double t, double b, double c, double d, const std::vector<double>& params)
84 return -c/2 * ((--t)*(t-2) - 1) + b;
87 double ease_out_in_quad (double t, double b, double c, double d, const std::vector<double>& params)
90 return ease_out_quad (t*2, b, c/2, d, params);
92 return ease_in_quad((t*2)-d, b+c/2, c/2, d, params);
95 double ease_in_cubic (double t, double b, double c, double d, const std::vector<double>& params)
97 return c*(t/=d)*t*t + b;
100 double ease_out_cubic (double t, double b, double c, double d, const std::vector<double>& params)
102 return c*((t=t/d-1)*t*t + 1) + b;
105 double ease_in_out_cubic (double t, double b, double c, double d, const std::vector<double>& params)
108 return c/2*t*t*t + b;
110 return c/2*((t-=2)*t*t + 2) + b;
113 double ease_out_in_cubic (double t, double b, double c, double d, const std::vector<double>& params)
115 if (t < d/2) return ease_out_cubic (t*2, b, c/2, d, params);
116 return ease_in_cubic((t*2)-d, b+c/2, c/2, d, params);
119 double ease_in_quart (double t, double b, double c, double d, const std::vector<double>& params)
121 return c*(t/=d)*t*t*t + b;
124 double ease_out_quart (double t, double b, double c, double d, const std::vector<double>& params)
126 return -c * ((t=t/d-1)*t*t*t - 1) + b;
129 double ease_in_out_quart (double t, double b, double c, double d, const std::vector<double>& params)
132 return c/2*t*t*t*t + b;
134 return -c/2 * ((t-=2)*t*t*t - 2) + b;
137 double ease_out_in_quart (double t, double b, double c, double d, const std::vector<double>& params)
140 return ease_out_quart (t*2, b, c/2, d, params);
142 return ease_in_quart((t*2)-d, b+c/2, c/2, d, params);
145 double ease_in_quint (double t, double b, double c, double d, const std::vector<double>& params)
147 return c*(t/=d)*t*t*t*t + b;
150 double ease_out_quint (double t, double b, double c, double d, const std::vector<double>& params)
152 return c*((t=t/d-1)*t*t*t*t + 1) + b;
155 double ease_in_out_quint (double t, double b, double c, double d, const std::vector<double>& params)
158 return c/2*t*t*t*t*t + b;
160 return c/2*((t-=2)*t*t*t*t + 2) + b;
163 double ease_out_in_quint (double t, double b, double c, double d, const std::vector<double>& params)
166 return ease_out_quint (t*2, b, c/2, d, params);
168 return ease_in_quint((t*2)-d, b+c/2, c/2, d, params);
171 double ease_in_sine (double t, double b, double c, double d, const std::vector<double>& params)
173 return -c * std::cos(t/d * (PI/2)) + c + b;
176 double ease_out_sine (double t, double b, double c, double d, const std::vector<double>& params)
178 return c * std::sin(t/d * (PI/2)) + b;
181 double ease_in_out_sine (double t, double b, double c, double d, const std::vector<double>& params)
183 return -c/2 * (std::cos(PI*t/d) - 1) + b;
186 double ease_out_in_sine (double t, double b, double c, double d, const std::vector<double>& params)
189 return ease_out_sine (t*2, b, c/2, d, params);
191 return ease_in_sine((t*2)-d, b+c/2, c/2, d, params);
194 double ease_in_expo (double t, double b, double c, double d, const std::vector<double>& params)
196 return (t==0) ? b : c * std::pow(2, 10 * (t/d - 1)) + b - c * 0.001;
199 double ease_out_expo (double t, double b, double c, double d, const std::vector<double>& params)
201 return (t==d) ? b+c : c * 1.001 * (-std::pow(2, -10 * t/d) + 1) + b;
204 double ease_in_out_expo (double t, double b, double c, double d, const std::vector<double>& params)
211 return c/2 * std::pow(2, 10 * (t - 1)) + b - c * 0.0005;
213 return c/2 * 1.0005 * (-std::pow(2, -10 * --t) + 2) + b;
216 double ease_out_in_expo (double t, double b, double c, double d, const std::vector<double>& params)
219 return ease_out_expo (t*2, b, c/2, d, params);
221 return ease_in_expo((t*2)-d, b+c/2, c/2, d, params);
224 double ease_in_circ (double t, double b, double c, double d, const std::vector<double>& params)
226 return -c * (std::sqrt(1 - (t/=d)*t) - 1) + b;
229 double ease_out_circ (double t, double b, double c, double d, const std::vector<double>& params)
231 return c * std::sqrt(1 - (t=t/d-1)*t) + b;
234 double ease_in_out_circ (double t, double b, double c, double d, const std::vector<double>& params)
237 return -c/2 * (std::sqrt(1 - t*t) - 1) + b;
239 return c/2 * (std::sqrt(1 - (t-=2)*t) + 1) + b;
242 double ease_out_in_circ (double t, double b, double c, double d, const std::vector<double>& params)
244 if (t < d/2) return ease_out_circ(t*2, b, c/2, d, params);
245 return ease_in_circ((t*2)-d, b+c/2, c/2, d, params);
248 double ease_in_elastic (double t, double b, double c, double d, const std::vector<double>& params)
251 if ((t/=d)==1) return b+c;
252 //var p:Number = !Boolean(p_params) || isNaN(p_params.period) ? d*.3 : p_params.period;
254 //var a:Number = !Boolean(p_params) || isNaN(p_params.amplitude) ? 0 : p_params.amplitude;
255 double p = params.size() > 0 ? params[0] : d*0.3;
257 double a = params.size() > 1 ? params[1] : 0.0;
258 if (a == 0.0 || a < std::abs(c))
264 s = p/(2*PI) * std::asin (c/a);
266 return -(a*std::pow(2,10*(t-=1)) * std::sin( (t*d-s)*(2*PI)/p )) + b;
269 double ease_out_elastic (double t, double b, double c, double d, const std::vector<double>& params)
275 //var p:Number = !Boolean(p_params) || isNaN(p_params.period) ? d*.3 : p_params.period;
277 //var a:Number = !Boolean(p_params) || isNaN(p_params.amplitude) ? 0 : p_params.amplitude;
278 double p = params.size() > 0 ? params[0] : d*0.3;
280 double a = params.size() > 1 ? params[1] : 0.0;
281 if (a == 0.0 || a < std::abs(c))
287 s = p/(2*PI) * std::asin (c/a);
289 return (a*std::pow(2,-10*t) * std::sin( (t*d-s)*(2*PI)/p ) + c + b);
292 double ease_in_out_elastic (double t, double b, double c, double d, const std::vector<double>& params)
298 //var p:Number = !Boolean(p_params) || isNaN(p_params.period) ? d*(.3*1.5) : p_params.period;
300 //var a:Number = !Boolean(p_params) || isNaN(p_params.amplitude) ? 0 : p_params.amplitude;
301 double p = params.size() > 0 ? params[0] : d*0.3*1.5;
303 double a = params.size() > 1 ? params[1] : 0.0;
304 if (a == 0.0 || a < std::abs(c))
310 s = p/(2*PI) * std::asin (c/a);
313 return -.5*(a*std::pow(2,10*(t-=1)) * std::sin( (t*d-s)*(2*PI)/p )) + b;
315 return a*std::pow(2,-10*(t-=1)) * std::sin( (t*d-s)*(2*PI)/p )*.5 + c + b;
318 double ease_out_in_elastic (double t, double b, double c, double d, const std::vector<double>& params)
320 if (t < d/2) return ease_out_elastic (t*2, b, c/2, d, params);
321 return ease_in_elastic((t*2)-d, b+c/2, c/2, d, params);
324 double ease_in_back (double t, double b, double c, double d, const std::vector<double>& params)
326 //var s:Number = !Boolean(p_params) || isNaN(p_params.overshoot) ? 1.70158 : p_params.overshoot;
327 double s = params.size() > 0 ? params[0] : 1.70158;
328 return c*(t/=d)*t*((s+1)*t - s) + b;
331 double ease_out_back (double t, double b, double c, double d, const std::vector<double>& params)
333 //var s:Number = !Boolean(p_params) || isNaN(p_params.overshoot) ? 1.70158 : p_params.overshoot;
334 double s = params.size() > 0 ? params[0] : 1.70158;
335 return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
338 double ease_in_out_back (double t, double b, double c, double d, const std::vector<double>& params)
340 //var s:Number = !Boolean(p_params) || isNaN(p_params.overshoot) ? 1.70158 : p_params.overshoot;
341 double s = params.size() > 0 ? params[0] : 1.70158;
342 if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
343 return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
346 double ease_out_int_back (double t, double b, double c, double d, const std::vector<double>& params)
348 if (t < d/2) return ease_out_back (t*2, b, c/2, d, params);
349 return ease_in_back((t*2)-d, b+c/2, c/2, d, params);
352 double ease_out_bounce (double t, double b, double c, double d, const std::vector<double>& params)
354 if ((t/=d) < (1/2.75))
355 return c*(7.5625*t*t) + b;
356 else if (t < (2/2.75))
357 return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
358 else if (t < (2.5/2.75))
359 return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
361 return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
364 double ease_in_bounce (double t, double b, double c, double d, const std::vector<double>& params)
366 return c - ease_out_bounce (d-t, 0, c, d, params) + b;
369 double ease_in_out_bounce (double t, double b, double c, double d, const std::vector<double>& params)
371 if (t < d/2) return ease_in_bounce (t*2, 0, c, d, params) * .5 + b;
372 else return ease_out_bounce (t*2-d, 0, c, d, params) * .5 + c*.5 + b;
376 double ease_out_in_bounce (double t, double b, double c, double d, const std::vector<double>& params)
378 if (t < d/2) return ease_out_bounce (t*2, b, c/2, d, params);
379 return ease_in_bounce((t*2)-d, b+c/2, c/2, d, params);
382 typedef std::function<double(double, double, double, double, const std::vector<double>&)> tween_t;
384 const std::unordered_map<std::wstring, tween_t>& get_tweens()
386 static const std::unordered_map<std::wstring, tween_t> tweens = {
388 {L"linear", ease_none },
389 {L"easenone", ease_none },
390 {L"easeinquad", ease_in_quad },
391 {L"easeoutquad", ease_out_quad },
392 {L"easeinoutquad", ease_in_out_quad },
393 {L"easeoutinquad", ease_out_in_quad },
394 {L"easeincubic", ease_in_cubic },
395 {L"easeoutcubic", ease_out_cubic },
396 {L"easeinoutcubic", ease_in_out_cubic },
397 {L"easeoutincubic", ease_out_in_cubic },
398 {L"easeinquart", ease_in_quart },
399 {L"easeoutquart", ease_out_quart },
400 {L"easeinoutquart", ease_in_out_quart },
401 {L"easeoutinquart", ease_out_in_quart },
402 {L"easeinquint", ease_in_quint },
403 {L"easeoutquint", ease_out_quint },
404 {L"easeinoutquint", ease_in_out_quint },
405 {L"easeoutinquint", ease_out_in_quint },
406 {L"easeinsine", ease_in_sine },
407 {L"easeoutsine", ease_out_sine },
408 {L"easeinoutsine", ease_in_out_sine },
409 {L"easeoutinsine", ease_out_in_sine },
410 {L"easeinexpo", ease_in_expo },
411 {L"easeoutexpo", ease_out_expo },
412 {L"easeinoutexpo", ease_in_out_expo },
413 {L"easeoutinexpo", ease_out_in_expo },
414 {L"easeincirc", ease_in_circ },
415 {L"easeoutcirc", ease_out_circ },
416 {L"easeinoutcirc", ease_in_out_circ },
417 {L"easeoutincirc", ease_out_in_circ },
418 {L"easeinelastic", ease_in_elastic },
419 {L"easeoutelastic", ease_out_elastic },
420 {L"easeinoutelastic", ease_in_out_elastic },
421 {L"easeoutinelastic", ease_out_in_elastic },
422 {L"easeinback", ease_in_back },
423 {L"easeoutback", ease_out_back },
424 {L"easeinoutback", ease_in_out_back },
425 {L"easeoutintback", ease_out_int_back },
426 {L"easeoutbounce", ease_out_bounce },
427 {L"easeinbounce", ease_in_bounce },
428 {L"easeinoutbounce", ease_in_out_bounce },
429 {L"easeoutinbounce", ease_out_in_bounce }
435 tweener_t get_tweener(std::wstring name)
437 std::transform(name.begin(), name.end(), name.begin(), std::towlower);
439 if(name == L"linear")
440 return [](double t, double b, double c, double d){return ease_none(t, b, c, d, std::vector<double>());};
442 std::vector<double> params;
444 static const boost::wregex expr(LR"((?<NAME>\w*)(:(?<V0>\d+\.?\d?))?(:(?<V1>\d+\.?\d?))?)"); // boost::regex has no repeated captures?
446 if(boost::regex_match(name, what, expr))
448 name = what["NAME"].str();
449 if(what["V0"].matched)
450 params.push_back(std::stod(what["V0"].str()));
451 if(what["V1"].matched)
452 params.push_back(std::stod(what["V1"].str()));
455 auto it = get_tweens().find(name);
456 if(it == get_tweens().end())
457 CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Could not find tween " + name));
459 auto tween = it->second;
460 return [=](double t, double b, double c, double d)
462 return tween(t, b, c, d, params);
466 tweener::tweener(const std::wstring& name)
467 : func_(get_tweener(name))
472 double tweener::operator()(double t, double b , double c, double d) const
474 return func_(t, b, c, d);
477 bool tweener::operator==(const tweener& other) const
479 return name_ == other.name_;
482 bool tweener::operator!=(const tweener& other) const
484 return !(*this == other);
487 const std::vector<std::wstring>& tweener::names()
489 /*static const auto names = cpplinq::from(get_tweens())
493 static const auto result = []
495 std::vector<std::wstring> tweens;
497 for (auto& tween : get_tweens())
498 tweens.push_back(tween.first);
502 //static const std::vector<std::wstring> result;