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