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 }