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

flash on 2010-2-14

Get Adobe Flash player
by jules 13 Feb 2010
    Embed
/**
 * Copyright jules ( http://wonderfl.net/user/jules )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/aPSG
 */

package {

	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.text.TextField;

	public class CubicBezierCurve extends Sprite
	{
		
		private var lineHolder:Sprite = new Sprite();
		private var pencilHolder:Sprite = new Sprite();
		
		private var line:Sprite;
		private var bezierLine:CubicBezier;
		private var pencilPoint:PencilPoint;
		private var button:Sprite;
		
		/**
		 * Constructor
		 * 	- Add containers
		 *  - Add points to the stage to start with
		 *  - Add a button to turn off the anchorpoints/controlpoints
		 */
		public function CubicBezierCurve()
		{
			stage.frameRate		= 25;
			stage.align			= StageAlign.TOP_LEFT;
			stage.scaleMode 	= StageScaleMode.NO_SCALE;
			
			
			//Add containers
			addChild(lineHolder);
			addChild(pencilHolder);
			
			//Points on the left
			for (var i:int; i<10;i++) {
			pencilPoint = new PencilPoint(100+i,200+i);
			pencilHolder.addChild(pencilPoint);
			pencilPoint.addEventListener(MouseEvent.MOUSE_DOWN, pencilDown);
			pencilPoint.addEventListener(MouseEvent.MOUSE_UP, pencilUp);
		
			//Points on the right
			pencilPoint = new PencilPoint(350,50);
			pencilHolder.addChild(pencilPoint);
			pencilPoint.addEventListener(MouseEvent.MOUSE_DOWN, pencilDown);
			pencilPoint.addEventListener(MouseEvent.MOUSE_UP, pencilUp);
			
			drawLine(new Event(Event.ENTER_FRAME));
			}
			//Add a button to turn off the anchorPoints and pencilPoints
			button = new Sprite();
			button.graphics.beginFill(0xFFCCAA);
			button.graphics.drawRoundRect(20,23,100,20,10);
			button.graphics.endFill();
			addChild(button);
			
			var hideText:TextField = new TextField();
			hideText.text = "Hide Anchors"
			hideText.x=25;
			hideText.y=25;
			hideText.height=18;
			hideText.selectable=false;
			addChild(hideText);
			hideText.addEventListener(MouseEvent.CLICK,hidePencils);
						
		}
		
		/**
		 * Add's a new point to the line at the clicked possition
		 */
		private function addAnchor(evt:MouseEvent):void
		{
			pencilPoint = new PencilPoint(evt.localX,evt.localY);
			var index:Number = parseInt(evt.target.name)+1;
			pencilHolder.addChildAt(pencilPoint, index);
			
			pencilPoint.addEventListener(MouseEvent.MOUSE_DOWN, pencilDown);
			pencilPoint.addEventListener(MouseEvent.MOUSE_UP, pencilUp);
			
			//Update the line
			drawLine(new Event(Event.ENTER_FRAME));
		}
		
		/**
		 * redraw's the line when the pencilPoint is changing possition
		 */
		private function pencilDown(evt:MouseEvent):void
		{
			this.addEventListener(Event.ENTER_FRAME, drawLine);
		}
		
		/**
		 * Stops redrawing the line
		 */
		private function pencilUp(evt:MouseEvent):void
		{
			//First make sure the line is redrawn
			this.dispatchEvent(new Event(Event.ENTER_FRAME));
			this.removeEventListener(Event.ENTER_FRAME, drawLine);
		}
		
		/**
		 * Draw's a bezierCurved line onEnterFrame while a pencilpoint is clicked
		 * 
		 * @todo	each line will be updated during this function. update only line's that might be changed so you can boost performance
		 * 
		 */
		private function drawLine(evt:Event):void
		{
			//Remove all excisting lines
			removeAllLines(lineHolder);

			var next:Boolean;
			var anchorChildren:Number=pencilHolder.numChildren;
			
			//Update all lines between anchor points (and skip the last point cause there's no line to draw from that point)
			//@todo		don't do this for all anchor points
			for (var i:Number=0; i<anchorChildren-1; i++){
 				//Make reference holders so we don't have to type to much when we need them
 				var anchor:Sprite;
				var control:Sprite;
				var nextAnchor:Sprite;
				var nextControl:Sprite;

				
				//Store easy references
				anchor = Sprite(Sprite(pencilHolder.getChildAt(i)).getChildByName("anchor"));
				control = Sprite(Sprite(pencilHolder.getChildAt(i)).getChildByName("point2"));
				nextAnchor = Sprite(Sprite(pencilHolder.getChildAt(i+1)).getChildByName("anchor"));
				nextControl = Sprite(Sprite(pencilHolder.getChildAt(i+1)).getChildByName("point1"));
				
				
				//Draw the curve
				bezierLine = new CubicBezier(anchor,nextAnchor,control,nextControl);
				line = new Sprite();
				line.addChild(bezierLine.draw());
				
				//Give curve a name, so we can use it later to determine which curve was clicked and needs to be subdivided
				line.name=""+i;
				
				//Add curve to the stage
				lineHolder.addChild(line);
				line.addEventListener(MouseEvent.CLICK, addAnchor);
				 
			}

			
		}
		
		/**
		 * removeAllLines in a container and clear up it's listeners
		 */
		private function removeAllLines(_sprite:Sprite):void
		{
			while (_sprite.numChildren > 0){
				_sprite.getChildAt(_sprite.numChildren-1).removeEventListener(MouseEvent.CLICK, addAnchor);
				_sprite.removeChildAt(_sprite.numChildren-1);
				
			}
		}
		
		/**
		 * Make pencilPoints invisible
		 * 
		 * @todo	remove eventlisteners / disable pencils while they are invisible
		 */
		private function hidePencils(evt:MouseEvent):void
		{
			//Alpha is 1 or 0, in other words true or false, so inverse it's state
			pencilHolder.alpha=Number(!pencilHolder.alpha);
		}
		
	}
}


	import flash.display.Sprite;
	
	class Point extends Sprite
	{	
		private var point:Sprite;
		
		/**
		 * Draws a simple point on a given possition with a given radius and color
		 * 
		 * @todo	get rid of setting point.x but use this.x
		 */
		public function Point(_x:Number=0, _y:Number=0, _radius:Number=2, _color:uint=0x00FFFF){
			super();
			point =  new Sprite();
			point.graphics.beginFill(_color);
			point.graphics.drawCircle(0,0,_radius);
			point.x=_x;
			point.y=_y;
			addChild(point);
		}
		
		public override function set x(_x:Number):void
		{
			point.x = _x;
		}
		
		public override function set y(_y:Number):void
		{
			point.y = _y;
		}
		
		public override function get x():Number
		{
			return point.x;
		}
		
		public override function get y():Number
		{
			return point.y;
		}
	}

	

	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	class PencilPoint extends Sprite
	{	
		private var anchorPoint		:Sprite;
		private var controlPoint1	:Sprite;
		private var controlPoint2	:Sprite;
		private var line			:Shape;
		
		private var moveControlPoint			:Boolean=false;
		private var moveControlPoint1XDistance	:Number;
		private var moveControlPoint1YDistance	:Number;
		private var moveControlPoint2XDistance	:Number;
		private var moveControlPoint2YDistance	:Number;
		
		private var rotationPoint	:Sprite;
		
		/**
		 * Constructor
		 * 
		 * Draw a new pencil point on a given possition.
		 * Each pencil point contains 1 anchorPoint and 2 controlPoints
		 * 
		 * @note	The new Point(); that's used in this function is a custom written class, so don't mix this up with the default way to make a new Point
		 */
		public function PencilPoint(_x:Number=0, _y:Number=0){
			super();
			
			//Create a new anchorPoint
			anchorPoint = new Point(_x,_y,4,0xFFFF00);
			//Give it a name so we can access it by name
			anchorPoint.name = "anchor";
			addChild(anchorPoint);
			//Add eventListeners to make them dragable
			anchorPoint.addEventListener(MouseEvent.MOUSE_DOWN,startMove);
			anchorPoint.addEventListener(MouseEvent.MOUSE_UP,stopMove);
			
			
			/**
			 * Repeate the abbove for two controlPoints
			 */
			 
			controlPoint1 = new Point(_x,_y);
			controlPoint1.name = "point1";
			addChild(controlPoint1);
			controlPoint1.addEventListener(MouseEvent.MOUSE_DOWN,startMove);
			controlPoint1.addEventListener(MouseEvent.MOUSE_UP,stopMove);
			
			controlPoint2 = new Point(_x,_y);
			controlPoint2.name = "point2";
			addChild(controlPoint2);
			controlPoint2.addEventListener(MouseEvent.MOUSE_DOWN,startMove);
			controlPoint2.addEventListener(MouseEvent.MOUSE_UP,stopMove);
			
		}
		
		/**
		 * Called by an eventListener which listens if a anchorPoint or a controlPoint is pressed
		 * 
		 * @todo	add a seperate listener when an anchor is pressed
		 */
		private function startMove(evt:MouseEvent):void
		{
			if(evt.target.parent.name == "anchor"){
				/**
				 * When the anchor starts to drag, set these settings so controlpoint will follow it's movements 
				 * You can do this by storing distance between anchorPoint and controlpoint
				 * This will be used later on at the enterFrame
				 */
				moveControlPoint = true;
				moveControlPoint1XDistance  = controlPoint1.x - anchorPoint.x;
				moveControlPoint1YDistance 	= controlPoint1.y - anchorPoint.y;
				moveControlPoint2XDistance  = controlPoint2.x - anchorPoint.x;
				moveControlPoint2YDistance 	= controlPoint2.y - anchorPoint.y;
			}
			
			//Reference the point which is moving so we can access this onEnterFrame
			rotationPoint = Sprite(evt.target);
			
			//Start dragging the point
			Sprite(evt.target).startDrag();
			
			//Listen onEnterFrame while dragging
			this.addEventListener(Event.ENTER_FRAME, drawLine);
		}
		
		/**
		 * 	Stop dragging and stop listeners
		 */
		private function stopMove(evt:MouseEvent):void
		{
			if(evt.target.parent.name == "anchor")			moveControlPoint = false;
			
			Sprite(evt.target).stopDrag();
			//Prevent there wasn't a new ENTER_FRAME fast enough to update the line to the current positions
			this.dispatchEvent(new Event(Event.ENTER_FRAME));
			this.removeEventListener(Event.ENTER_FRAME, drawLine);
		}
		
		/**
		 * - Draws a line from the each controlPoint to the anchorPoint
		 * - Rotates the opposite controlPoint, so they keep exactly in each others opposites possition
		 * - moves control points when the anchorPoint is moving
		 * 
		 * @todo	make an seperate listener function to update a moving anchorPoint
		 */
		private function drawLine(evt:Event):void
		{
			//Only remove lines, not the anchor points which are in the same container (this)
			if(this.numChildren>3)		removeChildAt(0);
			
			if(moveControlPoint){
				//Track movements of an anchor
				controlPoint1.x = anchorPoint.x + moveControlPoint1XDistance;
				controlPoint1.y = anchorPoint.y + moveControlPoint1YDistance;
				controlPoint2.x = anchorPoint.x + moveControlPoint2XDistance;
				controlPoint2.y = anchorPoint.y + moveControlPoint2YDistance;
			}
			
			var radius:Number;
			var rotation:Number;

			if(rotationPoint.parent.name=="point1"){
				//Get radius and rotation
				radius = getRadius(controlPoint1,anchorPoint);
				rotation = getRotation(controlPoint1,anchorPoint);
				
				//Rotate the opposite point
				setRotation(controlPoint2,anchorPoint,rotation+90);
			} else if(rotationPoint.parent.name=="point2"){
				//Get radius and totation
				radius = getRadius(controlPoint2,anchorPoint);
				rotation = getRotation(controlPoint2,anchorPoint);
				
				//Rotate opposite point
				setRotation(controlPoint1,anchorPoint,rotation+90);
			}
			
			//Draw line between controlPoints and the central anchorPoint
			line = new Shape();
			line.graphics.lineStyle(1,0xFF3333);
			line.graphics.moveTo(controlPoint1.x,controlPoint1.y);
			line.graphics.lineTo(anchorPoint.x,anchorPoint.y);
			line.graphics.lineTo(controlPoint2.x,controlPoint2.y);
			addChildAt(line,0);
			
		}
		
		/**
		 * Calculates the radius of point A, compared with pointB
		 * 
		 * @todo	This function should be added to a custom Math class
		 */
		private function getRadius(pointA:Sprite,pointB:Sprite):Number
		{
			var width:Number;
			var height:Number;
			
			if(pointA.x>pointB.x){
				width  = pointA.x - pointB.x;
			} else {
				width  = pointB.x - pointA.x;
			}
			
			if(pointA.y>pointB.y){
				height  = pointA.y - pointB.y;
			} else {
				height  = pointB.y - pointA.y;
			}
			
			return Math.sqrt(Math.pow(width,2) + Math.pow(height,2) );
		}
		
		/**
		 * Calculates the rotation between a point (controlPoint) and the centerPoint (anchorPoint)
		 * 
		 * @todo	This function should be added to a custom Math class
		 */
		private function getRotation(point:Sprite, centerPoint:Sprite):Number
		{
			var widthA:Number;
			var widthB:Number;
			var rotation:Number;
			
			if(centerPoint.x>point.x){
				widthA = centerPoint.x-point.x;
			} else {
				widthA = point.x-centerPoint.x;
			}
			
			if(centerPoint.y>point.y){
				widthB = centerPoint.y-point.y;
			} else {
				widthB = point.y-centerPoint.y;
			}
			
			//Rotation between 0 - 90
			rotation = Math.atan(widthA/widthB) * (180/Math.PI);
			
			//Calculate the real rotation depending in which quadrant the point is possitioned 
			if(point.y >= centerPoint.y && point.x >= centerPoint.x){
				rotation = 90 + ( 90 - rotation );
			} else if (point.y >= centerPoint.y && point.x <= centerPoint.x){
				rotation =180 + rotation;
			} else if(point.y <= centerPoint.y && point.x <= centerPoint.x){
				rotation= 270 + ( 90 - rotation );
			}
			
			//Be sure there's a possitive rotation
			if(!rotation > 0){
				rotation = 0;
			}
			 
			return rotation;
		}
		
		/**
		 * Rotates a point compared to a centerPoint (anchorPoint) with a given angle
		 */
		private function setRotation(point:Sprite, centerPoint:Sprite, angle:Number):void
		{
			angle=angle*Math.PI/180;
			var radius:Number = getRadius(point,centerPoint);
			
			point.x = Math.cos(angle) * radius + centerPoint.x;
			point.y = Math.sin(angle) * radius + centerPoint.y;
		}
	}

	

	
	
	import flash.display.Shape;
	import flash.display.Sprite;
	class CubicBezier
	{
		// Default values
		private var segments:Number = 100;
		private var color:uint 		= 0x000000; 
		private var thikness:Number = 2;
		
		//Reference objects
		private var anchor1:Sprite;
		private var anchor2:Sprite;
		private var control1:Sprite;
		private var control2:Sprite;
		
		/**
		 * Constructor
		 * 
		 * used to store references to anchor points and control points
		 */
		public function CubicBezier(anchorPoint1:Sprite,anchorPoint2:Sprite,controlPoint1:Sprite,controlPoint2:Sprite)
		{
			anchor1=anchorPoint1;
			anchor2=anchorPoint2;
			control1=controlPoint1;
			control2=controlPoint2;
		}
		
		/**
		 * Draws a bezier curve by dividing it up in the predefined segments and draws a line between them
		 * 
		 * @todo	find a way to use less segments and use curveTo to draw the line
		 */
		public function draw():Shape
		{
			var step:Number = 1/segments;
			var line:Shape = new Shape();
			line.graphics.lineStyle(thikness,color);
			line.graphics.moveTo(anchor1.x,anchor1.y);
			
			var posx:Number;
			var posy:Number;
			
			//This loops draws each step of the curve
			for (var u:Number = 0; u <= 1; u += step) { 
				posx = Math.pow(u,3)*(anchor2.x+3*(control1.x-control2.x)-anchor1.x)+3*Math.pow(u,2)*(anchor1.x-2*control1.x+control2.x)+3*u*(control1.x-anchor1.x)+anchor1.x;
				posy = Math.pow(u,3)*(anchor2.y+3*(control1.y-control2.y)-anchor1.y)+3*Math.pow(u,2)*(anchor1.y-2*control1.y+control2.y)+3*u*(control1.y-anchor1.y)+anchor1.y;
				line.graphics.lineTo(posx,posy);
			} 
			
			//As a final step, make sure the curve ends on the second anchor
			line.graphics.lineTo(anchor2.x,anchor2.y);
			
			return line;
		}
		
		/**
		 *	Set number of segments
		 */
		public function setSegments(_segments:Number):void
		{
			segments=_segments;
		}
		
		/**
		 * 	Set color
		 */
		public function setColor(_color:uint):void
		{
			color=_color;
		}
		
		/**
		 * 	Set thikness
		 */
		public function setThikness(_thikness:Number):void
		{
			thikness=_thikness;
		}
	}