Beautifl - Flash Gallery

Thumbnail : Bezier force
Bezier force
nutsu 2010-04-02 MIT License

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

/*
Bezier fource.

2つのbezier曲線の近傍点との距離を加速度にして運動。
計算のところはざっくりなので注意。
これ見たらなんだかなつかしくて、むかしのやつをWonderflへ
http://wonderfl.net/code/22347143956fe1042cd37277dc2ef3ba8308cd87
*/
package {
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.geom.ColorTransform;
	import flash.geom.Rectangle;
	import frocessing.display.F5MovieClip2DBmp;	
	[SWF( width=465, height=465, frameRate=60, backgroundColor=0 )]
	public class BezierAccel extends F5MovieClip2DBmp{
		
		private var bezier1:QBezier;
		private var bezier2:QBezier;
		
		private var handle1a:Handle;
		private var handle1b:Handle;
		private var handle1c:Handle;
		private var handle2a:Handle;
		private var handle2b:Handle;
		private var handle2c:Handle;
		private var handlegc:Shape;
		private var now_editting:Boolean = false;
		private var edit_handle:Handle;
		private var stageArea:Rectangle = new Rectangle(0, 0, 465, 465);
		
		private var plots:Array;
		private var N:int = 50;
		
		public function BezierAccel(){
			super(false,0);
			
			plots = [];
			for( var i:int=0; i<N; i++ ){
				plots[i] = new Plot( random( stageArea.width ), random( stageArea.height) );
			}
		}
		
		public function setup():void {
			stroke( 255, 0.5 );
			noFill();
			//
			addChild( handlegc = new Shape() );
			addChild( handle1a = new Handle(startEdit,  75, 120) );
			addChild( handle1b = new Handle(startEdit, 212, 205) );
			addChild( handle1c = new Handle(startEdit, 350, 120) );
			addChild( handle2a = new Handle(startEdit,  75, 305) );
			addChild( handle2b = new Handle(startEdit, 212, 220) );
			addChild( handle2c = new Handle(startEdit, 350, 305) );
			bezier1 = new QBezier( handle1a.x, handle1a.y, handle1b.x, handle1b.y, handle1c.x, handle1c.y );
			bezier2 = new QBezier( handle2a.x, handle2a.y, handle2b.x, handle2b.y, handle2c.x, handle2c.y );
			updateBezier();
		}
		
		private var ct:ColorTransform = new ColorTransform(1, 1, 1, 1, -1, -1, -1);
		
		public function draw():void 
		{
			
			bitmapData.colorTransform( bitmapData.rect, ct );
			
			if ( now_editting )
				updateBezier();
			
			for ( var i:int = 0; i < N; i++ ) {
				var p:Plot  = plots[i];
				var px:Number = p.x;
				var py:Number = p.y;
				var t1:Number = nearestValue( bezier1, px, py );
				var t2:Number = nearestValue( bezier2, px, py );
				var ax:Number = bezier1.fx(t1) + bezier2.fx(t2) - 2 * px;
				var ay:Number = bezier1.fy(t1) + bezier2.fy(t2) - 2 * py;
				var ad:Number = mag( ax, ay );
				p.x += p.vx += 0.8 * ax / ad;
				p.y += p.vy += 0.8 * ay / ad;
				p.vx *= 0.995;
				p.vy *= 0.995;
				line( px, py, p.x, p.y );
			}
		}
		
		private function updateBezier():void {
			bezier1.setValues( handle1a.x, handle1a.y, handle1b.x, handle1b.y, handle1c.x, handle1c.y );
			bezier2.setValues( handle2a.x, handle2a.y, handle2b.x, handle2b.y, handle2c.x, handle2c.y );
            handlegc.graphics.clear();
            handlegc.graphics.lineStyle( 0, 0x666666 );
			handlegc.graphics.moveTo( bezier1.x0, bezier1.y0 );
			handlegc.graphics.lineTo( bezier1.cx, bezier1.cy );
			handlegc.graphics.lineTo( bezier1.x1, bezier1.y1 );
			handlegc.graphics.moveTo( bezier2.x0, bezier2.y0 );
			handlegc.graphics.lineTo( bezier2.cx, bezier2.cy );
			handlegc.graphics.lineTo( bezier2.x1, bezier2.y1 );
			handlegc.graphics.lineStyle( 0, 0x666666 );
			handlegc.graphics.moveTo( bezier1.x0, bezier1.y0 );
			handlegc.graphics.curveTo( bezier1.cx, bezier1.cy, bezier1.x1, bezier1.y1 );
			handlegc.graphics.moveTo( bezier2.x0, bezier2.y0 );
			handlegc.graphics.curveTo( bezier2.cx, bezier2.cy, bezier2.x1, bezier2.y1 );
		}
		
		private function startEdit( h:Handle ):void{
			now_editting = true;
			edit_handle = h;
			edit_handle.startDrag(false, stageArea );
		}
		
		public function mouseReleased():void {
			if( edit_handle!=null ){
				edit_handle.stopDrag();
			}
			now_editting = false;
			updateBezier();
		}
		
		private static var ERR_T:Number = 1e-5;
		
		private function nearestValue( b:QBezier, px:Number, py:Number ):Number
		{
			var rslt:Number;
			
			var x0:Number = b.x0 - px;
			var y0:Number = b.y0 - py;
			var cx:Number = b.cx - px;
			var cy:Number = b.cy - py;
			var x1:Number = b.x1 - px;
			var y1:Number = b.y1 - py;
			
			var xx:Number = x0 + x1;
			var yy:Number = y0 + y1;
			var cc:Number = cx*cx + cy*cy;
			
			var A:Number  = 4*( xx*xx + yy*yy + 4*cc - 4*cx*xx - 4*cy*yy );
			var B:Number  = -12*( x0*xx + y0*yy + 2*cc + (-3*x0-x1)*cx + (-3*y0-y1)*cy );
			var C:Number  = 4*( (3*x0+x1-6*cx)*x0 + (3*y0+y1-6*cy)*y0 + 2*cc );
			var D:Number  = -4*(y0*y0-cy*y0+x0*x0-cx*x0);
			
			var TA:Number = 27*A*A*D*D + (4*B*B*B-18*A*B*C)*D + 4*A*C*C*C - B*B*C*C;
			
			
			if( TA>0 ){
				TA = Math.sqrt(TA)/( 6*Math.sqrt(3)*A*A ) - (27*A*A*D - 9*A*B*C + 2*B*B*B )/(54*A*A*A);
				if( TA<0 ){
					TA = -Math.pow(-TA,1/3);
				}else{
					TA = Math.pow(TA,1/3);
				}
				rslt = TA - (3*A*C-B*B)/(9*A*A*TA) - B/(3*A);				
				if ( rslt < 0 ) return 0;
				else if ( rslt > 1 ) return 1;
				else return rslt;
			}else if( TA<0 ){
				var diff:Function = function(t:Number):Number{ return (A*t*t*t + B*t*t + C*t + D); };
				
				var tt0:Number = (-B - Math.sqrt(B*B-3*A*C))/(3*A);
				var tt1:Number = (-B + Math.sqrt(B*B-3*A*C))/(3*A);
				
				var t0:Number = 0;
				var t1:Number = 1.0;
				
				var ot0:Number;
				var ot1:Number;
				var dt:Number;
				
				if( tt0>0 && D<0 ){
					ot0 = 0.0;
					ot1 = tt0;
					t0  = (ot0 + ot1)/2;
					while( Math.abs( dt=A*t0*t0*t0 + B*t0*t0 + C*t0 + D )>ERR_T ){
						if( dt>0 ){
							ot1 = t0;
							t0  = (ot0 + ot1)/2;
						}else{
							ot0 = t0;
							t0  = (ot0 + ot1)/2;
						}
					}
				}
				if( tt1<1.0 && (A+B+C+D)>0 ){
					ot0 = tt1;
					ot1 = 1.0;
					t1  = (ot0 + ot1)/2;
					while( Math.abs( dt=A*t1*t1*t1 + B*t1*t1 + C*t1 + D )>ERR_T ){
						if( dt<0 ){
							ot0 = t1;
							t1  = (ot0 + ot1)/2;
						}else{
							ot1 = t1;
							t1  = (ot0 + ot1)/2;
						}
					}
				}
				var dv0:Number = mag( t0*t0*(x1+x0-2*cx)+t0*(2*cx-2*x0)+x0, t0*t0*(y1+y0-2*cy)+t0*(2*cy-2*y0)+y0 );
				var dv1:Number = mag( t1*t1*(x1+x0-2*cx)+t1*(2*cx-2*x0)+x0, t1*t1*(y1+y0-2*cy)+t1*(2*cy-2*y0)+y0 );
				return ( dv0 < dv1) ? t0 : t1;
			}else{
				return -D/C;
			}
		}
	}
	
}

class QBezier {
	public var x0:Number, y0:Number, cx:Number, cy:Number, x1:Number, y1:Number;
	public function QBezier( x0:Number, y0:Number, cx:Number, cy:Number, x1:Number, y1:Number ) {
		setValues( x0, y0, cx, cy, x1, y1 );
	}
	public function setValues( x0:Number, y0:Number, cx:Number, cy:Number, x1:Number, y1:Number ):void {
		this.x0 = x0; this.y0 = y0;
		this.cx = cx; this.cy = cy;
		this.x1 = x1; this.y1 = y1; 
	}
	public function fx( t:Number ):Number {
		return x0*(1-t)*(1-t) + 2*cx*t*(1-t) + x1*t*t;
	}
	public function fy( t:Number ):Number {
		return y0*(1-t)*(1-t) + 2*cy*t*(1-t) + y1*t*t;
	}
}

class Plot {
	public var x:Number, y:Number, vx:Number, vy:Number;
	public function Plot( x:Number, y:Number ) {
		this.x = x; this.y = y;
		vx = vy = 0;
	}
}

import flash.events.MouseEvent;
import flash.display.Sprite;

class Handle extends Sprite{
	private var callback:Function;
	public function Handle( mousedowncallback:Function, x:Number, y:Number ) {
		this.x = x;
		this.y = y;
		callback = mousedowncallback;
		graphics.beginFill( 0xFFFFFF );
		graphics.drawCircle( 0, 0, 4 );
		graphics.endFill();
		buttonMode = true;
		addEventListener( MouseEvent.MOUSE_DOWN, mousedown );
	}
	private function mousedown(e:MouseEvent):void {
		callback.apply( null, [this] );
	}
}