Beautifl - Flash Gallery

Thumbnail : Particles on Bezier Curves
Particles on Bezier Curves
Blue_fox_and_red_grape 2011-05-01 MIT License

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

package {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.display.Graphics;
    [ SWF( frameRate = 60 ) ]
    public class ParticleTest extends Sprite {
        public function ParticleTest() {
            var sw:int = stage.stageWidth;
            var sh:int = stage.stageHeight;
            var balls:Vector.<Ball> = new <Ball>[]
            var curves:Vector.<Curve> = new <Curve>[new Curve, new Curve, new Curve, new Curve]
            var gravity:Number = 0.7
            
            stage.addEventListener("mouseDown",function():void{for each(var c:Curve in curves)c.reset()})
            
            addEventListener( Event.ENTER_FRAME, ENTER_FRAME )
            function ENTER_FRAME( e:Event ):void {
                balls.push(new Ball)
                var ball:Ball, curve:Curve
                graphics.clear()
                for each(curve in curves) { drawCurve(graphics, curve) }
                for( var i:int = 0 ; i < balls.length ; i++ ){
                    ball = balls[i]
                    ball.x += ball.vx , ball.y += ball.vy
                    
                    var t:Number, theT:Number=2, theCurve:Curve
                    for each(curve in curves){
                        t = curve.getT( ball.prevx, ball.prevy, ball.x, ball.y)
                        if(t < theT) { theT = t , theCurve = curve }
                    }
                    if( 0 <= theT && theT <= 1 ) {
                        ball.resolve(theCurve, theT)
                    }
                    if( ball.y>sh || ball.x<0 || ball.x>sw ) {
                        balls.splice(i, 1)
                    }
                    drawBall(graphics, ball)
                    ball.prevx = ball.x
                    ball.prevy = ball.y
                    ball.vy += gravity
                }
            }
            
            function drawBall(g:Graphics, b:Ball):void {
                g.lineStyle( 3, 0xFF0000 )
                g.moveTo( b.prevx, b.prevy )
                g.lineTo( b.x, b.y )
            }
            function drawCurve(g:Graphics, c:Curve):void {
                g.lineStyle( 3, 0x000000 )
                g.moveTo( c.p0x, c.p0y )
                g.curveTo( c.p1x, c.p1y, c.p2x, c.p2y )
            }
        }
    }
}
function linear( start:Number, end:Number, t:Number ):Number
{
    return start + ( end-start )*t;
}
function bezier( values:Array, t:Number ):Number
{
    if( values.length < 2 ) return NaN;
    if( values.length == 2 ) return linear( values[ 0 ], values[ 1 ], t );
    var postValues:Array = values.concat();
    var i:int;
    while( postValues.length > 1 )
    {
        var resultValues:Array = new Array;
        var count:int = postValues.length-1;
        for ( i = 0; i<count; ++i )
        {
            resultValues.push( linear( postValues[ i ], postValues[ i+1 ], t ) );
        }
        postValues = resultValues;
    }
    return postValues[ 0 ];
}
class Curve {
    public var p0x:Number, p0y:Number
    public var p1x:Number, p1y:Number
    public var p2x:Number, p2y:Number
    
    public function Curve() {
        reset()
    }
    
    public function reset():void {
        p0x = Math.random()*200 , p0y = Math.random()*100+150
        p1x = Math.random()*550 , p1y = Math.random()*400
        p2x = Math.random()*200+350 , p2y = Math.random()*100+150
    }
    
    // 0~1이면 충돌. NaN이면 비충돌
    public function getT(slx:Number,sly:Number, elx:Number,ely:Number):Number {
        // 선분이 수직일때 0으로 나누게 되는 문제를 편법으로 해결. 0.01을 더함
        if( slx == elx ) slx += 0.01
        var px:Number = p0x - 2*p1x + p2x, py:Number = p0y - 2*p1y + p2y
        var qx:Number = -2*p0x + 2*p1x, qy:Number = -2*p0y + 2*p1y
        var rx:Number = p0x, ry:Number = p0y
        var ax:Number = elx - slx, ay:Number = ely - sly
        var bx:Number = slx, by:Number = sly
        var X:Number = ay*px - ax*py
        var Y:Number = ay*qx - ax*qy
        var Z:Number = ay*rx - ay*bx - ax*ry + ax*by
        var d:Number = Y*Y - 4*X*Z
        if( d < 0 ) return NaN
        var t1:Number, s1:Number
        // d가 0일 경우는 거의 없다. 그래도 처리
        if( d == 0 ) {
            t1 = -Y/( 2*X )
            if( 0>t1 || 1<t1 ) {
                return NaN;
            } else {
                s1 = ( px*t1*t1 + qx*t1 + rx - bx )/ax
                if( 0<=s1 )if( s1<=1 ) return t1
                return NaN;
            }
        }
        var t2:Number, s2:Number
        var d2:Number = Math.sqrt( d )
        t1 = ( -Y + d2 )/( 2*X )
        t2 = ( -Y - d2 )/( 2*X )
        s1 = ( 0>t1 || 1<t1 )? 2 : ( px*t1*t1 + qx*t1 + rx - bx )/ax
        s2 = ( 0>t2 || 1<t2 )? 2 : ( px*t2*t2 + qx*t2 + rx - bx )/ax
        // 일반적인 처리
        if( s1 < s2 )if( 0<=s1 )if( s1<=1 ) return t1
        if( s2 < s1 )if( 0<=s2 )if( s2<=1 ) return t2
        // 둘중 하나가 음수일때 처리.
        // 베지어 곡선의 움푹한 부분에 선분이 들어가 있을 경우에 이런 상황이 발생한다.
        if( s1 < 0 )if( 0<=s2 )if( s2<=1 ) return t2
        if( s2 < 0 )if( 0<=s1 )if( s1<=1 ) return t1
        return NaN
    }

}
class Ball {
    public var prevx:Number, prevy:Number // 이전 위치
    public var x:Number, y:Number // 현재 위치
    public var vx:Number, vy:Number // 속도

    public function Ball() {
        reset()
    }
    
    public function resolve(curve:Curve, t:Number):void {
        with(curve){
            var dx:Number = (2*p0x - 4*p1x + 2*p2x)*t + (-2*p0x + 2*p1x)
            var dy:Number = (2*p0y - 4*p1y + 2*p2y)*t + (-2*p0y + 2*p1y)
            var d:Number = Math.sqrt(dx*dx + dy*dy)
            dx /= d , dy /= d
            var K:Number = 2 * (vx*dx + vy*dy)
            
            x = bezier( [ p0x, p1x, p2x ], t )
            y = bezier( [ p0y, p1y, p2y ], t )
            vx = (K*dx - vx) * 0.7 // 속력 감쇠
            vy = (K*dy - vy) * 0.7 // 속력 감쇠
            
            //x += dy , y += -dx // 보정
            if(vy < 0) { x -= -dy ; y -= dx } else x += dy , y += -dx
        }
    }
    
    public function reset():void {
        vx = Math.random()*10-5 , vy = Math.random()*10-5
        prevx = x = Math.random()*550
        prevy = y = 0
    }
}