Hi,

First of all, I would like to thank authors of Clutter for their excellent job! I have also studied the source code and I have to admit that it has many wise design decisions. Especially implicit animations are really charming,

However, I have two enhancement ideas related to implicit animations: I) implicit animations on allocation, and II) per property implicit animations. There is also a demonstration application that can be downloaded from:

http://temp.henrikhedberg.com/clutter/implicit_animations_on_allocation_20090421.tgz


I) Implicit Animations on Allocation
------------------------------------

When an actor receives a new bounding box from its container (clutter_actor_allocate), it just jumps and resizes itself without any animation. Current properties, such as x, y, width, and height cannot be used to animate transition after allocation, because setting those properties imply fixed layout (in opposite to container negotiated allocation).

The proposed solution is to introduce new properties related to allocation transitions:

- transition-move-x
- transition-move-y

An actor is moved from its current allocated (or fixed) location by transition-move-* units to left, right, up or down when painted into a stage. The default value is 0.0 meaning that the actor is rendered where it should normally be. When an allocation box changes, an actor applies a new allocation instantly but also sets these values to inverse the transition. Then an actor starts to animate these values towards the default value 0.0.

- transition-scale-x
- transition-scale-y

The width and height of an actor is scaled by transition-scale-* units. The default value is 1.0 meaning that the actor is rendered as is. The actual implementation of scaling varies with an actor. Container actors must not scale their children, but only scale their own (background) image if it exists. Visible actors (such as ClutterTexture) may simply do a scaling operation (in addition to default scaling defined by the scale-x and the scale-y properties) or use these properties as a factors when calculating the current visible width and height when painting (ClutterText, for example, may rewrap lines during the transition).

Example: An actor's current bounding box is (5, 10, 25, 30). After a size negotiation (clutter_actor_get_preferred_*) it gets a new bounding box (105, 210, 425, 630) from its container. This is what happens in the actor's allocate function:

1) The current allocation is saved (called as "old allocation" from now
   on).
2) The new allocation is stored by calling the allocate function in the
   parent class.
3) The difference between the old allocation and the new allocation is
   calculated. Thus, the actor knows that it has just jumped 100 units
   right, 200 units down, and its new width is 16 times the old width
   and the new height is 21 times the old height.
4) The actor sets transition-* properties as follows:

   - transition-move-x: -100
   - transition-move-y: -200
   - transition-scale-x: 1.0 / 16 = 0.0625
   - transition-scale-y: 1.0 / 21 = 0.0476

5) The actor starts to animate these properties back to their default
   values:

   clutter_actor_animate(actor, mode, duration,
                        "transition-move-x", 0.0,
                        "transition-move-y",0.0,
                        "transition-scale-x", 1.0,
                        "transition-scale-y", 1.0,
                        NULL);

Thus, when the actor paints itself the first time after the allocation the visible bounding box is (x1, y1, x2, y2) where

- x1 = 105 + (-100) = 5
- y1 = 210 + (-200) = 10
- x2 = 5 + (425 - 105) * 0.0625 = 25
- y2 = 10 + (630 - 210) * 0.0333 = 30

It seems that the actor has not move at all yet! After the animation has
completed, the visible bounding box is (x1, y1, x2, y2) where

- x1 = 105 + 0 = 105
- y1 = 210 + 0 = 210
- x2 = 105 + (425 - 105) * 1 = 425
- y2 = 210 + (630 - 210) * 1 = 630

The actor is now transitioned (smoothly) into its new location and size!

If an actor is not yet visible in a stage and thus does not have an old allocation, the transition-* properties are set as follows:

- transition-move-x: x1 + (x2 - x1) / 2
- transition-move-y: x1 + (y2 - y1) / 2
- transition-scale-x: 0
- transition-scale-y: 0

The demonstration implements also a new signal called allocation-changed. Subclasses or application developers may use the signal to overwrite the default behaviour of allocation transitions.


II) Per Property Implicit Animations
------------------------------------

The clutter_actor_animate* functions apply the very same animation mode and duration to all properties of an actor. It makes these convenience functions almost unusable in real situations.

Example: We want to move an actor using elastic animations and at the same time change its opacity from 0 to 255. We try to achive that by issuing the following statements:

clutter_actor_set_opacity(actor, 0);
clutter_actor_animate(actor, CLUTTER_EASE_IN_SINE, 500,
                      "opacity", 255, NULL);
clutter_actor_animate(actor, CLUTTER_EASE_OUT_ELASTIC, 2000,
                      "x", 100, "y", 200, NULL);

However, this is the equivalent of:

clutter_actor_set_opacity(actor, 0);
clutter_actor_animate(actor, CLUTTER_EASE_OUT_ELASTIC, 2000,
                      "opacity", 255, "x", 100, "y", 200, NULL);

Unfortunately this results flickering when also the opacity property has the "elastic" animation and the opacity value overflow (around 255). And naturally that is not what we wanted (wrote) originally!

Of course, we could use the plain animation API as follows (this is not tested...):

ClutterAnimation* animation1;
ClutterAnimation* animation2;
GValue value = { 0 };

clutter_actor_set_opacity(actor, 0);
animation1 = clutter_animation_new();
clutter_animation_set_object(animation1, actor);
clutter_animation_set_timeline(animation1, NULL);
clutter_animation_set_alpha(animation1, NULL);
clutter_animation_set_mode(animation1, CLUTTER_EASE_IN_SINE);
clutter_animation_set_duration(animation1, 500);
g_value_init(&value, G_TYPE_UCHAR)
g_value_set_uchar(&value, 255);
clutter_animation_bind(animation1, "opacity", &value);
g_value_unset(&value);
g_signal_connect(animation1, "completed", g_object_unref, NULL);
clutter_timeline_start(clutter_animation_get_timeline(animation1));

animation2 = clutter_animation_new();
clutter_animation_set_object(animation2, actor);
clutter_animation_set_timeline(animation2, NULL);
clutter_animation_set_alpha(animation2, NULL);
clutter_animation_set_mode(animation2, CLUTTER_EASE_OUT_ELASTIC);
clutter_animation_set_duration(animation2, 2000);
g_value_init(&value, G_TYPE_INT)
g_value_set_int(&value, 100);
clutter_animation_bind(animation2, "x", &value);
g_value_unset(&value);
g_value_init(&value, G_TYPE_INT)
g_value_set_uchar(&value, 200);
clutter_animation_bind(animation2, "y", &value);
g_value_unset(&value);
g_signal_connect(animation1, "completed", g_object_unref, NULL);
clutter_timeline_start(clutter_animation_get_timeline(animation2));

Not very practical, uh!

   The proposed solution is to combine only the properties of a single
clutter_actor_animate call into one animation object. Thus, each execution of clutter_actor_animate instantiate a new animation object for the actor. If the actor is already animating a property, it is unbinded from the existing animation and binded into a new animation. Thus, clutter_actor_animation overwrites the animation mode and the duration parameters for only single property and not for the whole actor.

   Example: Opacity and position properties are animated separately.

clutter_actor_set_opacity(actor, 0);
clutter_actor_animate(actor, CLUTTER_EASE_IN_SINE, 500,
                      "opacity", 255, NULL);
clutter_actor_animate(actor, CLUTTER_EASE_OUT_ELASTIC, 2000,
                      "x", 100, "y", 200, NULL);

Example: Calling the function on _a property_ of an actor that is already being animated will cause the current animation to change with the new final values, the new easing mode and the new duration - that is, this code:

clutter_actor_animate(actor, CLUTTER_LINEAR, 250,
                      "width", 100,
                      "height", 100,
                      NULL);
clutter_actor_animate(actor, CLUTTER_EASE_IN_CUBIC, 500,
                      "x", 100,
                      "y", 100,
                      "width", 200,
                      NULL);

is the equivalent of:

clutter_actor_animate(actor, CLUTTER_LINEAR, 250,
                      "height", 100,
                      NULL);
clutter_actor_animate(actor, CLUTTER_EASE_IN_CUBIC, 500
                      "x", 100,
                      "y", 100,
                      "width", 200,
                       NULL);

The demonstration applications implements per property implicit animations by storing references to animations into a hash table with a property name as a key (see the tangle_actor_animate function).

   * * *

If it is likely that these enhancements are to be integrated into Clutter core, I can provide a patch to the ClutterActor.

   BR,

   Henrik

--
   Henrik Hedberg  -  http://www.henrikhedberg.net/
--
To unsubscribe send a mail to [email protected]

Reply via email to