You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
223 lines
6.3 KiB
223 lines
6.3 KiB
//
|
|
// 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<float> 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, Time>
|
|
T indicates the type of the value (for example: float, float[16], std::array<float, 16>)
|
|
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<S(S)> 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 <typename T, typename Time>
|
|
class interpolator {
|
|
// ctor
|
|
template <typename S, typename F>
|
|
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 <typename F>
|
|
void set_ease_function(F const & new_ease_function);
|
|
};
|
|
*/
|
|
|
|
#ifndef J_interpolator_h
|
|
#define J_interpolator_h
|
|
|
|
#include <tr1/functional>
|
|
|
|
namespace J {
|
|
|
|
namespace interpolator_details {
|
|
// some templates to handle arrays...
|
|
template <typename Scalar, typename T>
|
|
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 <typename Scalar, typename T, size_t N>
|
|
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 <typename Scalar, typename T, size_t N>
|
|
void interpolate(Scalar const & ratio, std::array<T, N> & value, std::array<T, N> const & begin_value, std::array<T, N> const & end_value){
|
|
for (unsigned int i = 0; i < N; ++i)
|
|
interpolate(ratio, value[i], begin_value[i], end_value[i]);
|
|
}
|
|
|
|
template <typename T>
|
|
struct scalar_of {
|
|
// TODO: find out how to make it work for user-types
|
|
// maybe require T::Scalar
|
|
typedef T type;
|
|
};
|
|
template <typename T, size_t N>
|
|
struct scalar_of<T[N]> {
|
|
typedef typename scalar_of<T>::type type;
|
|
};
|
|
template <typename T, size_t N>
|
|
struct scalar_of<std::array<T, N> > {
|
|
typedef typename scalar_of<T>::type type;
|
|
};
|
|
}
|
|
|
|
namespace interpolators {
|
|
struct linear{
|
|
template <typename T>
|
|
T operator()(T x){
|
|
return x;
|
|
}
|
|
};
|
|
|
|
struct cubic_in_out{
|
|
template <typename T>
|
|
T operator()(T x){
|
|
return 3*x*x - 2*x*x*x;
|
|
}
|
|
};
|
|
|
|
struct quintic_in_out{
|
|
template <typename T>
|
|
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 <typename T>
|
|
T operator()(T x){
|
|
return 0.5f - 0.5f*std::cos(x*M_PI);
|
|
}
|
|
};
|
|
}
|
|
|
|
template <typename T, typename Time = float>
|
|
class interpolator {
|
|
typedef typename interpolator_details::scalar_of<T>::type Scalar;
|
|
typedef std::tr1::function<Scalar (Scalar)> EaseFunction;
|
|
|
|
EaseFunction ease_function;
|
|
|
|
Time length;
|
|
Time steps;
|
|
|
|
T value;
|
|
T begin_value;
|
|
T end_value;
|
|
|
|
bool done;
|
|
|
|
public:
|
|
|
|
template <typename S>
|
|
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 <typename S, typename F>
|
|
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 <size_t N>
|
|
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 <typename F>
|
|
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
|
|
|