Subject: Re: [boost] Interest in a tweener library
From: Julien Jorge (julien.jorge_at_[hidden])
Date: 2012-07-14 15:17:55
Le Fri, 13 Jul 2012 17:29:47 +0200,
Klaim - JoÃ«l Lamotte <mjklaim_at_[hidden]> a Ã©crit :
> Indeed, this library is very interesting, in particular in my domain
> (video games and other interactive and animated softwares).
> I used cpptweener before and wanted to update it to get more modern,
> but didn't found the time to in the end.
> I have some questions after having read the documentation page and
> some of the API doc:
> 1. Why does these types always accept double only? Is there a specific
> reason? I think this is greatly limiting the potential use of the
> library, because most use I've seen and done don't use double and
> sometime use custom value types. So I think it needs to be
> templatized to allow the user to provide the value type.
Since the goal of the tweeners is to compute intermediate values in a
range, I guessed that floating point values would be enough. Even
if the user wanted to interpolate on an other type, like integers or
my_custom_number_type, I supposed that he would convert the bound
values to double and cast the intermediate values from double to his
final type in the callbacks.
I never felt restricted by the double but I can think about using
> 2. If you templatize, then I would like to know the kind of concepts
> accepted by each ease function. This kind of library is very useful
> when you can use the same code to interpolate, for example, 2D and 3D
> vectors, instead of just a value. So it is important to says clearly
> which operations have to be implemented for the value type to work
> with the ease.
In the case where the tweener classes are templatized, I think that
the main requirement for the type is to support substraction and
The easing functions are agnostic with regard to the domain of the
interpolated values. They just take the fraction of the interpolation
duration represented in [0, 1] and tell "where" the tweened value is
in a fraction of the distance between the origin and target values.
So I think that the templatization would need two types: the user type
T used in tweener classes and an other type E used in the easing
functions. E should be a type that can represent values in [0, 1] and T
must accept a multiplication by E.
For the case of more complex types like 2D and 3D vectors, I think
they could be used for T but, as a user, that is not the way I would
use the library. Instead, I guess that I would write a helper function
that builds a tweener_group containing a single_tweener on each
dimension of the vector.
> 3. My understanding of the tweening concepts isn't complete, but
> maybe you can light me up: isn't there special maths that need to be
> supported if you want to "tween" an orientation value? (I mean any
> object that represent an orientation). Interpolation of
> orientation/rotation is as useful as other values to me.
When I use the tweeners in my games, I interpolate the position, the
orientation and rotations independently. In this situation, there is
no need for special maths.
If the tweened components were not independent, I suppose I would
apply the interpolation on some of them and adjust the others in the
callbacks. The math would not appear in the tweener library this way.
> 4. I see in tweener_group/sequence that the insert function take
> a const reference to a tweener. This is a bit obscure: does it imply
> a copy of the tweener or does it mean that I have to keep the
> lifetime of the tweener during the existence of the group/sequence?
> If it is a copy (I hope it is) then making the copy in the interface
> would be both useful and explicit. If not, using smart pointers would
> be necessary (to me at least).
Indeed, the group and sequence store a copy of the tweener. I did not
want to expose pointers on the interface, thus the tweener class
stores a pointer to a base_tweener and duplicates the instance when
copied. The goal of the const& is to avoid some copies but I do not
mind to remove it.
I think that the interface of tweener_group and tweener_sequence is
cleaner with methods arguments declared as tweener and I do not have to
handle the pointers in them.
One of the other reasons I used the tweener class in the interface was
that I did not want the user to be able to insert several times the same
pointer in a tweener_group or tweener_sequence.
For example, the following code would cause problems:
single_tweener* t = new single_tweener( /* â¦ */ );
for ( std::size_t i=0; i!=n; ++i )
s.insert( t );
Because when you will call s.update(), it will update every occurrence
of t at once. But this one is OK:
single_tweener t( /* â¦ */ );
for ( std::size_t i=0; i!=n; ++i )
s.insert( t );
Because a copy of t is inserted at each iteration.
> 5. Why is the clone function in base_tweener needed?
The clone function is used by the tweener class to copy the underlying
base_tweener when copied (See 4).
> 6. I suppose that there is no problem using lambdas to define update
The update function is declared as a boost::function. I guess that it
guarantees that it works with lambdas this but I never triedÂ :)
> 7. On performance side, shouldn't ease functions be inlined or
> template instead of implemented in the cpp file?
For the templates, see answer 2.
For the performance, I don't think they should be inlined. First, I
have no measure to tell that there is a performance bottleneck in the
use of the ease functions. Then, the functions are passed as an
argument to the constructor of single_tweener. I don't see how the
compiler could inline a function used this way.
> 8. The difference between single_tweener and tweener isn't clear to
> me. Can you explain?
The single_tweener computes the intermediate values between two bounds
according to an ease function. The tweener class does nothing but
carrying an instance of base_tweener and providing an interface to
update it. I use the tweener class to simplify the interface of
tweener_group and tweener_sequence and for the cases where I want a
tweener but do not care about its implementation.