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