Claw  1.7.3
C++ Tweeners

Tween is a C++ library providing an implementation for tweening. In details, this library allows to interpolate the intermediate values between two given values during a time interval. This practice is well used in animation and is widely popular in the context of ActionScript/Flash games and websites. The goal of this library is to provide similar functionalities to C++ programs.

Why this library

Making a variable to vary from a original value to a final value following a predefined function is something recurrent in programming and especially during the development of games. As far as we know, the only free C++ library providing this functionality is cpptweener, a port of the tweener library. Unfortunately, even if cpptweener is an accurate port of the ActionScript tweener library, she lacks several functionalities.

Our implementation is inspired from the aforementioned cpptweener and tweener libraries. Indeed, the same terminology will be used and the equations of Penners are also implemented. claw::tween provides the following functionalities:

Examples

This section presents some simple programs to show the usage of the claw::tween library.

Interpolating the value of a variable

The following C++ program changes the value of a given variable from its initial value to a target value using a linear interpolation of the values in-between.

#include <iostream>
int main()
{
double x(10);
while ( !t.is_finished() )
{
t.update(0.1);
std::cout << x << std::endl;
}
return 0;
}

In the above function a variable x is created with an initial value. The tweener is then created:

Since there is only a single value to interpolate, a single_tweener is used. Let's observe the detail of the constructor's parameters:

single_tweener( double& val, double end, double duration, easing_function e );

The first parameter is the variable whose value is interpolated. The second parameter is the value that must be assigned to val at the end of the interpolation. Then the third parameter is the total duration of the interpolation. The unit can be whatever the user wants (seconds, milliseconds, …) as long as this is the same unit used for the updates. Finally, the fourth parameter is the function used to compute the intermediate values. See Easing functions for details about those functions.

The next part of the example updates the tweeners until the end value is reached.

while ( !t.is_finished() )
{
t.update(0.1);
std::cout << x << std::endl;
}

The parameter of the update function is the elapsed time since the last update. Here the loop does ten iterations and produces the following output:

10.5
11
11.5
12
12.5
13
13.5
14
14.5
15

Interpolating the value of several variables

The following C++ program changes the value of several variables from their initial values to a target value using a linear interpolation of the values in-between. Since we do not want to manage each tweener individually, we will use a tweener_group to manage them.

#include <iostream>
struct coord
{
double x;
double y;
};
int main()
{
coord c = { 0, 0 };
do
{
t.update(0.1);
std::cout << c.x << ' ' << c.y << std::endl;
}
while ( !t.is_finished() );
return 0;
}

Here there are several values to interpolate simultaneously. Instead of updating each tweener individually, they are inserted into a tweener_group that will manage them. One can notice that claw::tween::tweener_group is also a tweener, thus we insert groups inside groups.

Interpolating values using callbacks

In the previous example all the member variables of coord are public, thus they can be passed by reference to the constructor of single_tweener. Unfortunately in most real-life situations the member variables are not public and those values are modified using setters. In this situation, one can use the second constructor of single_tweener:

single_tweener
( double init, double end, double duration, update_function callback,
easing_function e );

This constructor accept a callback function as the fourth parameter. This function, whose signature must be void (double), is called at each update with the interpolated value as its only parameter.

Here is a modification of the previous example where setters are used to update the values of the coord. The callback is created using boost::bind to create a function with the adequate signature.

#include <iostream>
#include <boost/bind.hpp>
class coord
{
public:
double get_x() const { return m_x; }
void set_x( double x ) { m_x = x; }
double get_y() const { return m_y; }
void set_y( double y ) { m_y = y; }
private:
double m_x;
double m_y;
};
int main()
{
coord c;
( 0, 10, 1, boost::bind( &coord::set_x, &c, _1 ),
( 0, 10, 1, boost::bind( &coord::set_y, &c, _1 ),
do
{
t.update(0.1);
std::cout << c.get_x() << ' ' << c.get_y() << std::endl;
}
while ( !t.is_finished() );
return 0;
}

Tweener classes

The library provides three tweener classes to manage tweeners, named single_tweener, tweener_group and tweener_sequence. Each one fills a particular need described in the following sections.

The contract of the tweeners

The tweener interface, as described below, is quite simple:

class tweener
{
public:
typedef boost::function<void()> finish_callback();
public:
tweener();
tweener( const tweener& that );
tweener& operator=( const tweener& that );
void swap( tweener& that ) throw();
bool is_finished() const;
double update( double dt );
void on_finished( finish_callback f );
};

The tweener library use two functions to manage the tweeners. First, the bool is_finished() const must return true if and only if the final value has been reached. Then, double update(double) is used to update the intermediate values, where the single argument is the amount of time since the last update or since the construction of the tweener. This function returns the amount of dt not used in the update. For example, if a tweener is at 1 time unit from the end of the interpolation and update(1.5) is called on this tweener, then 0.5 is returned. Indeed, only 1 of the 1.5 available units have been used to reach the end of the interpolation.

When a tweener is finished, all the functions passed to void on_finished( finish_callback ) are called in sequence.

claw::tween::single_tweener

The claw::tween::single_tweener object is the class to use to make one value to vary from a source value to a final value in a given amount of time and according to a given easing function. The interface is described below:

class single_tweener
{
public:
typedef boost::function<void (double)> update_function;
typedef boost::function<double (double)> easing_function;
public:
single_tweener();
single_tweener
( double init, double end, double duration, update_function callback,
easing_function e );
single_tweener
( double& val, double end, double duration, easing_function e );
void set_init( double v );
void set_end( double v );
void set_duration( double v );
void set_callback( update_function f );
void set_easing( easing_function f );
bool is_finished() const;
double update( double dt );
void on_finished( finish_callback f );
};

The default behavior of the single_tweener is to use a callback function to send the new value to the caller at each update. Let's have a look at the following constructor.

single_tweener
( double init, double end, double duration, update_function callback,
easing_function e );

The two first arguments defined the value returned by the tweener respectively at the beginning and the end of the interpolation. The third parameter is the total duration of the interpolation. The unit can be whatever the user wants (seconds, milliseconds, …) as long as this is the same unit used for the updates. The fourth argument is the callback function called at each update. His signature must be void (double), where the argument receive the current intermediate value. Finally, the fifth parameter is the function used to compute the intermediate values. See Easing functions for details about this topic.

In the situation where the interpolation is done on a known variable, one would have to provide a callback function to assign the intermediate values to the variable. In order to ease the use of tweeners for this kind of situation, an other constructor is defined:

single_tweener( double& val, double end, double duration, easing_function e );

The difference with the aforementioned constructor is that there is no callback function to provide. Here, the first parameter is the variable whose value is interpolated. Its initial value is taken for the initial value of the interpolation. The created tweener will manage the assignment of the intermediate value to val.

In other words, the following program:

double v;
void assign_v( double i )
{
v = i;
}
single_tweener create_tweener()
{
return single_tweener(0, 10, 1, &assign_v, easing_linear::ease_in);
}

is completely equivalent to this more concise program:

double v(0);
single_tweener create_tweener()
{
return single_tweener(v, 10, 1, easing_linear::ease_in);
}

The void on_finished() function allows to insert a callback that will be called when the tweener finishes, during a call to double update(double).

claw::tween::tweener_group

Once that single_tweener is understood, the other tweener classes are quite simple. The claw::tween::tweener_group class is a tweener class that executes several tweeners simultaneously. Its interface is defined as follows:

class tweener_group:
{
public:
void insert( const tweener& t );
void clear();
bool is_finished() const;
double update( double dt );
void on_finished( finish_callback f );
};

The tweener_group class does not compute values by itself but forwards the call to update(double) to other tweeners, thus allowing to play several tweeners in parallel.

The bool is_finished() function returns true when all the tweeners of the group are finished. Also, tweeners can be inserted in a tweener_group at any time.

The void on_finished() function allows to insert a callback that will be called when the last tweener in the group finishes, during a call to double update(double).

claw::tween::tweener_sequence

The claw::tween::tweener_sequence class is a tweener class that executes several tweeners in a row. Its interface is defined as follows:

class tweener_sequence:
{
public:
void insert( const tweener& t );
void clear();
bool is_finished() const;
double update( double dt );
void on_finished( finish_callback f );
};

The tweener_sequence class does not compute values by itself but forwards the call to update(double) to the current active tweener, splitting the argument value among several tweeners if needed.

The bool is_finished() function returns true when all the tweeners of the sequence are finished. Also, tweeners can be inserted in a tweener_sequence at any time.

The void on_finished() function allows to insert a callback that will be called when the last tweener in the sequence finishes, during a call to double update(double).

Easing functions

The easing functions define how the intermediate values are computed during the interpolation. For example, when tweening a value from 0 to 1 during 1 second using a linear interpolation, the value at 0.5 second must be 0.5. But when using a quadratic interpolation the value at this same date must be 0.25. The goal of an easing function is to compute those intermediate values according to a predefined curve.

The contract of the easing functions

An easing function is a function whose signature is double (double) verifing the following contract:

The second criteria may need more explainations. For example, if the tweener's start value is v1 and its last value is v2. Then a return value of 0 means that the intermediate value set by the tweener must be v1, while a return value equal to 1 corresponds to an intermediate value equal to v2. In other words, a return value of x implies an intermediate value set by the tweener to v1 + x × (v2 - v1).

claw::tween::symmetric_easing

There are typically three ways to apply an easing effect: when reaching the final value (ease-in), when leaving the initial value (ease-out), or in both situations (ease-in-out).

One can observe that the ease-out can be computed from the ease-in value, and ease-in-out can be computed from ease-in and ease-out. The goal of the claw::tween::symmetric_easing class is to define the ease-out and ease-in-out functions given the ease-out-function.

This class is defined as:

template<double (*Function)(double)>
class symmetric_easing
{
public:
static double ease_in( double t );
static double ease_out( double t );
static double ease_in_out( double t );
};

Given a function double f(double) verifying the easing contract, then symmetric_easing<f> provides the following implementations:

Predefined easing functions

The following easing classes are implementations of the symmetric_easing class in the library.

Easing Class and Ease-In Equation Ease-In Ease-Out Ease-In-Out

claw::tween::easing_back

f(t) = t² × (2.70158 × t − 1.70158)

back-in.png
back-out.png
back-inout.png

claw::tween::easing_bounce

f(t) =

1 − 7.5625 × (1-t)² if 1 − t < 1 ÷ 2.75

or 1 − (7.5625 × (1 − t − 1.5 ÷ 2.75)² + 0.75) if 1 − t < 2 ÷ 2.75

or 1 − (7.5625 × (1 − t − 2.25 ÷ 2.75)² + 0.9375) if 1 − t < 2.5 ÷ 2.75

1 − (7.5625 × (1 − t − 2.625 / 2.75)² + 0.984375) otherwise

bounce-in.png
bounce-out.png
bounce-inout.png

claw::tween::easing_circ

f(t) = 1 − √(1 − t²)

circ-in.png
circ-out.png
circ-inout.png

claw::tween::easing_cubic

f(t) = t³

cubic-in.png
cubic-out.png
cubic-inout.png

claw::tween::easing_elastic

f(t) = −210 × (t − 1) × sin(((t − 1) − 0.3 ÷ 4) × 2 × π ÷ 4)

elastic-in.png
elastic-out.png
elastic-inout.png

claw::tween::easing_expo

f(t) =

0 if t = 0

210 × (t − 1)

expo-in.png
expo-out.png
expo-inout.png

claw::tween::easing_linear

f(t) = t

linear-in.png
linear-out.png
linear-inout.png

claw::tween::easing_none

f(t) = 0

none-in.png
none-out.png
none-inout.png

claw::tween::easing_quad

f(t) = t²

quad-in.png
quad-out.png
quad-inout.png

claw::tween::easing_quart

f(t) = t⁴

quart-in.png
quart-out.png
quart-inout.png

claw::tween::easing_quint

f(t) = t⁵

quint-in.png
quint-out.png
quint-inout.png

claw::tween::easing_sine

f(t) = 1 − cos(t × π ÷ 2)

sine-in.png
sine-out.png
sine-inout.png