de.tu_berlin.cs.tfs.muvitorkit.animation
Class AnimatingCommand

java.lang.Object
  extended by org.eclipse.gef.commands.Command
      extended by de.tu_berlin.cs.tfs.muvitorkit.animation.AnimatingCommand

public class AnimatingCommand
extends Command

This is the main class users have to use in this GEF animation package. It implements a Command that can be given information about how some model elements (EMF EObjects) should be animated that are visualized as IFigures in GraphicalViewer s. For this, you specify stepwise absolute (Points) or relative (model elements) locations and optional size factors for the model elements to be animated as described below in detail.


Why not use GEF's animation mechanism?

The GEF already contains the classes Animation and Animator that can be used to animate figures. The main advantage of using this package is that you can specify more powerful animations more flexibly. More precisely, in addition to GEF you get this:


Stepwise animation definition - an example

To understand the principle of stepwise animation definition, we discuss the example time schedule matrix below. It represents the definition of an animation of three arbitrary model elements (element1-3). We have a column for each element to be animated and a row for each defined animation step, each with a positive integer value d1-3 representing the duration of this step in msecs (except for the first, which is the starting position row):
element1 element2 element3
start element1 Point(0,0) -/-
d1 element2 -/- element1
d2 element3 -/- element2
d3 element1 Point(100,100) -/-

First, you have to understand what the performed animation will look like. After that you shall see how you can define this animation by calling the appropriate methods.

In short, each table entry tells when (after the sum of durations till this step) the figure of the column's element has to be where (absolute or relative location). For example, element1 is supposed to start at its own position, to move in d1 msecs to the position of element2, after that to move in d2 msecs to the position of element3, and to return in d3 seconds back to its initial position.
Note that when specifying relative locations, they will be resolved to the absolute locations that the related figure has before the animation! Thus, element1's figure will move to the locations of the figures of element2 and element3 that they have before the animation, even if these will be animated themselves in this particular animation.
Of course, only those elements will be animated, for which a figure (being related to this element by some GraphicalEditPart) can be found. There is no need for element1-3 having figures in the same viewer at command-execution nor command-definition time, but only figures in open viewers that exist at execution-time will be animated. AnimatingCommand will not regard it as an error if no figure can be found for an element at all!

Let's continue with element2 to understand step interpolation: As you can see in the table, only an absolute starting position and a position after the third step has been specified. Generally, undefined steps between defined steps will be interpolated linearly. So, after d1 msecs element2's figure would reach the point (33,33), after d1+d2 msecs (66,66), and finally (100,100).
Note that an animation of a particular figure always starts at the first specified step and ends at the last specified step. Furthermore, this could lead to unexpected behavior if you specify a relative location by some other model element for which no figure location can be resolved! If no start or end location for the whole path of a model's figure can be resolved or interpolated, it will not be animated at all, but also no error message will be raised.

How element3's figure would be animated should be obvious by now. So let's see how we can specify this animation in some editor action. Suppose we have a container model element parent, which is the contents of the viewer that we expect to show, and we want to animate the figures for element1-3. This viewer must implement IGraphicalViewerProvider (have a look there for an example) to be found by AnimatingCommand. In addition we may have a CompoundCommand that manipulates our model before and possibly after the animation. The following shows the right code for this scenario (we will discuss another possibility for special purposes after that as well):

 AnimatingCommand aniComm = new AnimatingCommand();
 // initialize objects for animation
 aniComm.initializeAnimatedElement(element1, parent, null);
 aniComm.initializeAnimatedElement(element2, parent, null);
 // specify starting position
 aniComm.specifyStep(element1, element1);
 aniComm.specifyStep(element2, new Point(0, 0));
 // finish "starting step" definition, declare step with duration d1
 aniComm.nextStep(d1);
 // specify step 1
 aniComm.specifyStep(element1, element2);
 aniComm.initializeAnimatedElement(element3, parent, null);
 aniComm.specifyStep(element3, element1);
 // finish definition of step 1, declare step with duration d2
 aniComm.nextStep(d2);
 // specify step 2
 aniComm.specifyStep(element1, element3);
 aniComm.specifyStep(element3, element2);
 // finish definition of step 2, declare step with duration d3
 aniComm.nextStep(d3);
 // specify step 3
 aniComm.specifyStep(element1, element1);
 aniComm.specifyStep(element2, new Point(100, 100));
 // queue the animating command in the compound command
 compoundCommand.add(aniComm);
 
In this short code snippet, we first create a plain AnimatingCommand and declare element1 and element2 as to be animated in the viewer that has parent as contents. This must be done for an element before specifying any steps for it. We could have set some AnimationPathModifiers instead of the last null argument, but this is not mandatory.
Now, look at the matrix table above again: In the first step (which you have to imagine always as happening with the duration 0msec) we specify element1's and element2's figures to be at the location of the figure of element1 itself and at Point(0,0), respectively.
Note that we have not initialized element3 so far. We could have done so, though. It is not important in which step you initialize an element, as this does not change the animation at all, because AnimatingCommand will fill the specification for this element up to the current step with place holders. It is important that you must do this sometime before you call specifyStep(...) for this element!
With this we have completed the specification of the first step and can proceed to the next step by calling nextStep(d1), which states that the next step should take d1 msecs to complete.
The next row in the table says that in this step element1's figure should move to the original position of element2's figure, and that element3's figure should start its animation at the original position of element1's figure. For this we have to initialize element3 belatedly.
It should be clear by now, how the remaining lines correspond to the table entries. Note, that you do not have to finish the last step by calling nextStep a fourth time, however you would not know what duration to specify for this unused step.
By adding the AnimatingCommand to the compound command we are done!

Composite figures as targets

You should be aware that in fact not the figure of the target edit part is used to resolve the target location of an animation step, but the content pane of the target edit part is.
By default, an edit part returns its figure when asked for its content pane; so for simple figures or if you just want the center of the whole composite figure to be the target location, you do not have to worry about this.
But you can make use of this by overriding your GraphicalEditPart.getContentPane() to return a subfigure of your edit part's (composite) figure. So, you can let the center of the subfigure be the target location of each animation step that is specified with this edit part as target! If you have edit parts that are represented as composite figures

Animations that resize figures on their path

Optionally, you may specify a size factor for each step with specifyStep(Object, Object, double) to highlight some element. The size factor is absolute, a value of 1 will set the animated figure to the original size (as does the convenience method specifyStep(Object, Object)). A value of -1 will mark this element's size factor as to be interpolated for the current step, similar to the locations.

Modifying the path of an animated figure

If the figures' movement along a straight line bores you, you may choose from several alternative movements. For this you can call initializeAnimatedElement(Object, Object, AnimationPathModifier) with one of the predefined AnimationPathModifiers that are accessible by static getters. Have a look there for the different available modifiers.

Switch animations globally

Sometimes you might just want to switch off all animations caused by AnimatingCommands. For this you just have to call setPerformAnimation(boolean). This packages provides a sample ToggleAnimationAction, which can be provided as a button in the editor's tool bar that switches this flag.

Visualize the paths of animated elements (for debugging)

With setDebug(boolean) you can set a flag that forces the figures to mark their way as a red line, which will remain visible in the viewer after animation. So after the animation has been performed you can see and examine the paths of the figures.

Initializing at execution time rather at definition time

A remark about an slightly different way to define animations (i.e. by subclassing): AnimatingCommand features an empty method initialize() , which will be called in execute() and undo() before doing anything else. With this, you may implement own AnimatingCommands and include the element initializations and step definitions in this subclasses by putting them in the overridden initialize().
The main purpose of this approach is to perform initialization at execution time rather than command-creation time! For example, consider you have a list of elements you want to animate, e.g. all nodes being shown in a viewer (like in AnimatingCommand), and you initialize them dynamically in a loop. Now imagine you want to create a complex action with several model changes, including creation and deletion of nodes, and moving around all nodes. The only way to initialize elements that are newly created in the surrounding compound command, is to subclass AnimatingCommand and to put the initializing loop in initialize() so that all elements in the list are initialized just before the animation is executed when the compound command already created the new nodes, rather than when the compound command and the AnimatingCommand are being created and no model change has happened yet. Have a look at AnimatingDemoAction1 for an example.

Refreshing viewers between animation series

A word about refreshing the animated viewers: Depending on what you want to animate you may need to explicitly let the animated viewers be flushed before or after the animation. To achieve this you can experiment with setting the flagsflushBefore and flushAfter. This could be needed especially in consecutive animations, though, I can not figure out a general rule for all scenarios.

See Also:
IGraphicalViewerProvider, AnimationPathModifier, ToggleAnimationAction, AnimatedElement, Localizer, MultipleAnimation
Rating red

Field Summary
 boolean flushAfter
          Set this true to force viewers being flushed after this animation.
 boolean flushBefore
          Set this true to force viewers being flushed when preparing animated elements.
 
Constructor Summary
AnimatingCommand()
          Constructor, setting default label.
AnimatingCommand(String label)
          Constructor for a peculiar label.
 
Method Summary
 void animationDone()
          Called after an animation has been performed in execute() or undo().
 void dispose()
          If initialized is true all lists and hash maps are cleared.
 void execute()
          Performs the specified animation.
 Point getFinalLocation(Object modelOrFigure)
          This may be used to get the final bounds of a figure representing the model or for the animated figure itself.
 Point getInitialLocation(Object modelOrFigure)
          This may be used to get the initial bounds of a figure representing the model or for the animated figure itself.
 int getStepCounter()
           
 void initialize()
          This will be called in execute() or undo() before the animation is performed.
 void initializeAnimatedElement(Object modelOrEditPartorFigure, Object viewerOrTop)
          Before specifying any steps for an element to animate, you have to initialize it with this method.
 void initializeAnimatedElement(Object modelOrEditPartOrFigure, Object viewerOrTop, AnimationPathModifier pathModifier)
          Before specifying any steps for an element to animate, you have to initialize it with this method.
 boolean isAnimating()
           
static boolean isDebug()
          Getter for the debug flag.
static boolean isPerformAnimation()
          Getter for the performAnimation flag.
 void nextStep()
          Convenience method that calls nextStep(double) with a duration of 1.0.
 void nextStep(double relativeDuration)
          Completes the actual step definition by filling up the AnimatedElements that have not been specified for this step with a place holder step.
 void setCompleteDuration(int duration)
          You may use this to specify how long the animation should be in total in msec.
static void setDebug(boolean debug)
          Setting this flag to true may be used to display an animation in more detail for debugging.
static void setDefaultDuration(int msecs)
           
static void setPerformAnimation(boolean performAnimation)
          If this is set to false animations will be generally disabled until it is reset to true.
 void specifyStep(Object modelOrEditPart, Object locationObject)
          With this method you can specify the current animation step (the initial one or some subsequent one due to nextStep(double)) so that the relative size factor will be 1.
 void specifyStep(Object modelOrEditPart, Object locationObject, double sizeFactor)
          With this method you can specify the current animation step (the initial one or some subsequent one due to nextStep(double)) with a relative size factor.
 void undo()
          Performs the specified animation like execute(), but backwards.
 
Methods inherited from class org.eclipse.gef.commands.Command
canExecute, canUndo, chain, getDebugLabel, getLabel, redo, setDebugLabel, setLabel
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

flushBefore

public boolean flushBefore
Set this true to force viewers being flushed when preparing animated elements. You may have to experiment with this and flushAfter to get the right animation behavior, especially in consecutive animations.


flushAfter

public boolean flushAfter
Set this true to force viewers being flushed after this animation. You may have to experiment with this and flushAfter to get the right animation behavior, especially in consecutive animations.

Constructor Detail

AnimatingCommand

public AnimatingCommand()
Constructor, setting default label. The first step's duration is set to 0 to let it happen immediately.


AnimatingCommand

public AnimatingCommand(String label)
Constructor for a peculiar label. The first step's duration is set to 0 to let it happen immediately.

Parameters:
label -
Method Detail

execute

public void execute()
Performs the specified animation. Calls the optional initialize(), calculates the absolute durations, prepares the animation, performs the steps and finishes the animation. This may be overridden, but ensure to call super.execute()!

Overrides:
execute in class Command
See Also:
Command.execute(), calculateDurations(), prepareAnimation(boolean), performStep(int, int), animationDone()

undo

public void undo()
Performs the specified animation like execute(), but backwards. Calls the optional initialize(), calculates the absolute durations, prepares the animation, performs the steps in reversed order and finishes the animation. This may be overridden, but ensure to call super.undo()!

Overrides:
undo in class Command
See Also:
Command.undo(), calculateDurations(), prepareAnimation(boolean), performStep(int, int), animationDone()

dispose

public final void dispose()
If initialized is true all lists and hash maps are cleared.

Overrides:
dispose in class Command
See Also:
Command.dispose()

setPerformAnimation

public static final void setPerformAnimation(boolean performAnimation)
If this is set to false animations will be generally disabled until it is reset to true.

Parameters:
performAnimation -

setDebug

public static final void setDebug(boolean debug)
Setting this flag to true may be used to display an animation in more detail for debugging. The paths of the animated figures will be shown as well.

Parameters:
debug -

isDebug

public static final boolean isDebug()
Getter for the debug flag.

Returns:
the debug flag

isPerformAnimation

public static final boolean isPerformAnimation()
Getter for the performAnimation flag.

Returns:
the performAnimation

setCompleteDuration

public final void setCompleteDuration(int duration)
You may use this to specify how long the animation should be in total in msec. If it is set to -1 the DEFAULT_DURATION will be regarded as length for 1 relative time duration. Calling this method causes the absoluteDurations to be recalculated by calculateDurations().

Parameters:
duration - the time in msec for the whole animation

setDefaultDuration

public static void setDefaultDuration(int msecs)
Parameters:
msecs -

initialize

public void initialize()
This will be called in execute() or undo() before the animation is performed. May be overwritten to specify animation at execution time instead of command creation time.

This is useful e.g. if the models to be animated have not been created yet and should be retrieved from some list when this command is being executed. So, instead of calling initializeAnimatedElement(Object, Object, AnimationPathModifier), nextStep(double) and specifyStep(Object, Object) directly on this command, this could be done here when the models are accessible.


initializeAnimatedElement

public final void initializeAnimatedElement(Object modelOrEditPartOrFigure,
                                            Object viewerOrTop,
                                            AnimationPathModifier pathModifier)
Before specifying any steps for an element to animate, you have to initialize it with this method. If you already have specified some steps before, the AnimatedElement for the passed model will be filled up with place-holder steps up to the current step.

Parameters:
modelOrEditPartOrFigure - can be an GraphicalEditPart or EObject determining the model whose figure should be animated.v Alternatively, you may pass a specific IFigure to be animated.
viewerOrTop - can be an EObject or EditPartViewer determining the viewer holding a figure of the model
pathModifier - optional AnimationPathModifier

initializeAnimatedElement

public final void initializeAnimatedElement(Object modelOrEditPartorFigure,
                                            Object viewerOrTop)
Before specifying any steps for an element to animate, you have to initialize it with this method. If you already have specified some steps before, the AnimatedElement for the passed model will be filled up with place-holder steps up to the current step. Convenience method for standard paths.

Parameters:
modelOrEditPartorFigure - can be an GraphicalEditPart or EObject determining the model whose figure should be animated. Alternatively, you may pass a specific IFigure to be animated.
viewerOrTop - can be an EObject or EditPartViewer determining the viewer holding a figure of the model

nextStep

public final void nextStep()
Convenience method that calls nextStep(double) with a duration of 1.0.


nextStep

public final void nextStep(double relativeDuration)
Completes the actual step definition by filling up the AnimatedElements that have not been specified for this step with a place holder step. The next step is associated with the passed relative duration length.

Parameters:
relativeDuration - the relative duration length of the next step

specifyStep

public final void specifyStep(Object modelOrEditPart,
                              Object locationObject,
                              double sizeFactor)
With this method you can specify the current animation step (the initial one or some subsequent one due to nextStep(double)) with a relative size factor.

A valid argument for the location object has to be an EObject, GraphicalEditPart, Point, or null.

Parameters:
modelOrEditPart - can be an GraphicalEditPart or EObject determining the model whose figure should be animated.
locationObject - If null, the location will be interpolated
sizeFactor - If -1, the size will be interpolated

specifyStep

public final void specifyStep(Object modelOrEditPart,
                              Object locationObject)
With this method you can specify the current animation step (the initial one or some subsequent one due to nextStep(double)) so that the relative size factor will be 1. If the size is meant to be interpolated you should call specifyStep(Object, Object, double) with -1 as size factor!

A valid argument for the location object has to be an EObject, GraphicalEditPart, Point, or null.

Parameters:
modelOrEditPart - can be an GraphicalEditPart or EObject determining the model whose figure should be animated.
locationObject - If null, location and size will be interpolated

getStepCounter

public final int getStepCounter()
Returns:
the number of steps that have been defined so far

getFinalLocation

public final Point getFinalLocation(Object modelOrFigure)
This may be used to get the final bounds of a figure representing the model or for the animated figure itself. So the result of an animation can be preserved by setting these values via command to the model or figure.

Parameters:
modelOrFigure - The model/figure to get the last bounds after this animation for.
Returns:
The actual last bounds in the Localizer path of the AnimatedElement for modelOrFigure. May be null if animation (and thus resolving/interpolation) has not been performed properly.

isAnimating

public boolean isAnimating()

getInitialLocation

public final Point getInitialLocation(Object modelOrFigure)
This may be used to get the initial bounds of a figure representing the model or for the animated figure itself. So the result of an animation can be preserved by setting these values via command to the model or figure and be reset with undo.

Parameters:
modelOrFigure - The model/figure to get the first bounds after this animation for.
Returns:
The cached first bounds in the Localizer path of the AnimatedElement for model. May be null if animation (and thus resolving/interpolation) has not been performed properly.

animationDone

public final void animationDone()
Called after an animation has been performed in execute() or undo(). Calls AnimatedElement.animationDone() on all AnimatedElements and clears the AnimatedElement#resetViewers.