// // interpolator.h // J // // Created by Joshua Moerman on 9/2/11. // Copyright 2011 Vadovas. All rights reserved. // /* The interpolator interpolates between the current value and the value to interpolate to, both values are stored in the interpolator object. For easing there are different functors given (in the namespace interpolators: linear, cubic_in_out, quintic_in_out, cosine_in_out), but using a custom ease function is also possible (as long as it is convertible to std::function, see below for the type). The constructor takes three values: the first is the begin_value (usually 0), the second is the length of interpolation time, the last is the ease function. The interpolation time and the ease function can be changed later with the corresponding setters. The interpolated value can be retrieved with .get_value(), setting a new value to interpolate to is done via .set_value(new_value). Updating the time is done with .interpolate(dt). For example (interpolating a float from 0 to 10 in 5 seconds, with easing): // creation: interpolator my_value (0.0, 5.0, interpolators::cosine_in_out); my_value.set_value(10.0); // in update function: my_value.interpolate(dt); do_magic(my_value.get_value()); It can also be used with c-style arrays and std::array's. This can be usefull for interpolating matrices or colours and such. Templates and types: interpolator T indicates the type of the value (for example: float, float[16], std::array) Time indicates the type of the time-steps, you can use int if the system is frame-based (default: float) ease_function has type std::function where S is the element-type of T, this means: If T = float (or any other single value) -> S = float If T = float[4] (or std::array) -> S = float In other cases (eg. custom types) it will probably not work. Synopsis: template class interpolator { // ctor template interpolator(S const & begin_value_, Time const & length_, F const & ease_function_ = interpolators::cubic_in_out()); // time-update: void interpolate(Time const & dt = 1) // getters operator T const & () const; T const & get_value() const; // setters void set_value(T const & new_value); void set_length(Time new_length); template void set_ease_function(F const & new_ease_function); }; */ #ifndef J_interpolator_h #define J_interpolator_h #include #include namespace J { namespace interpolator_details { // some templates to handle arrays... template void interpolate(Scalar const & ratio, T & value, T const & begin_value, T const & end_value){ value = (Scalar(1.0) - ratio)*begin_value + ratio*end_value; } template void interpolate(Scalar const & ratio, T (& value)[N], T const (& begin_value)[N], T const (& end_value)[N]){ for (unsigned int i = 0; i < N; ++i) interpolate(ratio, value[i], begin_value[i], end_value[i]); } template void interpolate(Scalar const & ratio, std::array & value, std::array const & begin_value, std::array const & end_value){ for (unsigned int i = 0; i < N; ++i) interpolate(ratio, value[i], begin_value[i], end_value[i]); } template struct scalar_of { // TODO: find out how to make it work for user-types // maybe require T::Scalar typedef T type; }; template struct scalar_of { typedef typename scalar_of::type type; }; template struct scalar_of > { typedef typename scalar_of::type type; }; } namespace interpolators { struct linear{ template T operator()(T x){ return x; } }; struct cubic_in_out{ template T operator()(T x){ return 3*x*x - 2*x*x*x; } }; struct quintic_in_out{ template T operator()(T x){ return 6*x*x*x*x*x - 15*x*x*x*x + 10*x*x*x; return x*x*x*(x*(x*6-15)+10); } }; struct cosine_in_out{ template T operator()(T x){ return 0.5f - 0.5f*std::cos(x*M_PI); } }; } template class interpolator { typedef typename interpolator_details::scalar_of::type Scalar; typedef std::function EaseFunction; EaseFunction ease_function; Time length; Time steps; T value; T begin_value; T end_value; bool done; public: template interpolator(S const & begin_value_, Time const & length_) : ease_function(interpolators::cubic_in_out()) , length(length_) , steps(length_) , value(begin_value_) , begin_value(begin_value_) , end_value(begin_value_) , done(false) {} // NOTE: we can't use template-defaults in functions, so we have to make an overload for this one... template interpolator(S const & begin_value_, Time const & length_, F const & ease_function_) : ease_function(ease_function_) , length(length_) , steps(0) , value(begin_value_) , begin_value(begin_value_) , end_value(begin_value_) , done(false) {} operator T const & () const{ return value; } T const & get_value() const{ return value; } void set_value(T const & new_value){ begin_value = value; end_value = new_value; steps = 0; done = false; } // FIXME: temporary hack template void set_value(Scalar const (& new_value)[N]){ begin_value = value; for(unsigned int i = 0; i < N; ++i) end_value[i] = new_value[i]; steps = 0; done = false; } void set_length(Time new_length){ Scalar ratio = (Scalar) steps / (Scalar) length; length = new_length; steps = ratio*length; done = false; } template void set_ease_function(F const & new_ease_function){ ease_function = new_ease_function; } void interpolate(Time const & dt = 1){ if(done) return; steps += dt; if(steps >= length){ steps = length; done = true; } Scalar ratio = (Scalar) steps / (Scalar) length; ratio = ease_function(ratio); interpolator_details::interpolate(ratio, value, begin_value, end_value); } }; } // namespace J #endif