/**
* VERSION: 0.7
* DATE: 2011-11-29
* AS3
* UPDATES AND DOCS AT: http://www.greensock.com/roughease/
**/
package com.greensock.easing {
/**
* Most easing equations give a smooth, gradual transition between the start and end values, but RoughEase provides
* an easy way to get a rough, jagged effect instead. You can define an ease that it will use as a template (like a
* general guide - Linear.easeNone is the default) and then it will randomly plot points that wander from that template.
* The strength parameter controls how far from the template ease the points are allowed to go (a small number like
* 0.1 keeps it very close to the template ease whereas a larger number like 2 creates much larger jumps). You can
* also control the number of points in the ease, making it jerk more or less frequently. And lastly, you can associate
* a name with each RoughEase instance and retrieve it later like RoughEase.byName("myEaseName"). Since creating
* the initial RoughEase is the most processor-intensive part, it's a good idea to reuse instances if/when you can.
*
* EXAMPLE CODE
* import com.greensock.TweenLite;
* import com.greensock.easing.RoughEase;
*
* TweenLite.from(mc, 3, {alpha:0, ease:RoughEase.create(1, 15)});
*
* //or create an instance directly
* var rough:RoughEase = new RoughEase(1.5, 30, true, Strong.easeOut, "none", true, "superRoughEase");
* TweenLite.to(mc, 3, {y:300, ease:rough.ease});
*
* //and later, you can find the ease by name like:
* TweenLite.to(mc, 3, {y:300, ease:RoughEase.byName("superRoughEase")});
*
*
* Copyright 2011, GreenSock. All rights reserved. This work is subject to the terms in http://www.greensock.com/terms_of_use.html or for corporate Club GreenSock members, the software agreement that was issued with the corporate membership.
*
* @author Jack Doyle, jack@greensock.com
*/
public class RoughEase {
/** @private **/
private static var _lookup:Object = {}; //keeps track of all named instances so we can find them in byName().
/** @private **/
private static var _count:uint = 0;
/** @private **/
private var _name:String;
/** @private **/
private var _first:EasePoint;
/** @private **/
private var _last:EasePoint;
/**
* Constructor
*
* @param strength amount of variance from the templateEase (Linear.easeNone by default) that each random point can be placed. A low number like 0.1 will hug very closely to the templateEase whereas a larger number like 2 will allow the values to wander further away from the templateEase.
* @param points quantity of random points to plot in the ease. A larger number will cause more (and quicker) flickering.
* @param restrictMaxAndMin If true, the ease will prevent random points from exceeding the end value or dropping below the starting value. For example, if you're tweening the x property from 0 to 100, the RoughEase would force all random points to stay between 0 and 100 if restrictMaxAndMin is true, but if it is false, a x could potentially jump above 100 or below 0 at some point during the tween (it would always end at 100 though).
* @param templateEase an easing equation that should be used as a template or guide. Then random points are plotted at a certain distance away from the templateEase (based on the strength parameter). The default is Linear.easeNone.
* @param taper to make the strength of the roughness taper towards the end or beginning or both, use "out", "in", or "both" respectively here (default is "none").
* @param randomize to randomize the placement of the points, set randomize to true (otherwise the points will zig-zag evenly across the ease)
* @param name a name to associate with the ease so that you can use RoughEase.byName() to look it up later. Of course you should always make sure you use a unique name for each ease (if you leave it blank, a name will automatically be generated).
*/
public function RoughEase(strength:Number=1, points:uint=20, restrictMaxAndMin:Boolean=false, templateEase:Function=null, taper:String="none", randomize:Boolean=true, name:String="") {
if (name == "") {
_name = "roughEase" + (_count++);
} else {
_name = name;
_lookup[_name] = this; //only store it if a name is defined. This way, unnamed eases can be garbage collected without needing a dispose() call.
}
if (taper == "" || taper == null) {
taper = "none";
}
var a:Array = [];
var cnt:int = 0;
strength *= 0.4;
var x:Number, y:Number, bump:Number, invX:Number, obj:Object;
var i:int = points;
while (--i > -1) {
x = randomize ? Math.random() : (1 / points) * i;
y = (templateEase != null) ? templateEase(x, 0, 1, 1) : x;
if (taper == "none") {
bump = strength;
} else if (taper == "out") {
invX = 1 - x;
bump = invX * invX * strength;
} else if (taper == "in") {
bump = x * x * strength;
} else if (x < 0.5) { //"both" (start)
invX = x * 2;
bump = invX * invX * 0.5 * strength;
} else { //"both" (end)
invX = (1 - x) * 2;
bump = invX * invX * 0.5 * strength;
}
if (randomize) {
y += (Math.random() * bump) - (bump * 0.5);
} else if (i % 2) {
y += bump * 0.5;
} else {
y -= bump * 0.5;
}
if (restrictMaxAndMin) {
if (y > 1) {
y = 1;
} else if (y < 0) {
y = 0;
}
}
a[cnt++] = {x:x, y:y};
}
a.sortOn("x", Array.NUMERIC);
_first = _last = new EasePoint(1, 1, null);
i = points;
while (--i > -1) {
obj = a[i];
_first = new EasePoint(obj.x, obj.y, _first);
}
_first = new EasePoint(0, 0, (_first.time != 0) ? _first : _first.next);
}
/**
* This static function provides a quick way to create a RoughEase and immediately reference its ease function
* in a tween, like:
*
* TweenLite.from(mc, 2, {alpha:0, ease:RoughEase.create(1.5, 15)});
*
*
* @param strength amount of variance from the templateEase (Linear.easeNone by default) that each random point can be placed. A low number like 0.1 will hug very closely to the templateEase whereas a larger number like 2 will allow the values to wander further away from the templateEase.
* @param points quantity of random points to plot in the ease. A larger number will cause more (and quicker) flickering.
* @param restrictMaxAndMin If true, the ease will prevent random points from exceeding the end value or dropping below the starting value. For example, if you're tweening the x property from 0 to 100, the RoughEase would force all random points to stay between 0 and 100 if restrictMaxAndMin is true, but if it is false, a x could potentially jump above 100 or below 0 at some point during the tween (it would always end at 100 though).
* @param templateEase an easing equation that should be used as a template or guide. Then random points are plotted at a certain distance away from the templateEase (based on the strength parameter). The default is Linear.easeNone.
* @param taper to make the strength of the roughness taper towards the end or beginning or both, use "out", "in", or "both" respectively here (default is "none").
* @param randomize to randomize the placement of the points, set randomize to true (otherwise the points will zig-zag evenly across the ease)
* @param name a name to associate with the ease so that you can use RoughEase.byName() to look it up later. Of course you should always make sure you use a unique name for each ease (if you leave it blank, a name will automatically be generated).
* @return easing function
*/
public static function create(strength:Number=1, points:uint=20, restrictMaxAndMin:Boolean=false, templateEase:Function=null, taper:String="none", randomize:Boolean=true, name:String=""):Function {
var re:RoughEase = new RoughEase(strength, points, restrictMaxAndMin, templateEase, taper, randomize, name);
return re.ease;
}
/**
* Provides a quick way to look up a RoughEase by its name.
*
* @param name the name of the RoughEase
* @return easing function from the RoughEase associated with the name
*/
public static function byName(name:String):Function {
return _lookup[name].ease;
}
/**
* Easing function that interpolates the numbers
*
* @param t time
* @param b start
* @param c change
* @param d duration
* @return Result of the ease
*/
public function ease(t:Number, b:Number, c:Number, d:Number):Number {
var time:Number = t / d;
var p:EasePoint;
if (time < 0.5) {
p = _first;
while (p.time <= time) {
p = p.next;
}
p = p.prev;
} else {
p = _last;
while (p.time >= time) {
p = p.prev;
}
}
return b + (p.value + ((time - p.time) / p.gap) * p.change) * c;
}
/** Disposes the RoughEase so that it is no longer stored for easy lookups by name with byName()
, releasing it for garbage collection. **/
public function dispose():void {
delete _lookup[_name];
}
/** name of the RoughEase instance **/
public function get name():String {
return _name;
}
public function set name(value:String):void {
delete _lookup[_name];
_name = value;
_lookup[_name] = this;
}
}
}
internal class EasePoint {
public var time:Number;
public var gap:Number;
public var value:Number;
public var change:Number;
public var next:EasePoint;
public var prev:EasePoint;
public function EasePoint(time:Number, value:Number, next:EasePoint) {
this.time = time;
this.value = value;
if (next) {
this.next = next;
next.prev = this;
this.change = next.value - value;
this.gap = next.time - time;
}
}
}