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