package flare.animate {
import flare.util.Maths;
import flare.util.Vectors;
/**
* Transition that runs sub-transitions in sequential order while also
* invoking a function before each sub-transition is run. Each function
* must take a Transition instance (often a Transitioner) as input.
* Function sequences can only be played forwards; any attempt to play
* one in reverse will result in an error.
*
*
Function sequences are useful for ensuring a particular function
* is run before a sub-transition begins. For example, the function may
* populate the values of a Transitioner on the fly, or may be used to
* control other variables or side-effects that affect the subsequent
* sub-transition(s).
*/
public class FunctionSequence extends Sequence
{
/** @private */
protected var _funcs:Vector. = new Vector.();
/** @private */
protected var _offsetStart:Boolean = true;
/** Flag indicating if extra time should be added to the transition to
* offset the running time of invoked functions. True by default. */
public function get offsetStartTime():Boolean { return _offsetStart; }
public function set offsetStartTime(b:Boolean):void { _offsetStart = b; }
// --------------------------------------------------------------------
/**
* Creates a new FunctionSequence transition.
*/
public function FunctionSequence()
{
super();
}
/**
* Adds a function call and corresponding transition to this sequence.
* As the sequence plays, functions will be called at the beginning of
* their subsequence, with the provided transition passed as the
* function's sole argument.
* @param f a function to call at the beginning of a sub-sequence
* @param t the transition to run after the function call. This
* transition will be passed in as an input to the function. This
* value should be either a Transition
instance or
* a number indicating the duration for a new
* Transitioner
.
*/
public function push(f:Function, t:*):void
{
var tr:Transition = t is Transition ? Transition(t)
: Transitioner.instance(t);
super.add(tr);
_funcs.push(f);
}
/**
* Removes a sub-transition function from this sequence. The
* corresponding transition instance will also be removed.
* @param t the transition to remove
* @return true if the transition was found and removed, false
* otherwise
*/
public function removeFunction(f:Function):Boolean
{
if (running) throw new Error("Transition is running!");
var idx:int = Vectors.remove(_funcs, f);
var rem:Boolean = idx > 0;
if (rem) {
_trans.splice(idx, 1);
_dirty = true;
}
return rem;
}
/** @inheritDoc */
public override function add(t:Transition):void
{
super.add(t);
_funcs.push(null);
}
/**
* Removes a sub-transition from this sequence. Any corresponding
* function will also be removed.
* @param t the transition to remove
* @return true if the transition was found and removed, false
* otherwise
*/
public override function remove(t:Transition):Boolean
{
if (running) throw new Error("Transition is running!");
var idx:int = Vectors.remove(_trans, t);
var rem:Boolean = idx > 0;
if (rem) {
_funcs.splice(idx, 1);
_dirty = true;
}
return rem;
}
/** @inheritDoc */
public override function dispose():void {
super.dispose();
_funcs.length = 0;
}
/**
* Plays this function sequence. Function sequences can not be played
* in reverse.
* @param reverse If true, an error will be thrown and the sequence
* will not play.
*/
public override function play(reverse:Boolean=false):void {
if (reverse) throw new Error(
"Function sequences can't be played in reverse.");
super.play(false);
}
private function invoke(idx:int, t:Transition):void {
var f:Function = _funcs[idx] as Function;
if (f != null) {
if (_offsetStart) {
var d:Number = new Date().time;
f(t);
d = new Date().time - d;
_start += d;
} else {
f(t);
}
}
}
// --------------------------------------------------------------------
/**
* Sets up each sub-transition.
*/
protected override function setup():void
{
}
/**
* Starts this sequence transition, starting the first sub-transition
* to be played.
*/
protected override function start():void
{
if (_trans.length > 0) {
var t:Transition = _trans[_idx] as Transition;
invoke(_idx, t); t.doSetup(); t.doStart(false);
}
}
/**
* Steps this sequence transition, ensuring that any sub-transitions
* between the previous and current progress fraction are properly
* invoked.
* @param ef the current progress fraction.
*/
internal override function step(ef:Number):void
{
// find the right sub-transition
var t:Transition, f0:Number, f1:Number, i:int, inc:int;
f0 = _fracs[_idx] as Number; f1 = _fracs[_idx+1] as Number; inc = (ef<=f0 ? -1 : 1);
for (i = _idx; i>=0 && i<_trans.length; i+=inc) {
// get transition and progress fractions
t = _trans[i] as Transition; f0 = _fracs[i] as Number; f1 = _fracs[i+1] as Number;
// hand-off to new transition
if (i != _idx) {
invoke(i, t); t.doSetup(); t.doStart(false);
}
if ((inc<0 && ef >= f0) || (inc>0 && ef <= f1)) break;
t.doStep(inc<0 ? 0 : 1);
t.doEnd();
}
_idx = i; // set the transition index
if (_idx >= 0 && _idx < _trans.length) {
// run transition with mapped fraction
t.doStep(Maths.invLinearInterp(ef, f0, f1));
}
}
} // end of class FunctionSequence
}