/** drawing pacakage */ package com.senocular.drawing { import flash.geom.Point; /** * The Path class saves a collection of line drawing commands to represent a path. * Using the path class you can draw all of or a segment of that path in a movie clip * or get locations and orientations along the path to have objects follow it. * * Note: People using Flash MX 2004 will have to use a custom flash.geom.Point class * * @usage *
import com.senocular.drawing.Path;
	* // create a path instance that draws in _root
	* var myPath:Path = new Path();
	* // draw a square in the path
	* myPath.moveTo(50, 50);
	* myPath.lineTo(100, 50);
	* myPath.lineTo(100, 100);
	* myPath.lineTo(50, 100);
	* myPath.lineTo(50, 50);
	* // draw the path in _root
	* graphics.lineStyle(0, 0, 100);
	* myPath.draw(graphics);
	* 
* @author Trevor McCauley * @author senocular.com * @version 2.2.0 */ public class Path { private var _lengthValid:Boolean = true; protected var _length:Number = 0; protected var _moveToHasLength:Boolean = false; protected var _position:Point; protected var _segments:Array; // Constructor /** * Constructor. Creates a new path instance */ public function Path(){ init(); } // Public Properties /** * The approximate length of the total path in pixels. * @see moveToHasLength */ public function get length():Number { if (!_lengthValid) { _lengthValid = true; _length = 0; var seg:PathSegment; var i:int = _segments.length; while (i--){ seg = _segments[i]; if (_moveToHasLength || !(seg is PathMoveSegment)){ _length += seg.length; } } } return _length; } /** * Determines whether or not moveTo commands are treated * as line segments therefore adding to the total length of the path * @see length */ public function get moveToHasLength():Boolean { return _moveToHasLength; } public function set moveToHasLength(b:Boolean):void { _lengthValid = false; _moveToHasLength = b; } /** * The current position of the drawing pen based on the last drawing command used. * If you want to move the pen position without invoking a moveTo command, set * this property to a new point at the desired location * @see moveTo * @see lineTo * @see curveTo */ public function get position():Point { return _position.clone(); } public function set position(p:Point):void { _position = p.clone(); _segments.push(new PathSegment(_position)); } // Public Methods /** * Moves the current drawing position to (x, y). * @param x An integer indicating the horizontal location to move the drawing position * @param y An integer indicating the vertical location to move the drawing position * @see lineTo * @see curveTo * @return Nothing */ public function moveTo(x:Number, y:Number):void { if (_moveToHasLength){ _lengthValid = false; } var end:Point = new Point(x, y); _segments.push(new PathMoveSegment(_position, end)); _position = end; } /** * Creates a line in the path from the current drawing position * to (x, y); the current drawing position is then set to (x, y). * @param x An integer indicating the horizontal position of the end anchor point of the line * @param y An integer indicating the vertical position of the end anchor point of the line * @see moveTo * @see curveTo * @return Nothing */ public function lineTo(x:Number, y:Number):void { _lengthValid = false; var end:Point = new Point(x, y); _segments.push(new PathLineSegment(_position, end)); _position = end; } /** * Creates a curve in the path from the current drawing position to * (x, y) using the control point specified by (cx, cy). The current drawing position is then set * to (x, y). * @param cx An integer that specifies the horizontal position of the control point of the curve * @param cy An integer that specifies the vertical position of the control point of the curve * @param x An integer that specifies the horizontal position of the end anchor point of the curve * @param y An integer that specifies the vertical position of the end anchor point of the curve * @see moveTo * @see lineTo * @return Nothing */ public function curveTo(cx:Number, cy:Number, x:Number, y:Number):void { _lengthValid = false; var end:Point = new Point(x, y); _segments.push(new PathCurveSegment(_position, new Point(cx, cy), end)); _position = end; } /** * Creates a circle segment in the path from the current drawing position to * (x, y) using a third intermediary point (cx, cy) which lies on the circular path * between the current drawing position and (x, y). The current drawing position is then set * to (x, y). * @param cx An integer that specifies the horizontal position of the control point of the circle segment * @param cy An integer that specifies the vertical position of the control point of the circle segment * @param x An integer that specifies the horizontal position of the end anchor point of the circle segment * @param y An integer that specifies the vertical position of the end anchor point of the circle segment * @see moveTo * @see lineTo * @see curveTo * @return Nothing */ public function circleTo(cx:Number, cy:Number, x:Number, y:Number):void { _lengthValid = false; var end:Point = new Point(x, y); _segments.push(new PathCircleSegment(_position, new Point(cx, cy), end)); _position = end; } /** * Clears all drawing commands in the path * @see moveTo * @see lineTo * @see curveTo * @return nothing */ public function clear():void { init(); } /** * Draws the saved path into the target object passed. * @param target The object receiving a copy of the drawing commands saved to the path instance * @param startt A float between 0 and 1 where 0 is the start of the path and 1 is the end of * the path to start drawing in the target * @param endt A float between 0 and 1 where 0 is the start of the path and 1 is the end of * the path to stop drawing in the target * @see moveTo * @see lineTo * @see curveTo * @return Nothing */ public function draw(target:*, startt:Number = 0, endt:Number = 1):void { startt = cleant(startt, 0); endt = cleant(endt, 1); if (endt < startt){ draw(target, startt, 1); draw(target, 0, endt); return; } var segments:Array = getSegmentsToDraw(startt, endt); if (segments.length){ target.moveTo(segments[0]._start.x, segments[0]._start.y); var n:int = segments.length; var i:int; for (i=0; i 1){ t %= 1; if (t == 0) t = base; else if (t < 0) t += 1; } return t; } private function getSegmentsToDraw(startt:Number, endt:Number):Array { var startLength:Number = startt*length; var endLength:Number = endt*length; var curLength:Number = 0; var lastLength:Number = 0; var starti:int = -1; var endi:int = -1; var segStartt:Number = 0; var segEndt:Number = 1; var newSegments:Array = new Array(); var seg:PathSegment; var n:int = _segments.length; var i:int; for (i=0; i Math.PI) { angle -= Math.PI*2; }else if (angle < -Math.PI) { angle += Math.PI*2; } return angle; } internal override function trim(t:Number, fromStart:Boolean = false):PathSegment { var startPt:Point; var endPt:Point; if (fromStart){ endPt = _start; startPt = _end; }else{ startPt = _start; endPt = _end; } var newstart:Point = startPt; var newcontrol:Point; var newend:Point = startPt; var angle:Number = angleStart + t*arc; if (fromStart){ newstart = new Point(_center.x + Math.cos(angle)*radius, _center.y + Math.sin(angle)*radius); angle = (angleEnd + angle)/2; }else{ newend = new Point(_center.x + Math.cos(angle)*radius, _center.y + Math.sin(angle)*radius); angle = (angleStart + angle)/2; } newcontrol = new Point(_center.x + Math.cos(angle)*radius, _center.y + Math.sin(angle)*radius); return new _constructor(newstart, newcontrol, newend); } // Private Methods private function circleLength():Number { return radius*arc; } private function getCircleCenter(p1:Point, p2:Point, p3:Point):Point { var tmp:Point; if (p1.x == p2.x || p1.y == p2.y) { tmp = p1; p1 = p3; p3 = tmp; } if (p2.x == p3.x) { tmp = p1; p1 = p2; p2 = tmp; } if (p1.x == p2.x || p2.x == p3.x) return null; var ma:Number = (p2.y - p1.y)/(p2.x - p1.x); var mb:Number = (p3.y - p2.y)/(p3.x - p2.x); if (ma == mb) return null; var x12:Number = p1.x + p2.x; var x23:Number = p2.x + p3.x; var x:Number = (ma*mb*(p1.y - p3.y) + mb*x12 - ma*x23)/(2*(mb - ma)); var y:Number = (ma) ? (p1.y + p2.y)/2 - (x - x12/2)/ma : (p2.y + p3.y)/2 - (x - x23/2)/mb; return new Point(x,y); } }