/* This file is part of the source code for 3D-XplorMath-J, Version 1.0 (January 2008).
* Copyright (c) 2008 The 3D-XplorMath Consortium (http://3d-xplormath.org).
* This source code is released under a BSD License, which allows redistribution
* in source and binary form, with or without modification, provided copyright
* and license information are included, and with no warranty or guarantee of
* any kind. For details, see http://3d-xplormath.org/j/source/BSDLicense.txt
*/
package vmm.core;
import java.util.ArrayList;
/**
* A BasicAnimator can animate one or more Animateable objects. It is a fairly
* simple extension of TimerAnimation that keeps a list of Animateable items
* and in each frame calls each item's {@link Animateable#setFractionComplete(double)} method.
* In fact, all Animateable objects in the vmm core
* are Animateable Parameters such as {@link RealParamAnimateable}, and
* BasicAnimators are used only for Morphing animations.
*
It is also possible to add a {@link RealParamAnimateable} or
* {@link ComplexParamAnimateable} with specified start and end values, which
* will be used instead of the start and end values stored in the parameter itself.
*/
public class BasicAnimator extends TimerAnimation {
private ArrayList animatedItems = new ArrayList();
private ArrayList customLimitAnimatedParameters = null;
private ArrayList initialParameterValues; // used for saving parameter values at start of animation
private boolean restoreParameterValues = true;
/**
* Create a BasicAnimator with no items, with 100 frames and 50 millseconds per frame.
* Animated items can be added with the addAnimatedItem method.
*/
public BasicAnimator() {
this((Animateable[])null,100,50);
}
/**
* Create a BasicAnimator with no items, with a specified number of frames, and with 50 millisconds.
* Animated items can be added with the addAnimatedItem method.
*/
public BasicAnimator(int frames) {
this((Animateable[])null,frames,50);
}
/**
* Create a BasicAnimator with one Animateable item, with 100 frames and 50 millseconds per frame.
* More animated items could be added with the addAnimatedItem method.
* @param item An item to be animated by this animation. Can be null, in which case
* the animation has no items initially.
*/
public BasicAnimator(Animateable item) {
this(new Animateable[] { item }, 100, 50);
}
/**
* Create a BasicAnimator with one item, with a specified number of frames and delay per frame.
* More animated items could be added with the addAnimatedItem method.
* @param item An item to be animated by this animation. Can be null, in which case
* the animation has no items initially.
* @param frames The number of frames in the animation
* @param millisecondsPerFrame The desired number of milliseconds for each frame
*/
public BasicAnimator(Animateable item, int frames, int millisecondsPerFrame) {
this(new Animateable[] { item }, frames, millisecondsPerFrame);
}
/**
* Create a BasicAnimator with several animated itemss, and with a specified number of frames and delay per frame.
* @param items An array of items to be animated by this animation. Can be null, in which case
* the animation has no items initially.
* @param frames The number of frames in the animation.
* @param millisecondsPerFrame The desired number of milliseconds for each frame
*/
public BasicAnimator(Animateable[] items, int frames, int millisecondsPerFrame) {
super(frames,millisecondsPerFrame);
if (items != null)
for (int i = 0; i < items.length; i++)
animatedItems.add(items[i]);
}
/**
* Get the setting of the restoreParameterValues property.
* @see #setRestoreParameterValues(boolean)
*/
public boolean getRestoreParameterValues() {
return restoreParameterValues;
}
/**
* Set the restoreParameterValues property. If this property is true, then this BasicAnimator will save
* the value of any Parameter among its Animateable objects at the beginning of the animation, and it
* will restore those values when the animation ends. The default value is true. The value must be set
* before the animation is started for it to have any effect.
*/
public void setRestoreParameterValues(boolean restoreParameterValues) {
this.restoreParameterValues = restoreParameterValues;
}
/**
* Add an Animateable item to the list of items that change during this animation.
* @param item The item to be added. If null, nothing is done.
*/
synchronized public void addAnimatedItem(Animateable item) {
if (item != null && !(animatedItems.contains(item)))
animatedItems.add(item);
}
/**
* Adds an IntegerParam to this animator, with specified value for
* the animation. This value is assigned to the paramter at the beginning of the
* animation and is used throughout the animation, the old value is restored at the
* end of the animation, provided that the restoreParameterValues property of this
* animation is true.
* @param item the parameter whose value is to be fixed at a specified value throughout
* the animation. If this is null, nothing is done and no error occurs.
*/
synchronized public void addWithCustomValue(IntegerParam item, int value) {
if (item == null)
return;
if (customLimitAnimatedParameters == null)
customLimitAnimatedParameters = new ArrayList();
customLimitAnimatedParameters.add(new Object[] { item, value });
}
/**
* Adds a RealParam to this animator, with specified value for
* the animation. This value is assigned to the paramter at the beginning of the
* animation and is used throughout the animation, the old value is restored at the
* end of the animation, provided that the restoreParameterValues property of this
* animation is true.
* @param item the parameter whose value is to be fixed at a specified value throughout
* the animation. If this is null, nothing is done and no error occurs.
*/
synchronized public void addWithCustomValue(RealParam item, double value) {
if (item == null)
return;
if (customLimitAnimatedParameters == null)
customLimitAnimatedParameters = new ArrayList();
customLimitAnimatedParameters.add(new Object[] { item, value });
}
/**
* Adds a ComplexParam to this animator, with specified value for
* the animation. This value is assigned to the paramter at the beginning of the
* animation and is used throughout the animation, the old value is restored at the
* end of the animation, provided that the restoreParameterValues property of this
* animation is true.
* @param item the parameter whose value is to be fixed at a specified value throughout
* the animation. If this is null, nothing is done and no error occurs.
* @param value the value for the parameter. If this is null, 0 is used.
*/
synchronized public void addWithCustomValue(ComplexParam item, Complex value) {
if (item == null)
return;
if (customLimitAnimatedParameters == null)
customLimitAnimatedParameters = new ArrayList();
if (value == null)
value = new Complex();
customLimitAnimatedParameters.add(new Object[] { item, value });
}
/**
* Adds a RealParamAniamteable to this animator, with specified start and end values for
* the animation. These values are used instead of the start and end values stored in
* the parameter itself, and those values are not modified by the animation. (This is meant to
* allow, for example, the creation of "custom morphs" in addition to the standard Morph
* and Cyclic Morph, which always use the start and end values specified by the user and
* stored in the parameters.)
* @param item the parameter to be animated. If this is null, nothing is done and no error occurs.
*/
synchronized public void addWithCustomLimits(RealParamAnimateable item, double start, double end) {
if (item == null)
return;
if (customLimitAnimatedParameters == null)
customLimitAnimatedParameters = new ArrayList();
customLimitAnimatedParameters.add(new Object[] { item, start, end });
}
/**
* Adds a ComplexParamAniamteable to this animator, with specified start and end values for
* the animation. These values are used instead of the start and end values stored in
* the parameter itself, and those values are not modified by the animation. If the
* start or end value is null, 0 is used.
* @param item the parameter to be animated. If this is null, nothing is done and no error occurs.
*/
synchronized public void addWithCustomLimits(ComplexParamAnimateable item, Complex start, Complex end) {
if (item == null)
return;
if (start == null)
start = new Complex();
if (end == null)
end = new Complex();
if (customLimitAnimatedParameters == null)
customLimitAnimatedParameters = new ArrayList();
customLimitAnimatedParameters.add(new Object[] { item, start, end });
}
final synchronized public void start() {
if (isRunning())
cancel();
initialParameterValues = null;
if (restoreParameterValues) {
initialParameterValues = new ArrayList();
for (Animateable item : animatedItems) {
if (item instanceof Parameter) {
Parameter p = (Parameter)item;
initialParameterValues.add( new Object[] { p, p.getValueObject() } );
}
}
if (customLimitAnimatedParameters != null) {
for (Object[] item : customLimitAnimatedParameters) {
Parameter p = (Parameter)item[0];
initialParameterValues.add( new Object[] { p, p.getValueObject() } );
}
}
}
super.start();
}
final synchronized public void cancel() {
if (initialParameterValues != null) {
for (Object[] info : initialParameterValues) {
Parameter p = (Parameter)info[0];
p.setValueObject(info[1]);
}
initialParameterValues = null;
}
super.cancel();
}
/**
* Creates one frame of the animation by calling the {@link vmm.core.Animateable#setFractionComplete(double)}
* method of each object in this BasicAnimator's list of Animateable object.
*/
protected void drawFrame() {
if (animatedItems.size() == 0 && customLimitAnimatedParameters == null)
cancel();
double fractionComplete = (double)frameNumber / getFrames();
for (Animateable item : animatedItems)
item.setFractionComplete(fractionComplete);
if (customLimitAnimatedParameters != null) {
for (Object[] item : customLimitAnimatedParameters) {
if (item.length == 2) {
if (item[0] instanceof IntegerParam)
((IntegerParam)item[0]).setValue((Integer)item[1]);
else if (item[0] instanceof RealParam)
((RealParam)item[0]).setValue((Double)item[1]);
else
((ComplexParam)item[0]).setValue((Complex)item[1]);
}
else if (item[0] instanceof RealParamAnimateable) {
RealParamAnimateable param = (RealParamAnimateable)item[0];
double start = (Double)item[1];
double end = (Double)item[2];
double val = start + fractionComplete*(end - start);
param.setValue(val);
}
else {
ComplexParamAnimateable param = (ComplexParamAnimateable)item[0];
Complex start = (Complex)item[1];
Complex end = (Complex)item[2];
double valRe = start.re + fractionComplete*(end.re - start.re);
double valIm = start.im + fractionComplete*(end.re - start.re);
param.setValue(new Complex(valRe,valIm));
}
}
}
if (display != null && display.getView() != null)
display.getView().forceRedraw(); // This is necessary becasue if none of the parameter values actually change, then the frame will not be created.
}
/**
* Returns null to indicate that the default text ("Animation Running" or "Animation Paused" in the
* English version) should be shown in the display's status bar. This can be overridden in
* a subclass to show a different status message.
* @param running tells whether the animation is currently running.
*/
public String getStatusText(boolean running) {
return null;
}
}