├── LICENSE ├── README.md ├── callback.h ├── catch.cpp ├── catch.hpp ├── test.mk ├── tween.cpp └── tween.h /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jonas Minnberg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## C++11 Tween engine 3 | 4 | First of all, `Tween::update(double t)` needs to be called regularly. Normally you do it once every render loop, 5 | but you can also safely do it from another thread: 6 | 7 | ```c++ 8 | std::thread tweenThread([]() { 9 | auto t0 = getms(); 10 | while(run) { 11 | float seconds = (double)(getms()-t0) / 1000.0; 12 | Tween::updateTweens(seconds); 13 | sleepms(50); 14 | } 15 | }); 16 | ``` 17 | 18 | Here is how to create a simple tween that interpolates a variable from 0 to 100 over one second: 19 | 20 | ```c++ 21 | float x = 0; 22 | Tween::make().to(x, 100.0).seconds(1.0); 23 | ``` 24 | 25 | It will use the default *smoothstep* tweening function and start immediately after the whole expression is evaluated. 26 | 27 | You don't need to tween an existing value, but if you don't, you probably want 28 | a callback when the value changes: 29 | 30 | ```c++ 31 | Tween::make().fromTo(200, 0).linear().onUpdate([](int x) { 32 | printf("%d\n", x); 33 | }); 34 | ``` 35 | 36 | You can tween anything that is either an arithmetic value or something that looks like a *std::vector*; 37 | meaning anything that can be indexed with `[]` and has a `size()` method. 38 | 39 | This example uses the *vec2f* class to tween a position in from the left of the screen, delay for a second, then tween out to the right; 40 | 41 | ```c++ 42 | vec2f textPos(10, 100); 43 | Tween::make().from(textPos, vec2f(-500, 100)).easeInBack().onComplete([]() { 44 | Tween::make().to(textPos, vec2f(1500, 100)).easeOutBack().delay(1.0); 45 | }); 46 | ``` 47 | 48 | Many methods affect only the latest tweened value in the expression, and in fact are not even possible unless a `to()` or `from()` method 49 | has been called. Also, the `onUpdate()` callback can take 0 or more arguments, but the first argument must be of the same type as the 50 | last tweened value: 51 | 52 | ```c++ 53 | Color red { 0xffff0000 }; 54 | 55 | Tween::make().delay(1.0).to(0xffffffff).seconds(1.0); 56 | // ^^ Wont compile, delay() needs to be called after to() 57 | 58 | auto handleColor = [](int color) { 59 | // Do something with color 60 | }; 61 | 62 | Tween::make().to(0xffffffff).delay(1.0).onUpdate(handleColor).seconds(1.0); 63 | // ^^ Wont compile, handleColor must take 0 arguments, or a type Color 64 | ``` 65 | 66 | If you need to control the tween, you can save the result of the tween expression, but then it will not start automatically; 67 | 68 | ```c++ 69 | Tween myTween = Tween::make().from(x, 1.0).sine().repeating().seconds(0.5); 70 | myTween.start() 71 | ... 72 | myTween.cancel() 73 | ``` 74 | 75 | When tweening members of an object, a good habit is to tell the tween about it, so it can retain a reference to it during the 76 | tween; 77 | 78 | ```c++ 79 | 80 | auto mySprite = std::make_shared(0,0); 81 | Tween::make().retain(mySprite).to(mySprite->xpos, 100.0).seconds(2.5); 82 | mySprite = nullptr; // Safe, shared_ptr still held by the Tween 83 | ``` 84 | -------------------------------------------------------------------------------- /callback.h: -------------------------------------------------------------------------------- 1 | #ifndef CALLBACK_H 2 | #define CALLBACK_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace utils { 12 | 13 | template using require_fn0 = typename std::conditional()()), R>::type; 14 | template using require_fn1 = typename std::conditional()(0)), R>::type; 15 | template using require_fn2 = typename std::conditional()(0,0)), R>::type; 16 | template using require_fn3 = typename std::conditional()(0,0,0)), R>::type; 17 | template using require_fn4 = typename std::conditional()(0,0,0,0)), R>::type; 18 | 19 | 20 | template struct FCallerBase { 21 | virtual void call(const A& ... a) = 0; 22 | }; 23 | 24 | template struct FCaller0 : FCallerBase { 25 | 26 | FCaller0(const T &t) : t(t) {} 27 | FCaller0(T&& t) : t(std::move(t)) {} 28 | 29 | void call(const A& ... a) override { 30 | //std::tuple args(a...); 31 | t(); 32 | } 33 | T t; 34 | }; 35 | 36 | template struct FCaller1 : public FCallerBase { 37 | 38 | FCaller1(const T &t) : t(t) {} 39 | FCaller1(T&& t) : t(std::move(t)) {} 40 | 41 | void call(const A& ... a) override { 42 | std::tuple args(a...); 43 | t(std::get<0>(args)); 44 | } 45 | T t; 46 | }; 47 | 48 | template struct FCaller2 : public FCallerBase { 49 | 50 | FCaller2(T&& t) : t(std::move(t)) {} 51 | FCaller2(const T &t) : t(t) {} 52 | 53 | void call(const A& ... a) override { 54 | std::tuple args(a...); 55 | t(std::get<0>(args), std::get<1>(args)); 56 | } 57 | T t; 58 | }; 59 | 60 | template struct FCaller3 : public FCallerBase { 61 | 62 | FCaller3(T&& t) : t(std::move(t)) {} 63 | FCaller3(const T &t) : t(t) {} 64 | 65 | void call(const A& ... a) override { 66 | std::tuple args(a...); 67 | t(std::get<0>(args), std::get<1>(args), std::get<2>(args)); 68 | } 69 | T t; 70 | }; 71 | 72 | template class CallbackCaller { 73 | public: 74 | 75 | template require_fn3 callme(T&& t) { 76 | auto s = std::make_shared::type, A...>>(std::forward(t)); 77 | callbacks.push_back(s); 78 | } 79 | 80 | template require_fn2 callme(T&& t) { 81 | auto s = std::make_shared::type, A...>>(std::forward(t)); 82 | callbacks.push_back(s); 83 | } 84 | 85 | template require_fn1 callme(T&& t) { 86 | auto s = std::make_shared::type, A...>>(std::forward(t)); 87 | callbacks.push_back(s); 88 | } 89 | 90 | template require_fn0 callme(T&& t) { 91 | auto s = std::make_shared::type, A...>>(std::forward(t)); 92 | callbacks.push_back(s); 93 | } 94 | 95 | void call(const A& ... a) { 96 | for(auto &cb : callbacks) { 97 | cb->call(a...); 98 | } 99 | } 100 | 101 | std::vector>> callbacks; 102 | 103 | }; 104 | 105 | } 106 | 107 | #endif // CALLBACK_H 108 | -------------------------------------------------------------------------------- /catch.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include "catch.hpp" 3 | -------------------------------------------------------------------------------- /test.mk: -------------------------------------------------------------------------------- 1 | 2 | 3 | CXXFLAGS := -std=c++0x -DTWEEN_UNIT_TEST -pthread -g -O2 4 | LDFLAGS := -pthread 5 | LD := $(CXX) 6 | 7 | tweentest : tween.o catch.o 8 | $(LD) $(LDFLAGS) -o$@ $^ 9 | -------------------------------------------------------------------------------- /tween.cpp: -------------------------------------------------------------------------------- 1 | #include "tween.h" 2 | 3 | #ifndef M_PI 4 | #define M_PI 3.14159265358979323846 5 | #endif 6 | 7 | namespace tween { 8 | 9 | std::vector> Tween::allTweens; 10 | double Tween::currentTime = 0; 11 | std::mutex Tween::tweenMutex; 12 | 13 | double Tween::linear_fn(double t) { 14 | return t; 15 | } 16 | 17 | double Tween::smoothStep_fn(double t) { 18 | return (t*t *(3 - 2*t)); 19 | } 20 | 21 | double Tween::easeInSine_fn (double t) { 22 | return 1 - cos(t * (M_PI/2)); 23 | } 24 | 25 | double Tween::sine_fn(double t) { 26 | return (sin(t * (M_PI*2) - M_PI/2) + 1.0)/2.0; 27 | } 28 | 29 | double Tween::easeOutSine_fn(double t) { 30 | return sin(t * (M_PI/2)); 31 | } 32 | 33 | double Tween::easeInOutSine_fn(double t) { 34 | return -0.5 * (cos(M_PI*t) - 1); 35 | } 36 | 37 | double Tween::easeInBack_fn (double t) { 38 | double s = 1.70158f; 39 | return t*t*((s+1)*t - s); 40 | } 41 | 42 | double Tween::easeOutBack_fn(double t) { 43 | double s = 1.70158f; 44 | t--; 45 | return (t*t*((s+1)*t + s) + 1); 46 | } 47 | 48 | double Tween::easeInOutBack_fn(double t) { 49 | double s = 1.70158f * 1.525f; 50 | //double s2 = s * 1.525f; 51 | t *= 2; 52 | if (t < 1) return 1.0/2*(t*t*((s+1)*t - s)); 53 | double postFix = t-=2; 54 | return 1.0/2*((postFix)*t*((s+1)*t + s) + 2); 55 | } 56 | 57 | TweenImpl::~TweenImpl() { 58 | if(!isTweening) { 59 | Tween::addTween(*this); 60 | } 61 | } 62 | 63 | Tween Tween::make() { 64 | return Tween(std::make_shared(currentTime, smoothStep_fn, false)); 65 | } 66 | 67 | void Tween::addTween(const TweenImpl& ti) { 68 | std::lock_guard guard(tweenMutex); 69 | allTweens.push_back(std::make_shared(ti)); 70 | allTweens.back()->isTweening = true; 71 | } 72 | 73 | void Tween::cancel() { 74 | std::lock_guard guard(tweenMutex); 75 | 76 | auto it = Tween::allTweens.begin(); 77 | while(it != Tween::allTweens.end()) { 78 | if(*it == impl) { 79 | Tween::allTweens.erase(it); 80 | break; 81 | } 82 | ++it; 83 | } 84 | } 85 | 86 | bool TweenImpl::step() { 87 | size_t ended = 0; 88 | Tween tween; 89 | for(auto &a : args) { 90 | float t = (float)((Tween::currentTime - startTime - a->delay) / totalTime); 91 | if(t < 0.0) 92 | continue; 93 | if(t > 1.0) { 94 | if(do_rep) 95 | t -= 1.0; 96 | else if(backto) { 97 | ended++; 98 | a->set(0.0, tween); 99 | } else { 100 | ended++; 101 | a->set(1.0, tween); 102 | continue; 103 | } 104 | } 105 | a->set(tween_func(t), tween); 106 | } 107 | return ended < args.size(); 108 | } 109 | 110 | int Tween::updateTweens(double t) { 111 | 112 | std::lock_guard guard(tweenMutex); 113 | 114 | static std::vector> doneTweens; 115 | 116 | currentTime = t; 117 | auto it = allTweens.begin(); 118 | while(it != allTweens.end()) { 119 | if(!(*it)->step()) { 120 | doneTweens.push_back(*it); 121 | it = allTweens.erase(it); 122 | } else { 123 | it++; 124 | } 125 | } 126 | 127 | for(auto &dt : doneTweens) { 128 | Tween tw; 129 | dt->on_complete_cb.call(tw, 1.0); 130 | } 131 | doneTweens.clear(); 132 | 133 | return (int)allTweens.size(); 134 | } 135 | 136 | Tween::Tween(int dummy) : impl(std::make_shared(currentTime, smoothStep_fn)) { 137 | } 138 | 139 | Tween::Tween(std::shared_ptr i) : impl(i) { 140 | } 141 | 142 | void Tween::finish() { 143 | for(auto &a : impl->args) { 144 | a->set(1.0, *this); 145 | } 146 | cancel(); 147 | } 148 | 149 | Tween &Tween::seconds(float s) { 150 | impl->totalTime = s; 151 | return *this; 152 | } 153 | 154 | Tween &Tween::speed(float s) { 155 | impl->dspeed = s; 156 | return *this; 157 | } 158 | 159 | Tween &Tween::linear() { 160 | impl->tween_func = linear_fn; 161 | return *this; 162 | } 163 | 164 | Tween &Tween::smoothstep() { 165 | impl->tween_func = smoothStep_fn; 166 | return *this; 167 | } 168 | 169 | Tween &Tween::easeinback() { 170 | impl->tween_func = easeInBack_fn; 171 | return *this; 172 | } 173 | 174 | Tween &Tween::easeoutback() { 175 | impl->tween_func = easeOutBack_fn; 176 | return *this; 177 | } 178 | 179 | Tween &Tween::easeinsine() { 180 | impl->tween_func = easeInSine_fn; 181 | return *this; 182 | } 183 | 184 | Tween &Tween::easeoutsine() { 185 | impl->tween_func = easeOutSine_fn; 186 | return *this; 187 | } 188 | 189 | Tween &Tween::sine() { 190 | impl->tween_func = sine_fn; 191 | impl->backto = true; 192 | return *this; 193 | } 194 | 195 | Tween &Tween::repeating() { 196 | impl->do_rep = true; 197 | return *this; 198 | } 199 | 200 | void Tween::start() { 201 | if(!impl->isTweening) { 202 | impl->startTime = currentTime; 203 | std::lock_guard guard(tweenMutex); 204 | allTweens.push_back(impl); 205 | allTweens.back()->isTweening = true; 206 | } 207 | } 208 | 209 | } // namespace tween 210 | 211 | #if (defined UNIT_TEST || defined TWEEN_UNIT_TEST) 212 | 213 | #include "catch.hpp" 214 | 215 | #include 216 | #include 217 | #include 218 | #include 219 | #include 220 | #include 221 | 222 | void sleepms(uint ms) { 223 | #ifdef WIN32 224 | Sleep(ms); 225 | #else 226 | usleep(ms*1000); 227 | #endif 228 | } 229 | 230 | uint64_t getms() { 231 | timeval tv; 232 | gettimeofday(&tv, NULL); 233 | return (tv.tv_sec * 1000 + tv.tv_usec/1000); 234 | } 235 | 236 | TEST_CASE("tween::basic", "Basic tween") { 237 | 238 | using tween::Tween; 239 | 240 | struct { int score = 0; short energy = 0; } demo; 241 | 242 | auto showScore = [&](int score) { 243 | printf("SCORE:%d\n", score); 244 | }; 245 | 246 | auto showFood = [](int food, Tween t, double v) -> int { 247 | printf("FOOD:%d (%f)\n", food, v); 248 | return 0; 249 | }; 250 | 251 | Tween myt = Tween::make().linear().to(demo.score, 10).onUpdate(showScore).from(demo.energy, 250).onUpdate(showFood).seconds(2); 252 | 253 | double t = 0; 254 | for(int i=0; i<10; ++i) { 255 | Tween::updateTweens(t += 0.1); 256 | } 257 | REQUIRE(demo.score == 0); 258 | REQUIRE(demo.energy == 250); 259 | 260 | myt.start(); 261 | for(int i=0; i<10; ++i) { 262 | Tween::updateTweens(t += 0.1); 263 | } 264 | 265 | REQUIRE(demo.score == 5); 266 | REQUIRE(demo.energy == 125); 267 | for(int i=0; i<50; ++i) 268 | Tween::updateTweens(t += 0.1); 269 | 270 | REQUIRE(demo.score == 10); 271 | REQUIRE(demo.energy == 0); 272 | 273 | std::vector v = { 0, 1, 10, 100 }; 274 | Tween::make().to(v, {4,4,4,4}).seconds(4.0); 275 | for(int i=0; i<10; ++i) 276 | Tween::updateTweens(t += 0.1); 277 | REQUIRE(v[0] == 0.625); 278 | REQUIRE(v[3] == 85); 279 | for(int i=0; i<30; ++i) 280 | Tween::updateTweens(t += 0.1); 281 | REQUIRE(v[1] == 4); 282 | REQUIRE(v[2] == 4); 283 | 284 | Tween::make().fromTo(10,20).onUpdate([](int x) { printf("%d\n", x); }).seconds(1); 285 | for(int i=0; i<20; ++i) 286 | Tween::updateTweens(t += 0.1); 287 | 288 | REQUIRE(Tween::updateTweens(t) == 0); 289 | } 290 | 291 | TEST_CASE("tween::thread", "Tween threading") { 292 | 293 | using tween::Tween; 294 | 295 | std::mutex m; 296 | 297 | std::atomic run; 298 | std::atomic total; 299 | std::atomic done; 300 | done = 0; 301 | run = true; 302 | total = 0; 303 | 304 | Tween::updateTweens(0.0); 305 | 306 | auto l = [&run]() { 307 | auto t0 = getms(); 308 | while(run) { 309 | auto t1 = getms(); 310 | float t = (double)(t1-t0) / 1000.0; 311 | Tween::updateTweens(t); 312 | sleepms(2); 313 | } 314 | }; 315 | std::thread tweenThread(l); 316 | 317 | const int ITERATIONS = 500; 318 | const int TOTAL = (2+3+4+5)*ITERATIONS; 319 | 320 | for(int i=0; i guard(m); 331 | int tot = total; 332 | int dn = done; 333 | REQUIRE(tot == TOTAL); 334 | REQUIRE(dn == ITERATIONS); 335 | } 336 | 337 | #endif -------------------------------------------------------------------------------- /tween.h: -------------------------------------------------------------------------------- 1 | #ifndef TWEEN_H 2 | #define TWEEN_H 3 | 4 | #include "callback.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace tween { 14 | 15 | class Tween; 16 | 17 | template using is_compound = typename std::enable_if::value,R>::type; 18 | 19 | struct TweenAttrBase { 20 | virtual void set(double v, Tween& t) = 0; 21 | virtual bool compare(void *p) { return false; } 22 | double delay = 0.0; 23 | }; 24 | 25 | template struct TweenAttr : public TweenAttrBase { 26 | 27 | TweenAttr(T *target, T value, int cycles) : startValue(*target), delta(value - *target), maxValue(std::numeric_limits::max()), target(target) { 28 | if(cycles != 1) { 29 | maxValue = delta+1; 30 | delta = (delta+1)*cycles-1; 31 | } 32 | } 33 | 34 | T startValue; 35 | T delta; 36 | T maxValue; 37 | 38 | T *target; 39 | 40 | utils::CallbackCaller on_update_cb; 41 | 42 | virtual void set(double v, Tween &t) override { 43 | T newValue = startValue + static_cast(fmod(v * delta, maxValue)); 44 | if(newValue != *target) { 45 | *target = newValue; 46 | on_update_cb.call(*target, t, v); 47 | } 48 | } 49 | 50 | virtual bool compare(void *p) override { 51 | return p == static_cast(target); 52 | } 53 | }; 54 | 55 | template class TweenT; 56 | 57 | struct TweenImpl { 58 | 59 | TweenImpl(double t, std::function f, bool it = true) : startTime(t), tween_func(f), isTweening(it) {} 60 | 61 | ~TweenImpl(); 62 | 63 | double startTime; 64 | double totalTime; 65 | double dspeed; 66 | 67 | bool do_rep = false; 68 | bool backto = false; 69 | 70 | std::vector> args; 71 | std::function tween_func; 72 | utils::CallbackCaller on_complete_cb; 73 | std::vector> refs; 74 | 75 | bool isTweening = true; 76 | 77 | bool step(); 78 | 79 | }; 80 | 81 | class Tween { 82 | protected: 83 | Tween(std::shared_ptr i); 84 | public: 85 | Tween(int dummy = 0); 86 | //Tween& operator=(const Tween &other) = delete; 87 | //Tween(const Tween& other) = delete; 88 | 89 | static Tween make(); 90 | 91 | void start(); 92 | 93 | Tween& seconds(float s); 94 | Tween& speed(float s); 95 | Tween& linear(); 96 | Tween& smoothstep(); 97 | Tween& easeinback(); 98 | Tween& easeoutback(); 99 | Tween& easeinsine(); 100 | Tween& easeoutsine(); 101 | Tween& sine(); 102 | Tween& repeating(); 103 | 104 | //template ::value>::type> 105 | template is_compound to(T &target, T value, int cycles = 1) { 106 | for(int i=0; i TweenT to(T &target, U value, int cycles = 1) { 113 | std::lock_guard guard(tweenMutex); 114 | auto *targetp = ⌖ 115 | for(auto &t : allTweens) { 116 | auto it = t->args.begin(); 117 | while(it != t->args.end()) { 118 | if((*it)->compare((void*)targetp)) { 119 | // Already tweening this value 120 | it = t->args.erase(it); 121 | } else 122 | it++; 123 | } 124 | } 125 | 126 | auto taptr = std::make_shared>(targetp, static_cast(value), cycles); 127 | impl->args.emplace_back(taptr); 128 | return TweenT(impl, taptr); 129 | } 130 | 131 | //template ::value>::type> 132 | template is_compound from(T &target, T value) { 133 | for(int i=0; i TweenT from(T &target, U value) { 140 | T realValue = target; 141 | target = value; 142 | return to(target, realValue); 143 | } 144 | 145 | template void retain(std::shared_ptr obj) { 146 | impl->refs.push_back(std::shared_ptr(obj)); 147 | } 148 | 149 | template TweenT fromTo(T &target, U v0, U v1, int cycles = 1) { 150 | *target = static_cast(v0); 151 | return to(target, v1); 152 | } 153 | 154 | template TweenT fromTo(T v0, U v1, int cycles = 1) { 155 | auto p = std::make_shared(v0); 156 | retain(p); 157 | return to(*(p.get()), v1); 158 | } 159 | 160 | template Tween& onComplete(F&& f) { 161 | impl->on_complete_cb.callme(std::forward(f)); 162 | return *this; 163 | } 164 | 165 | void cancel(); 166 | void finish(); 167 | 168 | static int updateTweens(double t); 169 | static void addTween(const TweenImpl& ti); 170 | 171 | protected: 172 | 173 | static double linear_fn(double t); 174 | static double smoothStep_fn(double t); 175 | static double easeInSine_fn(double t); 176 | static double easeOutSine_fn(double t); 177 | static double easeInOutSine_fn(double t); 178 | static double easeInBack_fn(double t); 179 | static double easeOutBack_fn(double t); 180 | static double easeInOutBack_fn(double t); 181 | static double sine_fn(double t); 182 | 183 | std::shared_ptr impl; 184 | 185 | public: 186 | static double currentTime; 187 | static std::vector> allTweens; 188 | static std::mutex tweenMutex; 189 | }; 190 | 191 | template class TweenT : public Tween { 192 | public: 193 | 194 | TweenT(std::shared_ptr i, std::shared_ptr> ta) : Tween(i), ta(ta) {} 195 | 196 | TweenT(const TweenT &t) { 197 | impl = t.impl; 198 | } 199 | 200 | template TweenT& onUpdate(F &&f) { 201 | ta->on_update_cb.callme(std::forward(f)); 202 | return *this; 203 | } 204 | 205 | TweenT& delay(float d) { 206 | ta->delay = d; 207 | return *this; 208 | } 209 | 210 | std::shared_ptr> ta; 211 | }; 212 | 213 | }; // namespace tween 214 | 215 | #endif // TWEEN_H --------------------------------------------------------------------------------