]> git.sesse.net Git - casparcg/blob - common/tweener.cpp
f5420063ff2e9bd317dab6c297423f9628eb899a
[casparcg] / common / tweener.cpp
1 /*\r
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
3 *\r
4 * This file is part of CasparCG (www.casparcg.com).\r
5 *\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
10 *\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
15 *\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
18 *\r
19 * Author: Robert Nagy, ronag89@gmail.com\r
20 */\r
21 \r
22 \r
23 // The following code is based on Tweener for actionscript, http://code.google.com/p/tweener/\r
24 //\r
25 //Disclaimer for Robert Penner's Easing Equations license:\r
26 //\r
27 //TERMS OF USE - EASING EQUATIONS\r
28 //\r
29 //Open source under the BSD License.\r
30 //\r
31 //Copyright © 2001 Robert Penner\r
32 //All rights reserved.\r
33 //\r
34 //Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\r
35 //\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
39 //\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
41 #include "stdafx.h"\r
42 \r
43 #include "tweener.h"\r
44 \r
45 #include "except.h"\r
46 \r
47 #include <boost/assign/list_of.hpp>\r
48 #include <boost/regex.hpp>\r
49 #include <boost/lexical_cast.hpp>\r
50 \r
51 #include <unordered_map>\r
52 #include <string>\r
53 #include <locale>\r
54 #include <functional>\r
55 #include <vector>\r
56 \r
57 namespace caspar { namespace core {\r
58 \r
59 typedef std::function<double(double, double, double, double)> tweener_t;\r
60                         \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
63 \r
64 double ease_none (double t, double b, double c, double d, const std::vector<double>& params) \r
65 {\r
66         return c*t/d + b;\r
67 }\r
68 \r
69 double ease_in_quad (double t, double b, double c, double d, const std::vector<double>& params) \r
70 {\r
71         return c*(t/=d)*t + b;\r
72 }\r
73         \r
74 double ease_out_quad (double t, double b, double c, double d, const std::vector<double>& params) \r
75 {\r
76         return -c *(t/=d)*(t-2) + b;\r
77 }       \r
78 \r
79 double ease_in_out_quad (double t, double b, double c, double d, const std::vector<double>& params)\r
80 {\r
81         if ((t/=d/2) < 1) \r
82                 return c/2*t*t + b;\r
83 \r
84         return -c/2 * ((--t)*(t-2) - 1) + b;\r
85 }       \r
86 \r
87 double ease_out_in_quad (double t, double b, double c, double d, const std::vector<double>& params)\r
88 {\r
89         if (t < d/2) \r
90                 return ease_out_quad (t*2, b, c/2, d, params);\r
91 \r
92         return ease_in_quad((t*2)-d, b+c/2, c/2, d, params);\r
93 }\r
94         \r
95 double ease_in_cubic (double t, double b, double c, double d, const std::vector<double>& params) \r
96 {\r
97         return c*(t/=d)*t*t + b;\r
98 }       \r
99 \r
100 double ease_out_cubic (double t, double b, double c, double d, const std::vector<double>& params) \r
101 {\r
102         return c*((t=t/d-1)*t*t + 1) + b;\r
103 }\r
104         \r
105 double ease_in_out_cubic (double t, double b, double c, double d, const std::vector<double>& params) \r
106 {\r
107         if ((t/=d/2) < 1) \r
108                 return c/2*t*t*t + b;\r
109 \r
110         return c/2*((t-=2)*t*t + 2) + b;\r
111 }\r
112         \r
113 double ease_out_in_cubic (double t, double b, double c, double d, const std::vector<double>& params) \r
114 {\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
117 }\r
118         \r
119 double ease_in_quart (double t, double b, double c, double d, const std::vector<double>& params) \r
120 {\r
121         return c*(t/=d)*t*t*t + b;\r
122 }\r
123         \r
124 double ease_out_quart (double t, double b, double c, double d, const std::vector<double>& params) \r
125 {\r
126         return -c * ((t=t/d-1)*t*t*t - 1) + b;\r
127 }       \r
128 \r
129 double ease_in_out_quart (double t, double b, double c, double d, const std::vector<double>& params) \r
130 {\r
131         if ((t/=d/2) < 1)\r
132                 return c/2*t*t*t*t + b;\r
133 \r
134         return -c/2 * ((t-=2)*t*t*t - 2) + b;\r
135 }       \r
136 \r
137 double ease_out_in_quart (double t, double b, double c, double d, const std::vector<double>& params) \r
138 {\r
139         if (t < d/2)\r
140                 return ease_out_quart (t*2, b, c/2, d, params);\r
141 \r
142         return ease_in_quart((t*2)-d, b+c/2, c/2, d, params);\r
143 }       \r
144 \r
145 double ease_in_quint (double t, double b, double c, double d, const std::vector<double>& params) \r
146 {\r
147         return c*(t/=d)*t*t*t*t + b;\r
148 }\r
149         \r
150 double ease_out_quint (double t, double b, double c, double d, const std::vector<double>& params) \r
151 {\r
152         return c*((t=t/d-1)*t*t*t*t + 1) + b;\r
153 }\r
154         \r
155 double ease_in_out_quint (double t, double b, double c, double d, const std::vector<double>& params) \r
156 {\r
157         if ((t/=d/2) < 1) \r
158                 return c/2*t*t*t*t*t + b;\r
159 \r
160         return c/2*((t-=2)*t*t*t*t + 2) + b;\r
161 }\r
162         \r
163 double ease_out_in_quint (double t, double b, double c, double d, const std::vector<double>& params) \r
164 {\r
165         if (t < d/2) \r
166                 return ease_out_quint (t*2, b, c/2, d, params);\r
167 \r
168         return ease_in_quint((t*2)-d, b+c/2, c/2, d, params);\r
169 }       \r
170 \r
171 double ease_in_sine (double t, double b, double c, double d, const std::vector<double>& params) \r
172 {\r
173         return -c * std::cos(t/d * (PI/2)) + c + b;\r
174 }       \r
175 \r
176 double ease_out_sine (double t, double b, double c, double d, const std::vector<double>& params) \r
177 {\r
178         return c * std::sin(t/d * (PI/2)) + b;\r
179 }       \r
180 \r
181 double ease_in_out_sine (double t, double b, double c, double d, const std::vector<double>& params) \r
182 {\r
183         return -c/2 * (std::cos(PI*t/d) - 1) + b;\r
184 }       \r
185 \r
186 double ease_out_in_sine (double t, double b, double c, double d, const std::vector<double>& params)\r
187 {\r
188         if (t < d/2) \r
189                 return ease_out_sine (t*2, b, c/2, d, params);\r
190         \r
191         return ease_in_sine((t*2)-d, b+c/2, c/2, d, params);\r
192 }       \r
193 \r
194 double ease_in_expo (double t, double b, double c, double d, const std::vector<double>& params) \r
195 {\r
196         return (t==0) ? b : c * std::pow(2, 10 * (t/d - 1)) + b - c * 0.001;\r
197 }       \r
198 \r
199 double ease_out_expo (double t, double b, double c, double d, const std::vector<double>& params) \r
200 {\r
201         return (t==d) ? b+c : c * 1.001 * (-std::pow(2, -10 * t/d) + 1) + b;\r
202 }\r
203         \r
204 double ease_in_out_expo (double t, double b, double c, double d, const std::vector<double>& params) \r
205 {\r
206         if (t==0) \r
207                 return b;\r
208         if (t==d) \r
209                 return b+c;\r
210         if ((t/=d/2) < 1) \r
211                 return c/2 * std::pow(2, 10 * (t - 1)) + b - c * 0.0005;\r
212 \r
213         return c/2 * 1.0005 * (-std::pow(2, -10 * --t) + 2) + b;\r
214 }\r
215         \r
216 double ease_out_in_expo (double t, double b, double c, double d, const std::vector<double>& params) \r
217 {\r
218         if (t < d/2) \r
219                 return ease_out_expo (t*2, b, c/2, d, params);\r
220 \r
221         return ease_in_expo((t*2)-d, b+c/2, c/2, d, params);\r
222 }\r
223         \r
224 double ease_in_circ (double t, double b, double c, double d, const std::vector<double>& params) \r
225 {\r
226         return -c * (std::sqrt(1 - (t/=d)*t) - 1) + b;\r
227 }\r
228         \r
229 double ease_out_circ (double t, double b, double c, double d, const std::vector<double>& params) \r
230 {\r
231         return c * std::sqrt(1 - (t=t/d-1)*t) + b;\r
232 }\r
233         \r
234 double ease_in_out_circ (double t, double b, double c, double d, const std::vector<double>& params) \r
235 {\r
236         if ((t/=d/2) < 1) \r
237                 return -c/2 * (std::sqrt(1 - t*t) - 1) + b;\r
238 \r
239         return c/2 * (std::sqrt(1 - (t-=2)*t) + 1) + b;\r
240 }\r
241         \r
242 double ease_out_in_circ (double t, double b, double c, double d, const std::vector<double>& params) \r
243 {\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
246 }\r
247         \r
248 double ease_in_elastic (double t, double b, double c, double d, const std::vector<double>& params)\r
249 {\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
253         //var s:Number;\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
256         double s;\r
257         double a = params.size() > 1 ? params[1] : 0.0;\r
258         if (a == 0.0 || a < std::abs(c)) \r
259         {\r
260                 a = c;\r
261                 s = p/4;\r
262         } \r
263         else \r
264                 s = p/(2*PI) * std::asin (c/a);\r
265         \r
266         return -(a*std::pow(2,10*(t-=1)) * std::sin( (t*d-s)*(2*PI)/p )) + b;\r
267 }\r
268         \r
269 double ease_out_elastic (double t, double b, double c, double d, const std::vector<double>& params) \r
270 {\r
271         if (t==0) \r
272                 return b;\r
273         if ((t/=d)==1) \r
274                 return b+c;\r
275         //var p:Number = !Boolean(p_params) || isNaN(p_params.period) ? d*.3 : p_params.period;\r
276         //var s:Number;\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
279         double s;\r
280         double a = params.size() > 1 ? params[1] : 0.0;\r
281         if (a == 0.0 || a < std::abs(c))\r
282         {\r
283                 a = c;\r
284                 s = p/4;\r
285         } \r
286         else \r
287                 s = p/(2*PI) * std::asin (c/a);\r
288         \r
289         return (a*std::pow(2,-10*t) * std::sin( (t*d-s)*(2*PI)/p ) + c + b);\r
290 }       \r
291 \r
292 double ease_in_out_elastic (double t, double b, double c, double d, const std::vector<double>& params) \r
293 {\r
294         if (t==0)\r
295                 return b;\r
296         if ((t/=d/2)==2) \r
297                 return b+c;\r
298         //var p:Number = !Boolean(p_params) || isNaN(p_params.period) ? d*(.3*1.5) : p_params.period;\r
299         //var s:Number;\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
302         double s;\r
303         double a = params.size() > 1 ? params[1] : 0.0;\r
304         if (a == 0.0 || a < std::abs(c)) \r
305         {\r
306                 a = c;\r
307                 s = p/4;\r
308         }\r
309         else\r
310                 s = p/(2*PI) * std::asin (c/a);\r
311         \r
312         if (t < 1) \r
313                 return -.5*(a*std::pow(2,10*(t-=1)) * std::sin( (t*d-s)*(2*PI)/p )) + b;\r
314 \r
315         return a*std::pow(2,-10*(t-=1)) * std::sin( (t*d-s)*(2*PI)/p )*.5 + c + b;\r
316 }\r
317         \r
318 double ease_out_in_elastic (double t, double b, double c, double d, const std::vector<double>& params) \r
319 {\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
322 }\r
323         \r
324 double ease_in_back (double t, double b, double c, double d, const std::vector<double>& params) \r
325 {\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
329 }\r
330         \r
331 double ease_out_back (double t, double b, double c, double d, const std::vector<double>& params)\r
332 {\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
336 }\r
337         \r
338 double ease_in_out_back (double t, double b, double c, double d, const std::vector<double>& params)\r
339 {\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
344 }\r
345         \r
346 double ease_out_int_back (double t, double b, double c, double d, const std::vector<double>& params)\r
347 {\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
350 }\r
351         \r
352 double ease_out_bounce (double t, double b, double c, double d, const std::vector<double>& params) \r
353 {\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
360         else \r
361                 return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;    \r
362 }\r
363         \r
364 double ease_in_bounce (double t, double b, double c, double d, const std::vector<double>& params)\r
365 {\r
366         return c - ease_out_bounce (d-t, 0, c, d, params) + b;\r
367 }\r
368 \r
369 double ease_in_out_bounce (double t, double b, double c, double d, const std::vector<double>& params) \r
370 {\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
373 }\r
374         \r
375 \r
376 double ease_out_in_bounce (double t, double b, double c, double d, const std::vector<double>& params) \r
377 {\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
380 }\r
381 \r
382 tweener_t get_tweener(std::wstring name)\r
383 {\r
384         std::transform(name.begin(), name.end(), name.begin(), std::tolower);\r
385 \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
388         \r
389         std::vector<double> params;\r
390         \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
394         {\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
400         }\r
401                 \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
404                 (L"",                                   ease_none                  )    \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
447 \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
451         \r
452         auto tween = it->second;\r
453         return [=](double t, double b, double c, double d)\r
454         {\r
455                 return tween(t, b, c, d, params);\r
456         };\r
457 };\r
458 \r
459 tweener::tweener(const std::wstring& name)\r
460         : func_(get_tweener(name))\r
461 {\r
462 }\r
463 \r
464 tweener::tweener(const wchar_t* name)\r
465         : func_(get_tweener(name))\r
466 {\r
467 }\r
468 \r
469 double tweener::operator()(double t, double b , double c, double d) const\r
470 {\r
471         return func_(t, b, c, d);\r
472 }\r
473 \r
474 }}