]> git.sesse.net Git - casparcg/blob - common/utility/tweener.cpp
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches...
[casparcg] / common / utility / 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 <boost/assign/list_of.hpp>\r
46 #include <boost/regex.hpp>\r
47 #include <boost/lexical_cast.hpp>\r
48 \r
49 #include <unordered_map>\r
50 #include <string>\r
51 #include <locale>\r
52 #include <functional>\r
53 #include <vector>\r
54 \r
55 namespace caspar {\r
56 \r
57 typedef std::function<double(double, double, double, double)> tweener_t;\r
58                         \r
59 static const double PI = std::atan(1.0)*4.0;\r
60 static const double H_PI = std::atan(1.0)*2.0;\r
61 \r
62 double ease_none (double t, double b, double c, double d, const std::vector<double>& params) \r
63 {\r
64         return c*t/d + b;\r
65 }\r
66 \r
67 double ease_in_quad (double t, double b, double c, double d, const std::vector<double>& params) \r
68 {\r
69         return c*(t/=d)*t + b;\r
70 }\r
71         \r
72 double ease_out_quad (double t, double b, double c, double d, const std::vector<double>& params) \r
73 {\r
74         return -c *(t/=d)*(t-2) + b;\r
75 }       \r
76 \r
77 double ease_in_out_quad (double t, double b, double c, double d, const std::vector<double>& params)\r
78 {\r
79         if ((t/=d/2) < 1) \r
80                 return c/2*t*t + b;\r
81 \r
82         return -c/2 * ((--t)*(t-2) - 1) + b;\r
83 }       \r
84 \r
85 double ease_out_in_quad (double t, double b, double c, double d, const std::vector<double>& params)\r
86 {\r
87         if (t < d/2) \r
88                 return ease_out_quad (t*2, b, c/2, d, params);\r
89 \r
90         return ease_in_quad((t*2)-d, b+c/2, c/2, d, params);\r
91 }\r
92         \r
93 double ease_in_cubic (double t, double b, double c, double d, const std::vector<double>& params) \r
94 {\r
95         return c*(t/=d)*t*t + b;\r
96 }       \r
97 \r
98 double ease_out_cubic (double t, double b, double c, double d, const std::vector<double>& params) \r
99 {\r
100         return c*((t=t/d-1)*t*t + 1) + b;\r
101 }\r
102         \r
103 double ease_in_out_cubic (double t, double b, double c, double d, const std::vector<double>& params) \r
104 {\r
105         if ((t/=d/2) < 1) \r
106                 return c/2*t*t*t + b;\r
107 \r
108         return c/2*((t-=2)*t*t + 2) + b;\r
109 }\r
110         \r
111 double ease_out_in_cubic (double t, double b, double c, double d, const std::vector<double>& params) \r
112 {\r
113         if (t < d/2) return ease_out_cubic (t*2, b, c/2, d, params);\r
114         return ease_in_cubic((t*2)-d, b+c/2, c/2, d, params);\r
115 }\r
116         \r
117 double ease_in_quart (double t, double b, double c, double d, const std::vector<double>& params) \r
118 {\r
119         return c*(t/=d)*t*t*t + b;\r
120 }\r
121         \r
122 double ease_out_quart (double t, double b, double c, double d, const std::vector<double>& params) \r
123 {\r
124         return -c * ((t=t/d-1)*t*t*t - 1) + b;\r
125 }       \r
126 \r
127 double ease_in_out_quart (double t, double b, double c, double d, const std::vector<double>& params) \r
128 {\r
129         if ((t/=d/2) < 1)\r
130                 return c/2*t*t*t*t + b;\r
131 \r
132         return -c/2 * ((t-=2)*t*t*t - 2) + b;\r
133 }       \r
134 \r
135 double ease_out_in_quart (double t, double b, double c, double d, const std::vector<double>& params) \r
136 {\r
137         if (t < d/2)\r
138                 return ease_out_quart (t*2, b, c/2, d, params);\r
139 \r
140         return ease_in_quart((t*2)-d, b+c/2, c/2, d, params);\r
141 }       \r
142 \r
143 double ease_in_quint (double t, double b, double c, double d, const std::vector<double>& params) \r
144 {\r
145         return c*(t/=d)*t*t*t*t + b;\r
146 }\r
147         \r
148 double ease_out_quint (double t, double b, double c, double d, const std::vector<double>& params) \r
149 {\r
150         return c*((t=t/d-1)*t*t*t*t + 1) + b;\r
151 }\r
152         \r
153 double ease_in_out_quint (double t, double b, double c, double d, const std::vector<double>& params) \r
154 {\r
155         if ((t/=d/2) < 1) \r
156                 return c/2*t*t*t*t*t + b;\r
157 \r
158         return c/2*((t-=2)*t*t*t*t + 2) + b;\r
159 }\r
160         \r
161 double ease_out_in_quint (double t, double b, double c, double d, const std::vector<double>& params) \r
162 {\r
163         if (t < d/2) \r
164                 return ease_out_quint (t*2, b, c/2, d, params);\r
165 \r
166         return ease_in_quint((t*2)-d, b+c/2, c/2, d, params);\r
167 }       \r
168 \r
169 double ease_in_sine (double t, double b, double c, double d, const std::vector<double>& params) \r
170 {\r
171         return -c * std::cos(t/d * (PI/2)) + c + b;\r
172 }       \r
173 \r
174 double ease_out_sine (double t, double b, double c, double d, const std::vector<double>& params) \r
175 {\r
176         return c * std::sin(t/d * (PI/2)) + b;\r
177 }       \r
178 \r
179 double ease_in_out_sine (double t, double b, double c, double d, const std::vector<double>& params) \r
180 {\r
181         return -c/2 * (std::cos(PI*t/d) - 1) + b;\r
182 }       \r
183 \r
184 double ease_out_in_sine (double t, double b, double c, double d, const std::vector<double>& params)\r
185 {\r
186         if (t < d/2) \r
187                 return ease_out_sine (t*2, b, c/2, d, params);\r
188         \r
189         return ease_in_sine((t*2)-d, b+c/2, c/2, d, params);\r
190 }       \r
191 \r
192 double ease_in_expo (double t, double b, double c, double d, const std::vector<double>& params) \r
193 {\r
194         return (t==0) ? b : c * std::pow(2, 10 * (t/d - 1)) + b - c * 0.001;\r
195 }       \r
196 \r
197 double ease_out_expo (double t, double b, double c, double d, const std::vector<double>& params) \r
198 {\r
199         return (t==d) ? b+c : c * 1.001 * (-std::pow(2, -10 * t/d) + 1) + b;\r
200 }\r
201         \r
202 double ease_in_out_expo (double t, double b, double c, double d, const std::vector<double>& params) \r
203 {\r
204         if (t==0) \r
205                 return b;\r
206         if (t==d) \r
207                 return b+c;\r
208         if ((t/=d/2) < 1) \r
209                 return c/2 * std::pow(2, 10 * (t - 1)) + b - c * 0.0005;\r
210 \r
211         return c/2 * 1.0005 * (-std::pow(2, -10 * --t) + 2) + b;\r
212 }\r
213         \r
214 double ease_out_in_expo (double t, double b, double c, double d, const std::vector<double>& params) \r
215 {\r
216         if (t < d/2) \r
217                 return ease_out_expo (t*2, b, c/2, d, params);\r
218 \r
219         return ease_in_expo((t*2)-d, b+c/2, c/2, d, params);\r
220 }\r
221         \r
222 double ease_in_circ (double t, double b, double c, double d, const std::vector<double>& params) \r
223 {\r
224         return -c * (std::sqrt(1 - (t/=d)*t) - 1) + b;\r
225 }\r
226         \r
227 double ease_out_circ (double t, double b, double c, double d, const std::vector<double>& params) \r
228 {\r
229         return c * std::sqrt(1 - (t=t/d-1)*t) + b;\r
230 }\r
231         \r
232 double ease_in_out_circ (double t, double b, double c, double d, const std::vector<double>& params) \r
233 {\r
234         if ((t/=d/2) < 1) \r
235                 return -c/2 * (std::sqrt(1 - t*t) - 1) + b;\r
236 \r
237         return c/2 * (std::sqrt(1 - (t-=2)*t) + 1) + b;\r
238 }\r
239         \r
240 double ease_out_in_circ (double t, double b, double c, double d, const std::vector<double>& params) \r
241 {\r
242         if (t < d/2) return ease_out_circ(t*2, b, c/2, d, params);\r
243         return ease_in_circ((t*2)-d, b+c/2, c/2, d, params);\r
244 }\r
245         \r
246 double ease_in_elastic (double t, double b, double c, double d, const std::vector<double>& params)\r
247 {\r
248         if (t==0) return b;\r
249         if ((t/=d)==1) return b+c;\r
250         //var p:Number = !Boolean(p_params) || isNaN(p_params.period) ? d*.3 : p_params.period;\r
251         //var s:Number;\r
252         //var a:Number = !Boolean(p_params) || isNaN(p_params.amplitude) ? 0 : p_params.amplitude;\r
253         double p = params.size() > 0 ? params[0] : d*0.3;\r
254         double s;\r
255         double a = params.size() > 1 ? params[1] : 0.0;\r
256         if (a == 0.0 || a < std::abs(c)) \r
257         {\r
258                 a = c;\r
259                 s = p/4;\r
260         } \r
261         else \r
262                 s = p/(2*PI) * std::asin (c/a);\r
263         \r
264         return -(a*std::pow(2,10*(t-=1)) * std::sin( (t*d-s)*(2*PI)/p )) + b;\r
265 }\r
266         \r
267 double ease_out_elastic (double t, double b, double c, double d, const std::vector<double>& params) \r
268 {\r
269         if (t==0) \r
270                 return b;\r
271         if ((t/=d)==1) \r
272                 return b+c;\r
273         //var p:Number = !Boolean(p_params) || isNaN(p_params.period) ? d*.3 : p_params.period;\r
274         //var s:Number;\r
275         //var a:Number = !Boolean(p_params) || isNaN(p_params.amplitude) ? 0 : p_params.amplitude;\r
276         double p = params.size() > 0 ? params[0] : d*0.3;\r
277         double s;\r
278         double a = params.size() > 1 ? params[1] : 0.0;\r
279         if (a == 0.0 || a < std::abs(c))\r
280         {\r
281                 a = c;\r
282                 s = p/4;\r
283         } \r
284         else \r
285                 s = p/(2*PI) * std::asin (c/a);\r
286         \r
287         return (a*std::pow(2,-10*t) * std::sin( (t*d-s)*(2*PI)/p ) + c + b);\r
288 }       \r
289 \r
290 double ease_in_out_elastic (double t, double b, double c, double d, const std::vector<double>& params) \r
291 {\r
292         if (t==0)\r
293                 return b;\r
294         if ((t/=d/2)==2) \r
295                 return b+c;\r
296         //var p:Number = !Boolean(p_params) || isNaN(p_params.period) ? d*(.3*1.5) : p_params.period;\r
297         //var s:Number;\r
298         //var a:Number = !Boolean(p_params) || isNaN(p_params.amplitude) ? 0 : p_params.amplitude;\r
299         double p = params.size() > 0 ? params[0] : d*0.3*1.5;\r
300         double s;\r
301         double a = params.size() > 1 ? params[1] : 0.0;\r
302         if (a == 0.0 || a < std::abs(c)) \r
303         {\r
304                 a = c;\r
305                 s = p/4;\r
306         }\r
307         else\r
308                 s = p/(2*PI) * std::asin (c/a);\r
309         \r
310         if (t < 1) \r
311                 return -.5*(a*std::pow(2,10*(t-=1)) * std::sin( (t*d-s)*(2*PI)/p )) + b;\r
312 \r
313         return a*std::pow(2,-10*(t-=1)) * std::sin( (t*d-s)*(2*PI)/p )*.5 + c + b;\r
314 }\r
315         \r
316 double ease_out_in_elastic (double t, double b, double c, double d, const std::vector<double>& params) \r
317 {\r
318         if (t < d/2) return ease_out_elastic (t*2, b, c/2, d, params);\r
319         return ease_in_elastic((t*2)-d, b+c/2, c/2, d, params);\r
320 }\r
321         \r
322 double ease_in_back (double t, double b, double c, double d, const std::vector<double>& params) \r
323 {\r
324         //var s:Number = !Boolean(p_params) || isNaN(p_params.overshoot) ? 1.70158 : p_params.overshoot;\r
325         double s = params.size() > 0 ? params[0] : 1.70158;\r
326         return c*(t/=d)*t*((s+1)*t - s) + b;\r
327 }\r
328         \r
329 double ease_out_back (double t, double b, double c, double d, const std::vector<double>& params)\r
330 {\r
331         //var s:Number = !Boolean(p_params) || isNaN(p_params.overshoot) ? 1.70158 : p_params.overshoot;\r
332         double s = params.size() > 0 ? params[0] : 1.70158;\r
333         return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;\r
334 }\r
335         \r
336 double ease_in_out_back (double t, double b, double c, double d, const std::vector<double>& params)\r
337 {\r
338         //var s:Number = !Boolean(p_params) || isNaN(p_params.overshoot) ? 1.70158 : p_params.overshoot;\r
339         double s = params.size() > 0 ? params[0] : 1.70158;\r
340         if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;\r
341         return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;\r
342 }\r
343         \r
344 double ease_out_int_back (double t, double b, double c, double d, const std::vector<double>& params)\r
345 {\r
346         if (t < d/2) return ease_out_back (t*2, b, c/2, d, params);\r
347         return ease_in_back((t*2)-d, b+c/2, c/2, d, params);\r
348 }\r
349         \r
350 double ease_out_bounce (double t, double b, double c, double d, const std::vector<double>& params) \r
351 {\r
352         if ((t/=d) < (1/2.75))\r
353                 return c*(7.5625*t*t) + b;\r
354         else if (t < (2/2.75))\r
355                 return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;\r
356         else if (t < (2.5/2.75))\r
357                 return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;\r
358         else \r
359                 return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;    \r
360 }\r
361         \r
362 double ease_in_bounce (double t, double b, double c, double d, const std::vector<double>& params)\r
363 {\r
364         return c - ease_out_bounce (d-t, 0, c, d, params) + b;\r
365 }\r
366 \r
367 double ease_in_out_bounce (double t, double b, double c, double d, const std::vector<double>& params) \r
368 {\r
369         if (t < d/2) return ease_in_bounce (t*2, 0, c, d, params) * .5 + b;\r
370         else return ease_out_bounce (t*2-d, 0, c, d, params) * .5 + c*.5 + b;\r
371 }\r
372         \r
373 \r
374 double ease_out_in_bounce (double t, double b, double c, double d, const std::vector<double>& params) \r
375 {\r
376         if (t < d/2) return ease_out_bounce (t*2, b, c/2, d, params);\r
377         return ease_in_bounce((t*2)-d, b+c/2, c/2, d, params);\r
378 }\r
379 \r
380 tweener_t get_tweener(std::wstring name)\r
381 {\r
382         std::transform(name.begin(), name.end(), name.begin(), std::tolower);\r
383 \r
384         if(name == L"linear")\r
385                 return [](double t, double b, double c, double d){return ease_none(t, b, c, d, std::vector<double>());};\r
386         \r
387         std::vector<double> params;\r
388         \r
389         static const boost::wregex expr(L"(?<NAME>\\w*)(:(?<V0>\\d+\\.?\\d?))?(:(?<V1>\\d+\\.?\\d?))?"); // boost::regex has no repeated captures?\r
390         boost::wsmatch what;\r
391         if(boost::regex_match(name, what, expr))\r
392         {\r
393                 name = what["NAME"].str();\r
394                 if(what["V0"].matched)\r
395                         params.push_back(boost::lexical_cast<double>(what["V0"].str()));\r
396                 if(what["V1"].matched)\r
397                         params.push_back(boost::lexical_cast<double>(what["V1"].str()));\r
398         }\r
399                 \r
400         typedef std::function<double(double, double, double, double, const std::vector<double>&)> tween_t;      \r
401         static const std::unordered_map<std::wstring, tween_t> tweens = boost::assign::map_list_of      \r
402                 (L"",                                   ease_none                  )    \r
403                 (L"linear",                             ease_none                  )    \r
404                 (L"easenone",                   ease_none                  )\r
405                 (L"easeinquad",                 ease_in_quad       )\r
406                 (L"easeoutquad",                ease_out_quad      )\r
407                 (L"easeinoutquad",              ease_in_out_quad   )\r
408                 (L"easeoutinquad",              ease_out_in_quad   )\r
409                 (L"easeincubic",                ease_in_cubic      )\r
410                 (L"easeoutcubic",               ease_out_cubic     )\r
411                 (L"easeinoutcubic",             ease_in_out_cubic  )\r
412                 (L"easeoutincubic",             ease_out_in_cubic  )\r
413                 (L"easeinquart",                ease_in_quart      )\r
414                 (L"easeoutquart",               ease_out_quart     )\r
415                 (L"easeinoutquart",             ease_in_out_quart  )\r
416                 (L"easeoutinquart",             ease_out_in_quart  )\r
417                 (L"easeinquint",                ease_in_quint      )\r
418                 (L"easeoutquint",               ease_out_quint     )\r
419                 (L"easeinoutquint",             ease_in_out_quint  )\r
420                 (L"easeoutinquint",             ease_out_in_quint  )\r
421                 (L"easeinsine",                 ease_in_sine       )\r
422                 (L"easeoutsine",                ease_out_sine      )\r
423                 (L"easeinoutsine",              ease_in_out_sine   )\r
424                 (L"easeoutinsine",              ease_out_in_sine   )\r
425                 (L"easeinexpo",                 ease_in_expo       )\r
426                 (L"easeoutexpo",                ease_out_expo      )\r
427                 (L"easeinoutexpo",              ease_in_out_expo   )\r
428                 (L"easeoutinexpo",              ease_out_in_expo   )\r
429                 (L"easeincirc",                 ease_in_circ       )\r
430                 (L"easeoutcirc",                ease_out_circ      )\r
431                 (L"easeinoutcirc",              ease_in_out_circ   )\r
432                 (L"easeoutincirc",              ease_out_in_circ   )\r
433                 (L"easeinelastic",              ease_in_elastic    )\r
434                 (L"easeoutelastic",             ease_out_elastic   )\r
435                 (L"easeinoutelastic",   ease_in_out_elastic)\r
436                 (L"easeoutinelastic",   ease_out_in_elastic)\r
437                 (L"easeinback",                 ease_in_back       )\r
438                 (L"easeoutback",                ease_out_back      )\r
439                 (L"easeinoutback",              ease_in_out_back   )\r
440                 (L"easeoutintback",             ease_out_int_back  )\r
441                 (L"easeoutbounce",              ease_out_bounce    )\r
442                 (L"easeinbounce",               ease_in_bounce     )\r
443                 (L"easeinoutbounce",    ease_in_out_bounce )\r
444                 (L"easeoutinbounce",    ease_out_in_bounce );\r
445 \r
446         auto it = tweens.find(name);\r
447         if(it == tweens.end())\r
448                 it = tweens.find(L"linear");\r
449         \r
450         return [=](double t, double b, double c, double d)\r
451         {\r
452                 return it->second(t, b, c, d, params);\r
453         };\r
454 };\r
455 \r
456 }