In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

forked from: Sewing

/**
 * Copyright RubenElzerman ( http://wonderfl.net/user/RubenElzerman )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/yZsl
 */

// forked from Saqoosha's Sewing
package {
	
    import caurina.transitions.Equations;
    import caurina.transitions.Tweener;
    import caurina.transitions.properties.CurveModifiers;
    
    import flash.display.Graphics;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageQuality;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.geom.Point;
    import flash.net.URLLoader;
    import flash.net.URLLoaderDataFormat;
    import flash.net.URLRequest;
    import flash.utils.setTimeout;
    
    [SWF(width=465, height=465, backgroundColor=0xffffff, frameRate=60)]
    
	public class Sewing extends Sprite {

		private var _loader:URLLoader;
		private var _canvas:Sprite;
		private var _paths:Array;
	
		public function Sewing() {
			Wonderfl.capture_delay(8);
			
			this.stage.quality = StageQuality.BEST;
			this.stage.align = StageAlign.TOP_LEFT;
			this.stage.scaleMode = StageScaleMode.NO_SCALE;
			
			CurveModifiers.init();
			
			this._canvas = this.addChild(new Sprite()) as Sprite;
			
			this._loader = new URLLoader();
			this._loader.dataFormat = URLLoaderDataFormat.TEXT;
			this._loader.addEventListener(Event.COMPLETE, this._handleLoaded);
			this._loader.load(new URLRequest('http://saqoosha.net/lab/wonderfl/saqoosha.svg'));
		}
		
		private function _handleLoaded(e:Event):void {
			var svg:XML = new XML(this._loader.data);
			this._paths = [];
			var path:SVGPath;
			var delay:Number = 2.0;
			var cx:Number = parseInt(svg.@width) / 2;
			var cy:Number = parseInt(svg.@height) / 2;
			this._canvas.x = this.stage.stageWidth / 2 - cx;
			this._canvas.y = this.stage.stageHeight / 2 - cy;
			for each (var pathNode:XML in svg..*::path) {
				path = new SVGPath(pathNode);
				this._paths.push(path);
				var i:Number = 0;
				var di:Number = Math.PI * 2 / path.points.length;
				var px:Number = cx;
				var py:Number = this.stage.stageHeight - this._canvas.y;
				for each (var pt:Point in path.points) {
					Tweener.addTween(pt, {
						x: pt.x,
						y: pt.y,
						_bezier:[
							{ x:cx + Math.sin(delay * 1.3) * 300 + Math.sin(delay * 10.3) * 80, y:50 },
							{ x:100 - Math.sin(delay * 7.2) * 80, y:50 }
						],
						time: 1.0,
						delay: delay,
						transition: Equations.easeNone
					});
					pt.x = px;
					pt.y = py;
					delay += 0.004;
					i += di;
				}
			}
			this.addEventListener(Event.ENTER_FRAME, this._drawPaths);
			setTimeout(function ():void {
				removeEventListener(Event.ENTER_FRAME, _drawPaths);
			}, (delay + 1) * 1000);
		}
		
		private function _drawPaths(e:Event):void {
			this._canvas.graphics.clear();
			for each (var path:SVGPath in this._paths) {
				path.draw(this._canvas.graphics);
			}
		}
	}
}















import flash.display.Graphics;
import flash.geom.Point;
import flash.geom.Rectangle;


/**
* @class PathToArray
* @author Helen Triolo (with contributions from many people)
* @version 1.01 
* @description  Takes as input an SVG Path node (eg, from Illustrator 10, SVG Factory, etc, but must
*			not contain any CRLF characters), and an empty array. 
*			Parses the path node to make an array of drawing commands, which include cubic bezier 
*			draw commands.  Converts the cubic beziers to an array of equivalent quad beziers, 
*			using Robert Penner's code to convert with accuracy within 1 pixel.  
*			Drawing commands are produced in the format originally devised by Peter Hall
*           in his ASVDrawing class.  These are the possible elements in array dCmds (and the 
*			corresponding Flash drawing API commands to apply them):
*				['M',[x,y]]					moveTo(x,y)
*				['L',[x,y]]					lineTo(x,y)
*				['C',[cx,cy,ax,ay]]			moveTo(cx,cy,ax,ay)
*				['S',[width,color,alpha]]	lineStyle(widtb,color,alpha)
*				['F',[color,alpha]]			beginFill(color,alpha)
*				['EF']						endFill()
* History:
*	v1.00		2005/11/13	Original release
*	v1.01		2006/05/06	Stroke corrected to stroke, line 250 (thanks Gábor Szabó)
*
* @param svgnode (XMLNode) Path node from SVG file
* @param dCmds (Array) Empty array to write commands to
*/

class SVGPath {

	private var _commands:Array;
	public function get commands():Array { return this._commands }
	
	private var _points:Array;
	public function get points():Array { return this._points }
	private function addPoint(... points:Array):void {
		for each (var pt:Point in points) {
			this._addPoint(pt);
		}
	}
	private function _addPoint(pt:Point):void {
		if (pt.x < this._bounds.left) {
			this._bounds.left = pt.x;
		} else if (this._bounds.right < pt.x) {
			this._bounds.right = pt.x;
		}
		if (pt.y < this._bounds.top) {
			this._bounds.top = pt.y;
		} else if (this._bounds.bottom < pt.y) {
			this._bounds.bottom = pt.y;
		}
		this._points.push(pt);
	}
	
	private var _bounds:Rectangle;
	public function get boundsRect():Rectangle { return this._bounds }

	private var _hasFill:Boolean;
	public function get hasFill():Boolean { return this._hasFill }
	
	private var _hasStroke:Boolean;
	public function get hasStroke():Boolean { return this._hasStroke }
	
	public function swapFillAndStroke():void {
		var flg:Boolean = this._hasFill;
		this._hasFill = this._hasStroke;
		this._hasStroke = flg;
		
		var val:Number = this._fillColor;
		this._fillColor = this._strokeColor;
		this._strokeColor = val;
		
		val = this._fillAlpha;
		this._fillAlpha = this._strokeAlpha;
		this._strokeAlpha = val;
	}

	private var _fillColor:Number;
	public function get fillColor():Number { return this._fillColor }
	public function set fillColor(col:Number):void { this._fillColor = col }
	
	private var _fillAlpha:Number;
	public function get fillAlpha():Number { return this._fillAlpha }
	public function set fillAlpha(al:Number):void { this._fillAlpha = al }
	
	private var _strokeColor:Number;
	public function get strokeColor():Number { return this._strokeColor }
	public function set strokeColor(col:Number):void { this._strokeColor = col }
	
	private var _strokeAlpha:Number;
	public function get strokeAlpha():Number { return this._strokeAlpha }
	public function set strokeAlpha(al:Number):void { this._strokeAlpha = al }
	
	private var _strokeWidth:Number;
	public function get strokeWidth():Number { return this._strokeWidth }
	public function set strokeWidth(wd:Number):void { this._strokeWidth = wd }

	/**
	 * 
	 */
	public function SVGPath(svgNode:XML) {
		this._commands = [];
		this._points = [];
		this._bounds = new Rectangle();
		this._bounds.top = this._bounds.left = Number.MAX_VALUE;
		this._bounds.bottom = this._bounds.right = Number.MIN_VALUE;
		this._hasFill = false;
		this._hasStroke = false;
		
		this.makeDrawCmds(this.extractCmds(svgNode));
	}

	/**
	* @method extractCmds ()
	* @param node (XMLNode) SVG path node
	* @description Parse path node and convert to array of SVG drawing commands and data
	*			eg, M,250.8,33.8,c,-33.6,-9.7,-42,19.1,-48.2,22.6,s,-27.9,2.2,-33.3,5.8,
	*				c,-5.3,3.5,-17.3,23.5,-8.4,41.6
	* @returns (Array) array of drawing commands
	*/	
	private function extractCmds(node:XML):Array {
		var i:Number;
		var startColor:Number;
		var thisColor:Number;

		//var hasFill:Boolean = false;
		var hasTransform:Boolean = false;
		//var hasStroke:Boolean = false;
		var hasStrokeWidth:Boolean = false;
		var hasRotate:Number = 0;
		var dstring:String = "";
		var rotation:Number;

		// is there a fill attribute, a transform attribute, a stroke attribute?
		for each (var a:XML in node.attributes()) {
			switch (a.name().toString()) {
				case 'fill': this._hasFill = true; break;
				case 'transform': hasTransform = true; break;
				case 'stroke': this._hasStroke = true; break;
				case 'stroke-width': hasStrokeWidth = true; break;
			}
		}
		if (this._hasFill) {
			// parse for fill color specification
			// if a hex number is specified, startColor will be > 0
			// if a color name is specified, startColor will be 0
			var fillStr:String = node.@fill;
			if (fillStr == 'none') {
				this._hasFill = false;
			} else {
				startColor = fillStr.indexOf("#") + 1;
				if (startColor == 0) {   // name specified instead of color number
					thisColor = SVGColor.getByName(fillStr);
					if (isNaN(thisColor)) {
						this._hasFill = false;
					} else {
						this._fillColor = thisColor;
						this._fillAlpha = 1.0;
					}
				} else {
					this._fillColor = parseInt(fillStr.substr(startColor, 6), 16);
					this._fillAlpha = 1.0;
				}
			}
		}
		
		// stroke: color, width, alpha
		if (this._hasStroke) {
			// parse for stroke color specification
			var strokeStr:String = node.@stroke;
			if (strokeStr == 'none') {
				this._hasStroke = false;
			} else {
				startColor = strokeStr.indexOf("#")+1;		
				if (startColor == 0) {   // name specified instead of color number
					thisColor = SVGColor.getByName(strokeStr);
					if (isNaN(thisColor)) {
						this._hasStroke = false;
					} else {
						this._strokeWidth = 0;
						this._strokeColor = SVGColor.getByName(strokeStr);
						this._strokeAlpha = 1.0;
					}
				} else {
					this._strokeWidth = 0;
					this._strokeColor = parseInt(strokeStr.substr(startColor,6),16);
					this._strokeAlpha = 1.0;
				}
			}
		}
		
		if (hasStrokeWidth) this._strokeWidth = Number(node.attribute("stroke-width"));
	
		// if stroke and fill are both undefined, set fill to black 
		if (!this._hasFill && !this._hasStroke) {
			this._hasFill = true;
			this._fillColor = 0;
			this._fillAlpha = 1.0;
		}
		
		if (hasTransform) {
			// parse for rotation specification
			var transformStr:String = node.@transform;
			hasRotate = transformStr.indexOf("rotate");
			if (hasRotate > -1) {
				var startRotate:Number = transformStr.indexOf("(");
				var endRotate:Number = transformStr.indexOf(")");
				rotation = parseInt(transformStr.substr(startRotate+1, endRotate-startRotate));
			} else {
				rotation = 0;
			}
	
		} else rotation = 0;
		
		// if commas included, is it Adobe Illustrator (no spaces) or SVG Factory/other?
		dstring = node.@d;
		if (dstring.indexOf(",") > -1) {  // has commas?
			if (dstring.indexOf(" ") > -1) {  // yes, has spaces?
				// change spaces to commas, then deal as for Illustrator
				dstring = String2.replace(dstring," ",",");
			}
		} else {  // no commas
			// get rid of extra spaces and change rest to commas
			dstring = String2.shrinkSequencesOf(dstring, " ");
			dstring = String2.replace(dstring, " ",",");
		}		
		dstring = String2.replace(dstring, "c",",c,");
		dstring = String2.replace(dstring, "C",",C,");
		dstring = String2.replace(dstring, "S",",S,");
		dstring = String2.replace(dstring, "s",",s,");
		// separate the z from the last element
		dstring = String2.replace(dstring, "z",",z");
		// change the following if M can be mid-path
		dstring = String2.replace(dstring, "M","M,");
		dstring = String2.replace(dstring, "L",",L,");
		dstring = String2.replace(dstring, "l",",l,");
		dstring = String2.replace(dstring, "H",",H,");
		dstring = String2.replace(dstring, "h",",h,");
		dstring = String2.replace(dstring, "V",",V,");
		dstring = String2.replace(dstring, "v",",v,");
		dstring = String2.replace(dstring, "Q",",Q,");
		dstring = String2.replace(dstring, "q",",q,");
		dstring = String2.replace(dstring, "T",",T,");
		dstring = String2.replace(dstring, "t",",t,");
		// Adobe includes no delimiter before negative numbers
		dstring = String2.replace(dstring, "-",",-");
		// get rid of any dup commas we might have introduced
		dstring = String2.replace(dstring, ",,",",");
		// get rid of spaces
		// (cr/lf's have to be removed before the xml object can be created,
		//  so that is done in xml.onData method)
		dstring = String2.replace(dstring, " ","");
		dstring = String2.replace(dstring, "\t","");

		return dstring.split(",");	
	}

	/**
	* @method makeDrawCmds
	* @param svgCmds (Array) array of svg draw commands (as output from extractCmds)
	* @description Convert svg draw commands to array of ASVDrawing commands: _commands
	*/	
	private function makeDrawCmds(svgCmds:Array):void {
		var j:Number = 0, ii:int;
		var qc:Array;
		var firstP:Point;
		var lastP:Point;
		var lastC:Point;
		var cmd:String;
		var cp:Point, pp:Point;
		
		do {
			cmd = svgCmds[j++];
			switch (cmd) {
			case "M" :
				// moveTo point
				firstP = lastP = new Point(Number(svgCmds[j]), Number(svgCmds[j+1]));
				if (this._hasFill) {
					_commands.push(new BeginFillCommand(this._fillColor, this._fillAlpha));
				}
				if (this._hasStroke) {
					_commands.push(new LineStyleCommand(this._strokeWidth, this._strokeColor, this._strokeAlpha));
				}
				_commands.push(new MoveToCommand(firstP));
				this.addPoint(firstP);
				j += 2;
				if (j < svgCmds.length && !isNaN(Number(svgCmds[j]))) {  
					do {
						// if multiple points listed, add the rest as lineTo points
						lastP = new Point(Number(svgCmds[j]), Number(svgCmds[j+1]));
						_commands.push(new LineToCommand(lastP));
						this.addPoint(lastP);
						firstP = lastP;
						j += 2;
					} while (j < svgCmds.length && !isNaN(Number(svgCmds[j])));
				}
				break;
				
			case "l" :
				do {
					lastP = new Point(lastP.x+Number(svgCmds[j]), lastP.y+Number(svgCmds[j+1]));
					_commands.push(new LineToCommand(lastP));
					this.addPoint(lastP);
					firstP = lastP;
					j += 2;
				} while (j < svgCmds.length && !isNaN(Number(svgCmds[j])));
				break;
				
			case "L" :
				do {
					lastP = new Point(Number(svgCmds[j]), Number(svgCmds[j+1]));
					_commands.push(new LineToCommand(lastP));				
					this.addPoint(lastP);
					firstP = lastP;
					j += 2;
				} while (j < svgCmds.length && !isNaN(Number(svgCmds[j])));
				break;
				
			case "h" :
				do {
					lastP = new Point(lastP.x+Number(svgCmds[j]), lastP.y);
					_commands.push(new LineToCommand(lastP));
					this.addPoint(lastP);
					firstP = lastP;
					j += 1;
				} while (j < svgCmds.length && !isNaN(Number(svgCmds[j])));
				break;
				
			case "H" :
				do {
					lastP = new Point(Number(svgCmds[j]), lastP.y);
					_commands.push(new LineToCommand(lastP));
					this.addPoint(lastP);
					firstP = lastP;
					j += 1;
				} while (j < svgCmds.length && !isNaN(Number(svgCmds[j])));
				break;
				
			case "v" :
				do {
					lastP = new Point(lastP.x, lastP.y+Number(svgCmds[j]));
					_commands.push(new LineToCommand(lastP));
					this.addPoint(lastP);
					firstP = lastP;
					j += 1;
				} while (j < svgCmds.length && !isNaN(Number(svgCmds[j])));
				break;
				
			case "V" :
				do {
					lastP = new Point(lastP.x, Number(svgCmds[j]));
					_commands.push(new LineToCommand(lastP));
					this.addPoint(lastP);
					firstP = lastP;
					j += 1;
				} while (j < svgCmds.length && !isNaN(Number(svgCmds[j])));
				break;
	
			case "q" :
				do {
					// control is relative to lastP, not lastC
					lastC = new Point(lastP.x+Number(svgCmds[j]), lastP.y+Number(svgCmds[j+1]));
					lastP = new Point(lastP.x+Number(svgCmds[j+2]), lastP.y+Number(svgCmds[j+3]));
					_commands.push(new CurveToCommand(lastC, lastP));
					this.addPoint(lastC, lastP);
					firstP = lastP;
					j += 4;
				} while (j < svgCmds.length && !isNaN(Number(svgCmds[j])));
				break;
				
			case "Q" :
				do {
					lastC = new Point(Number(svgCmds[j]), Number(svgCmds[j+1]));
					lastP = new Point(Number(svgCmds[j+2]), Number(svgCmds[j+3]));
					_commands.push(new CurveToCommand(lastC, lastP));
					this.addPoint(lastC, lastP);
					firstP = lastP;
					j += 4;
				} while (j < svgCmds.length && !isNaN(Number(svgCmds[j])));
				break;
				
			case "c" :
				do {
				// don't save if c1.x=c1.y=c2.x=c2.y=0 
					if (!Number(svgCmds[j]) && !Number(svgCmds[j+1]) && !Number(svgCmds[j+2]) && !Number(svgCmds[j+3])) {
					} else {
						qc = [];
						Math2.getQuadBez_RP(
							{x:lastP.x, y:lastP.y},   
							{x:lastP.x+Number(svgCmds[j]), y:lastP.y+Number(svgCmds[j+1])},
							{x:lastP.x+Number(svgCmds[j+2]), y:lastP.y+Number(svgCmds[j+3])},
							{x:lastP.x+Number(svgCmds[j+4]), y:lastP.y+Number(svgCmds[j+5])},
						    1, qc);
						for (ii=0; ii<qc.length; ii++) {
							cp = new Point(qc[ii].cx, qc[ii].cy);
							pp = new Point(qc[ii].p2x, qc[ii].p2y);
							_commands.push(new CurveToCommand(cp, pp));
							this.addPoint(cp, pp);
						}
						lastC = new Point(lastP.x+Number(svgCmds[j+2]), lastP.y+Number(svgCmds[j+3]));
						lastP = new Point(lastP.x+Number(svgCmds[j+4]), lastP.y+Number(svgCmds[j+5]));
						this.addPoint(lastC, lastP);
						firstP = lastP;
					}
					j += 6;
				} while (j < svgCmds.length && !isNaN(Number(svgCmds[j])));
				break;
	
			case "C" :
				do {
				// don't save if c1.x=c1.y=c2.x=c2.y=0 
					if (!Number(svgCmds[j]) && !Number(svgCmds[j+1]) && !Number(svgCmds[j+2]) && !Number(svgCmds[j+3])) {
					} else {
						qc = [];
						Math2.getQuadBez_RP(
							{x:firstP.x, y:firstP.y},   
							{x:Number(svgCmds[j]), y:Number(svgCmds[j+1])},
							{x:Number(svgCmds[j+2]), y:Number(svgCmds[j+3])},
							{x:Number(svgCmds[j+4]), y:Number(svgCmds[j+5])},
							1, qc);
						for (ii=0; ii<qc.length; ii++) {
							cp = new Point(qc[ii].cx, qc[ii].cy);
							pp = new Point(qc[ii].p2x, qc[ii].p2y);
							_commands.push(new CurveToCommand(cp, pp));
							this.addPoint(cp, pp);
						}
						lastC = new Point(lastP.x+Number(svgCmds[j+2]), lastP.y+Number(svgCmds[j+3]));
						lastP = new Point(Number(svgCmds[j+4]), Number(svgCmds[j+5]));
						this.addPoint(lastC, lastP);
						firstP = lastP;
					}
					j += 6;
				} while (j < svgCmds.length && !isNaN(Number(svgCmds[j])));
				break;
				
			case "s" :
				do {
				// don't save if c1.x=c1.y=c2.x=c2.y=0 
					if (!Number(svgCmds[j]) && !Number(svgCmds[j+1]) && !Number(svgCmds[j+2]) && !Number(svgCmds[j+3])) {
					} else {
						qc = [];
						Math2.getQuadBez_RP(
							{x:firstP.x, y:firstP.y},   
							{x:lastP.x + (lastP.x - lastC.x), y:lastP.y + (lastP.y - lastC.y)},
							{x:lastP.x+Number(svgCmds[j]), y:lastP.y+Number(svgCmds[j+1])},
							{x:lastP.x+Number(svgCmds[j+2]), y:lastP.y+Number(svgCmds[j+3])},
							1, qc);
						for (ii=0; ii<qc.length; ii++) {
							cp = new Point(qc[ii].cx, qc[ii].cy);
							pp = new Point(qc[ii].p2x, qc[ii].p2y);
							_commands.push(new CurveToCommand(cp, pp));
							this.addPoint(cp, pp);
						}
						lastC = new Point(lastP.x+Number(svgCmds[j]), lastP.y+Number(svgCmds[j+1]));
						lastP = new Point(lastP.x+Number(svgCmds[j+2]), lastP.y+Number(svgCmds[j+3]));
						this.addPoint(lastC, lastP);
						firstP = lastP;
					}
					j += 4;
				} while (j < svgCmds.length && !isNaN(Number(svgCmds[j])));
				break;
				
			case "S" :
				do {
				// don't save if c1.x=c1.y=c2.x=c2.y=0 
					if (!Number(svgCmds[j]) && !Number(svgCmds[j+1]) && !Number(svgCmds[j+2]) && !Number(svgCmds[j+3])) {
					} else {
						qc = [];
						Math2.getQuadBez_RP(
							{x:firstP.x, y:firstP.y},   
							{x:lastP.x + (lastP.x - lastC.x), y:lastP.y + (lastP.y - lastC.y)},
							{x:Number(svgCmds[j]), y:Number(svgCmds[j+1])},
							{x:Number(svgCmds[j+2]), y:Number(svgCmds[j+3])}, 
							1, qc);
						for (ii=0; ii<qc.length; ii++) {
							cp = new Point(qc[ii].cx, qc[ii].cy);
							pp = new Point(qc[ii].p2x, qc[ii].p2y);
							_commands.push(new CurveToCommand(cp, pp));
							this.addPoint(cp, pp);
						}
						lastC = new Point(Number(svgCmds[j]), Number(svgCmds[j+1]));
						lastP = new Point(Number(svgCmds[j+2]), Number(svgCmds[j+3]));
						this.addPoint(lastC, lastP);
						firstP = lastP;
					}
					j += 4;
				} while (j < svgCmds.length && !isNaN(Number(svgCmds[j])));
				break;
				
			case "z" :
			case "Z" :
				if (!firstP.equals(lastP)) {
					_commands.push(new LineToCommand(firstP));
					this.addPoint(firstP);
				}
				j++;
				break;		
				
			} // end switch
		}  while (j < svgCmds.length);
	}
	
	/**
	 * 
	 */
	public function draw(graphics:Graphics):void {
		for each (var command:SVGPathCommand in this._commands) {
			command.execute(graphics);
		}
	}
	
}



class String2 {
	
	/**
	* @class String2
	* @author Helen Triolo, with inclusions from Tim Groleau
	* @description String functions not included in String needed for path->array conversion
	*/

	/**
	* @method replace ()
	* @description Replaces sFind in s with sReplace
	* @param s (String) original string
	* @param sFind (String) part to be replaced
	* @param sReplace (String) string to replace it with
	* @returns (String) string with replacement
	*/
	public static function replace(s:String, sFind:String, sReplace:String):String {
	  return s.split(sFind).join(sReplace);
	}
	
	/**
	* @method shrinkSequencesOf (Groleau)
	* @description Shrinks all sequences of a given character in a string to one
	* @param s (String) original string
	* @param ch (String) character to be found
	* @returns (String) string with sequences shrunk
	*/
	public static function shrinkSequencesOf(s:String, ch:String):String {
		var len:Number = s.length;
		var idx:Number = 0;
		var idx2:Number = 0;
		var rs:String = "";
		
		while ((idx2 = s.indexOf(ch, idx) + 1) != 0) {
			// include string up to first character in sequence
			rs += s.substring(idx, idx2);
			idx = idx2;
			
			// remove all subsequent characters in sequence
			while ((s.charAt(idx) == ch) && (idx < len)) idx++;
		}
		return rs + s.substring(idx, len);	
	}
}


class Math2 {
	
	/**
	* @class Math2
	* @author Helen Triolo, with inclusions from Robert Penner, Tim Groleau
	* @description Math functions not included in Math needed for path->array conversion
	*/
	
	/**
	* @method ratioTo (Groleau)
	* @description Returns the point on segment [p1,p2] which is ratio times the total distance 
	*				between p1 and p2 away from p1
	* @param p1 (Object) x and y values of point p1
	* @param p2 (Object) x and y values of point p2
	* @param ratio (Number) real
	* @returns Object
	*/
	public static function ratioTo(p1:Object, p2:Object, ratio:Number):Object {
		return {x:p1.x + (p2.x - p1.x) * ratio, y:p1.y + (p2.y - p1.y) * ratio };
	}

	/**
	* @method intersect2Lines (Penner)
	* @description Returns the point of intersection between two lines
	* @param p1, p2 (Objects) points on line 1
	* @param p3, p4 (Objects) points on line 2
	* @returns Object (point of intersection)
	*/
	public static function intersect2Lines (p1:Object, p2:Object, p3:Object, p4:Object):Object {
		var x1:Number = p1.x; var y1:Number = p1.y;
		var x4:Number = p4.x; var y4:Number = p4.y;
	    var dx1:Number = p2.x - x1;
	    var dx2:Number = p3.x - x4;

	   if (!(dx1 || dx2)) return NaN;
	   var m1:Number = (p2.y - y1) / dx1;
	   var m2:Number = (p3.y - y4) / dx2;
	   if (!dx1) {
	      return { x:x1, y:m2 * (x1 - x4) + y4 };
	   } else if (!dx2) {
	      return { x:x4, y:m1 * (x4 - x1) + y1 };
	   }
	   var xInt:Number = (-m2 * x4 + y4 + m1 * x1 - y1) / (m1 - m2);
	   var yInt:Number = m1 * (xInt - x1) + y1;
	   return { x:xInt,y:yInt };
	}

	/**
	* @method rotation
	* @description Returns the angle in degrees from the horizontal to a point dy up and dx over
	* @param dy (Number) pixels
	* @param dx (Number) pixels
	* @returns Number (angle, degrees)
	*/
	public static function rotation(dy:Number, dx:Number):Number {
		return Math.atan2(dy, dx) * 180/Math.PI;
	}

	/**
	* @method midPt
	* @description Returns the midpoint (x/y) of a line segment from p1x/p1y to p2x/p2y
	* @param p1x (Number) pixels
	* @param p1y (Number) pixels
	* @param p2x (Number) pixels
	* @param p2y (Number) pixels
	* @returns Object (midpoint)
	*/
	public static function midPt(p1x:Number, p1y:Number, p2x:Number, p2y:Number):Object {
		return {x:(p1x + p2x)/2, y:(p1y + p2y)/2};
	}

	/**
	* @method getQuadBez_RP (Penner)
	* @description  Approximates a cubic bezier with as many quadratic bezier segments (n) as required 
	*			to achieve a specified tolerance
	* @param p1 (Object) endpoint
	* @param c1 (Object) 1st control point
	* @param c2 (Object) 2nd control point
	* @param p2 (Object) endpoint
	* @param k: tolerance (low number = most accurate result)
	* @param qcurves (Array) will contain array of quadratic bezier curves, each element containing
	*		p1x, p1y, cx, cy, p2x, p2y
	*/
 	public static function getQuadBez_RP(p1:Object, c1:Object, c2:Object, p2:Object, k:Number, qcurves:Array):void {
		// find intersection between bezier arms
		var s:Object = Math2.intersect2Lines (p1, c1, c2, p2);
		// find distance between the midpoints
		var dx:Number = (p1.x + p2.x + s.x * 4 - (c1.x + c2.x) * 3) * .125;
		var dy:Number = (p1.y + p2.y + s.y * 4 - (c1.y + c2.y) * 3) * .125;
		// split curve if the quadratic isn't close enough
		if (dx*dx + dy*dy > k) {
			var halves:Object = Math2.bezierSplit (p1.x, p1.y, c1.x, c1.y, c2.x, c2.y, p2.x, p2.y);
			var b0:Object = halves.b0; var b1:Object = halves.b1;
			// recursive call to subdivide curve
			getQuadBez_RP (p1,     b0.c1, b0.c2, b0.p2, k, qcurves);
			getQuadBez_RP (b1.p1,  b1.c1, b1.c2, p2,    k, qcurves);
		} else {
			// end recursion by saving points
			qcurves.push({p1x:p1.x, p1y:p1.y, cx:s.x, cy:s.y, p2x:p2.x, p2y:p2.y});
		}
	}
		
	/**
	* @method bezierSplit (Penner)
	* @description    Divides a cubic bezier curve into two halves (each also cubic beziers)
	* @param p1x (Number) pixels, endpoint 1
	* @param p1y (Number) pixels
	* @param c1x (Number) pixels, control point 1
	* @param c1y (Number) pixels
	* @param c2x (Number) pixels, control point 2
	* @param c2y (Number) pixels
	* @param p2x (Number) pixels, endpoint 2
	* @param p2y (Number) pixels
	* @returns Object (object with two cubic bezier definitions, b0 and b1)
	*/
	public static function bezierSplit(p1x:Number, p1y:Number, c1x:Number, c1y:Number, c2x:Number, c2y:Number, p2x:Number, p2y:Number):Object {
	    var m:Function = Math2.midPt;
		var p1:Object = {x:p1x, y:p1y};
		var p2:Object = {x:p2x, y:p2y};
	    var p01:Object = m (p1x, p1y, c1x, c1y);
	    var p12:Object = m (c1x, c1y, c2x, c2y);
	    var p23:Object = m (c2x, c2y, p2x, p2y);
	    var p02:Object = m (p01.x, p01.y, p12.x, p12.y);
	    var p13:Object = m (p12.x, p12.y, p23.x, p23.y);
	    var p03:Object = m (p02.x, p02.y, p13.x, p13.y);

		/*
        b0:{a:p0,  b:p01, c:p02, d:p03},
        b1:{a:p03, b:p13, c:p23, d:p3 }  
		*/

		return { b0:{p1:p1, c1:p01, c2:p02, p2:p03}, b1:{p1:p03, c1:p13, c2:p23, p2:p2} };
	}

	/**
	* @method pointOnCurve (Penner)
	* @description Returns a point on a quadratic bezier curve with Robert Penner's optimization 
	*				of the standard equation:
	*					{x:p1x * (1-t) * (1-t) + 2 * cx * t * (1-t) + p2x * t * t,
	*		             y:p1y * (1-t) * (1-t) + 2 * cy * t * (1-t) + p2y * t * t }
	* @param p1x (Number) pixels, endpoint 1
	* @param p1y (Number) pixels
	* @param cx (Number) pixels, control point 
	* @param cy (Number) pixels
	* @param p2x (Number) pixels, endpoint 2
	* @param p2y (Number) pixels
	* @param t (Number) if time is 0-1 along curve, value of t to find point at
	* @returns Object (point at time t)
	*/
	public static function pointOnCurve(p1x:Number, p1y:Number, cx:Number, cy:Number, p2x:Number, p2y:Number, t:Number):Object {
		var o:Object = new Object();
		o.x = p1x + t*(2*(1-t)*(cx-p1x) + t*(p2x - p1x));
		o.y = p1y + t*(2*(1-t)*(cy-p1y) + t*(p2y - p1y));
		return o;
	}

	/**
	* @method pointsOnCurve (Penner)
	* @description Returns an array of objects defining points on a quadratic bezier curve, each of which
	*				includes:
	*					x,y = location of point defining start of segment
	*					r = rotation of segment (last entry has none because it's just a point)
	*					n = number of subdivisions into which curve will be divided (pts = n+1)
	* @param p1x (Number) pixels, endpoint 1
	* @param p1y (Number) pixels
	* @param cx (Number) pixels, control point 
	* @param cy (Number) pixels
	* @param p2x (Number) pixels, endpoint 2
	* @param p2y (Number) pixels
	* @param n (Number) number of points to return
	* @returns Array
	*/
	public static function pointsOnCurve(p1x:Number, p1y:Number, cx:Number, cy:Number, p2x:Number, p2y:Number, n:Number):Array {
		var pts:Array = [];
		for (var i:Number=0; i <= n; i++) {
		   pts.push(Math2.pointOnCurve(p1x, p1y, cx, cy, p2x, p2y, i/n));
		   if (i > 0) {
			   pts[i].r = Math2.rotation(pts[i].y-pts[(i-1)].y, pts[i].x-pts[(i-1)].x);
		   }
		}
		pts.splice(0,1);  // remove 1st element to return 1/n, 2/n,... n
		return pts;
	}

	/**
	* @method pointsOnLine (Penner)
	* @description Returns an array of point positions and rotations for n evenly spaced points 
	*					along a line segment
	*					x,y = location of point defining start of segment
	*					r = rotation of segment (last entry has none because it's just a point)
	*					n = number of subdivisions into which curve will be divided (pts = n+1)
	* @param p1x (Number) pixels, endpoint 1
	* @param p1y (Number) pixels
	* @param p2x (Number) pixels, endpoint 2
	* @param p2y (Number) pixels
	* @param n (Number) number of points to return
	* @returns Array
	*//* ....................................................................
	   Returns an array of point positions and rotation for n evenly spaced points along a line segment
	*/
	public static function pointsOnLine(p1x:Number, p1y:Number, p2x:Number, p2y:Number, n:Number):Array {
		var pts:Array = [];
		if (p2x != p1x) {
			var m:Number = (p2y-p1y)/(p2x-p1x);
			var b:Number = p1y - p1x * m;
			for (var i:Number=0; i <= n; i++) {
				var x:Number = p1x + ((p2x-p1x)/n) * i;
				pts.push({x:x, y:m*x+b});
				if (i > 0) {
					pts[i].r = Math2.rotation(pts[i].y-pts[(i-1)].y, pts[i].x-pts[(i-1)].x);
				}
			}
		// vertical segment
		} else {
			for (i=0; i<=n; i++) {
				pts.push({x:p1x, y:p1y + ((p2y-p1y)/n) * i, r:90});
			}
		}			
		pts.splice(0,1);  // remove 1st element to return 1/n, 2/n,... n
		return pts;
	}

	/**
	* @method curveApproxLen
	* @description Returns the approximate length of a curved segment, found by dividing it 
	*				into two segments at t=0.5
	* @param p1x (Number) pixels, endpoint 1
	* @param p1y (Number) pixels
	* @param cx (Number) pixels, control point 
	* @param cy (Number) pixels
	* @param p2x (Number) pixels, endpoint 2
	* @param p2y (Number) pixels
	* @returns Number
	*/
	public static function curveApproxLen(p1x:Number, p1y:Number, cx:Number, cy:Number, p2x:Number, p2y:Number):Number {
		var mp:Object = Math2.pointOnCurve(p1x, p1y, cx, cy, p2x, p2y, 0.5);
		var len1:Number = Math.sqrt((mp.x - p1x) * (mp.x - p1x) + (mp.y - p1y) * (mp.y - p1y));
		var len2:Number = Math.sqrt((mp.x - p2x) * (mp.x - p2x) + (mp.y - p2y) * (mp.y - p2y));
		return len1+len2;
	}

	/**
	* @method lineLen
	* @description Returns the length of a line segment
	* @param p1x (Number) pixels, endpoint 1
	* @param p1y (Number) pixels
	* @param p2x (Number) pixels, endpoint 2
	* @param p2y (Number) pixels
	* @returns Number
	*/
	public static function lineLen(p1x:Number, p1y:Number, p2x:Number, p2y:Number):Number {
		return Math.sqrt((p2x - p1x) * (p2x - p1x) + (p2y - p1y) * (p2y - p1y));
	}

	/**
	* @method roundTo
	* @description Returns a number rounded to specified number of decimals 
	* @param n (Number) 
	* @param ndec (Number) number of decimals to round to
	* @returns Number
	*/
	public static function roundTo(n:Number, ndec:Number):Number {
		var multiplier:Number = Math.pow(10, ndec);
		return Math.round(n*multiplier)/multiplier;
	}
}



class SVGColor {
	
	public static function getByName(name:String):Number {
		var col:Number = SVGColor._colorTable[name];
		return isNaN(col) ? 0 : col;
	}
	
	private static const _colorTable:Object = {
		blue:0x0000ff,
		green:0x008000,
		red:0xff0000,
		aliceblue:0xf0f8ff,
		antiquewhite:0xfaebd7,
		aqua:0x00ffff,
		aquamarine:0x7fffd4,
		azure:0xf0ffff,
		beige:0xf5f5dc,
		bisque:0xffe4c4,
		black:0x000000,
		blanchedalmond:0xffebcd,
		blueviolet:0x8a2be2,
		brown:0xa52a2a,
		burlywood:0xdeb887,
		cadetblue:0x5f9ea0,
		chartreuse:0x7fff00,
		chocolate:0xd2691e,
		coral:0xff7f50,
		cornflowerblue:0x6495ed,
		cornsilk:0xfff8dc,
		crimson:0xdc143c,
		cyan:0x00ffff,
		darkblue:0x00008b,
		darkcyan:0x008b8b,
		darkgoldenrod:0xb8860b,
		darkgray:0xa9a9a9,
		darkgreen:0x006400,
		darkgrey:0xa9a9a9,
		darkkhaki:0xbdb76b,
		darkmagenta:0x8b008b,
		darkolivegreen:0x556b2f,
		darkorange:0xff8c00,
		darkorchid:0x9932cc,
		darkred:0x8b0000,
		darksalmon:0xe9967a,
		darkseagreen:0x8fbc8f,
		darkslateblue:0x483d8b,
		darkslategray:0x2f4f4f,
		darkslategrey:0x2f4f4f,
		darkturquoise:0x00ced1,
		darkviolet:0x9400d3,
		deeppink:0xff1493,
		deepskyblue:0x00bfff,
		dimgray:0x696969,
		dimgrey:0x696969,
		dodgerblue:0x1e90ff,
		firebrick:0xb22222,
		floralwhite:0xfffaf0,
		forestgreen:0x228b22,
		fuchsia:0xff00ff,
		gainsboro:0xdcdcdc,
		ghostwhite:0xf8f8ff,
		gold:0xffd700,
		goldenrod:0xdaa520,
		gray:0x808080,
		grey:0x808080,
		greenyellow:0xadff2f,
		honeydew:0xf0fff0,
		hotpink:0xff69b4,
		indianred:0xcd5c5c,
		indigo:0x4b0082,
		ivory:0xfffff0,
		khaki:0xf0e68c,
		lavender:0xe6e6fa,
		lavenderblush:0xfff0f5,
		lawngreen:0x7cfc00,
		lemonchiffon:0xfffacd,
		lightblue:0xadd8e6,
		lightcoral:0xf08080,
		lightcyan:0xe0ffff,
		lightgoldenrodyellow:0xfafad2,
		lightgray:0xd3d3d3,
		lightgreen:0x90ee90,
		lightgrey:0xd3d3d3,
		lightpink:0xffb6c1,
		lightsalmon:0xffa07a,
		lightseagreen:0x20b2aa,
		lightskyblue:0x87cefa,
		lightslategray:0x778899,
		lightslategrey:0x778899,
		lightsteelblue:0xb0c4de,
		lightyellow:0xffffe0,
		lime:0x00ff00,
		limegreen:0x32cd32,
		linen:0xfaf0e6,
		magenta:0xff00ff,
		maroon:0x800000,
		mediumaquamarine:0x66cdaa,
		mediumblue:0x0000cd,
		mediumorchid:0xba55d3,
		mediumpurple:0x9370db,
		mediumseagreen:0x3cb371,
		mediumslateblue:0x7b68ee,
		mediumspringgreen:0x00fa9a,
		mediumturquoise:0x48d1cc,
		mediumvioletred:0xc71585,
		midnightblue:0x191970,
		mintcream:0xf5fffa,
		mistyrose:0xffe4e1,
		moccasin:0xffe4b5,
		navajowhite:0xffdead,
		navy:0x000080,
		oldlace:0xfdf5e6,
		olive:0x808000,
		olivedrab:0x6b8e23,
		orange:0xffa500,
		orangered:0xff4500,
		orchid:0xda70d6,
		palegoldenrod:0xeee8aa,
		palegreen:0x98fb98,
		paleturquoise:0xafeeee,
		palevioletred:0xdb7093,
		papayawhip:0xffefd5,
		peachpuff:0xffdab9,
		peru:0xcd853f,
		pink:0xffc0cb,
		plum:0xdda0dd,
		powderblue:0xb0e0e6,
		purple:0x800080,
		rosybrown:0xbc8f8f,
		royalblue:0x4169e1,
		saddlebrown:0x8b4513,
		salmon:0xfa8072,
		sandybrown:0xf4a460,
		seagreen:0x2e8b57,
		seashell:0xfff5ee,
		sienna:0xa0522d,
		silver:0xc0c0c0,
		skyblue:0x87ceeb,
		slateblue:0x6a5acd,
		slategray:0x708090,
		slategrey:0x708090,
		snow:0xfffafa,
		springgreen:0x00ff7f,
		steelblue:0x4682b4,
		tan:0xd2b48c,
		teal:0x008080,
		thistle:0xd8bfd8,
		tomato:0xff6347,
		turquoise:0x40e0d0,
		violet:0xee82ee,
		wheat:0xf5deb3,
		white:0xffffff,
		whitesmoke:0xf5f5f5,    
		yellow:0xffff00,
		yellowgreen:0x9acd32
	}
	
}


class SVGPathCommandType {

	public static const BEGIN_FILL_COMMAND:Number = 1;
	public static const LINE_STYLE_COMMAND:Number = 2;
	public static const MOVE_TO_COMMAND:Number = 3;
	public static const LINE_TO_COMMAND:Number = 4;
	public static const CURVE_TO_COMMAND:Number = 5;
	
}


interface ISVGPathCommand {
		
	function execute(graphics:Graphics):void;
	
}


class SVGPathCommand implements ISVGPathCommand {
	
	private var _type:Number;
	public function get type():Number { return this._type; }
	
	public function SVGPathCommand(type:Number = 0) {
		this._type = type;
	}
	
	public function execute(graphics:Graphics):void {
		throw new Error('Subclass must be implement execute method.');
	}
	
}


class BeginFillCommand extends SVGPathCommand {
	
	private var _color:Number;
	public function get color():Number { return this._color; }
	
	private var _alpha:Number;
	public function get alpha():Number { return this._alpha; }
	
	public function BeginFillCommand(color:Number, alpha:Number) {
		super(SVGPathCommandType.BEGIN_FILL_COMMAND);
		this._color = color;
		this._alpha = alpha;
	}
	
	public override function execute(graphics:Graphics):void {
		graphics.beginFill(this._color, this._alpha);
	}
	
}


class CurveToCommand extends SVGPathCommand implements ISVGPathCommand {
	
	private var _pt1:Point;
	public function get x1():Number { return this._pt1.x; }
	public function get y1():Number { return this._pt1.y; }
	
	private var _pt2:Point;
	public function get x2():Number { return this._pt2.x; }
	public function get y2():Number { return this._pt2.y; }
	
	public function CurveToCommand(pt1:Point, pt2:Point) {
		super(SVGPathCommandType.CURVE_TO_COMMAND);
		this._pt1 = pt1;
		this._pt2 = pt2;
	}
	
	public override function execute(graphics:Graphics):void {
		graphics.curveTo(this._pt1.x, this._pt1.y, this._pt2.x, this._pt2.y);
	}
	
}


class LineStyleCommand extends SVGPathCommand implements ISVGPathCommand {
	
	private var _width:Number;
	public function get width():Number { return this._width; }
	
	private var _color:Number;
	public function get color():Number { return this._color; }
	
	private var _alpha:Number;
	public function get alpha():Number { return this._alpha; }
	
	public function LineStyleCommand(width:Number, color:Number, alpha:Number) {
		super(SVGPathCommandType.LINE_STYLE_COMMAND);
		this._width = width;
		this._color = color;
		this._alpha = alpha;
	}
	
	public override function execute(graphics:Graphics):void {
		graphics.lineStyle(this._width, this._color, this._alpha);
	}
}


class LineToCommand extends SVGPathCommand implements ISVGPathCommand {
	
	private var _pt:Point;
	public function get x():Number { return this._pt.x; }
	public function get y():Number { return this._pt.y; }
	
	public function LineToCommand(pt:Point) {
		super(SVGPathCommandType.LINE_TO_COMMAND);
		this._pt = pt;
	}
	
	public override function execute(graphics:Graphics):void {
		graphics.lineTo(this._pt.x, this._pt.y);
	}
	
}


class MoveToCommand extends SVGPathCommand implements ISVGPathCommand {
	
	private var _pt:Point;
	public function get x():Number { return this._pt.x; }
	public function get y():Number { return this._pt.y; }
	
	public function MoveToCommand(pt:Point) {
		super(SVGPathCommandType.MOVE_TO_COMMAND);
		this._pt = pt;
	}
	
	public override function execute(graphics:Graphics):void {
		graphics.moveTo(this._pt.x, this._pt.y);
	}
	
}