fc2ブログ

クォータニオンでベジエカーブ

2008-03-05 | 21:14

マウスドラッグでポイント移動できます。

クォータニオンのslerp(球面線形補間)でベジエカーブを求めています。

package{
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.utils.*;
    import flash.filters.DropShadowFilter;

    import org.papervision3d.core.math.*;

    [SWF(width="600", height="400",backgroundColor="0xffffff")]

        public class BezierWithQuaternion extends Sprite{
            private var balls:Array = [];
            private var RADIUS:int = 6;
            public function BezierWithQuaternion(){

                var points:Array = [
                {x:100,y:320},   
                {x:200,y:200},   
                {x:300, y:200},
                {x:400,y:320}];   
                for(var i:int = 0 ; i < points.length ; i++){
                    var b:Ball = new Ball(0xff0000, RADIUS);
                    balls.push(b);
                    addChild(b);
                    b.x = points[i].x;
                    b.y = points[i].y;

                    b.addEventListener( MouseEvent.MOUSE_DOWN, pickup );
                    b.addEventListener( MouseEvent.MOUSE_UP, place );
                    b.addEventListener( MouseEvent.MOUSE_MOVE, function(e:*):void{
                            cal();
                            });
                }

                cal();
            }

            public function pickup( event:MouseEvent ):void {
                // ドラッグ処理を開始して、影フィルターをつける
                event.target.startDrag( );
                event.target.filters = [ new DropShadowFilter( ) ];
                // 最前面に表示されるようにする
                setChildIndex( DisplayObject( event.target ), numChildren - 1 );
            }

            public function place( event:MouseEvent ):void {
                // ドラッグ処理を終了して影フィルターをオフ
                event.target.stopDrag( );
                event.target.filters = null;

                cal();
            }

            private function cal() :void{
                graphics.clear();

                var quaternions:Array = [];

                for each(var ball:Ball in balls) {
                    quaternions.push(new Quaternion(ball.x, ball.y, 0));
                }

                graphics.lineStyle(3,0xff, 0.7);
                graphics.moveTo(balls[0].x, balls[0].y);
                var nq0:Quaternion = quaternions[0]; 
                var nq1:Quaternion = quaternions[1]; 
                var nq2:Quaternion = quaternions[2]; 
                var nq3:Quaternion = quaternions[3]; 
                for(var t:Number = 0 ; t <= 1.0 ; t += 0.01){
                    var qc:Quaternion = curve(nq0, nq1, nq2, nq3, t);
                    graphics.lineTo(qc.x , qc.y );
                }
                graphics.lineTo(balls[3].x, balls[3].y);
            }

            private function curve(q00:Quaternion, q01:Quaternion, 
                    q02:Quaternion, q03:Quaternion, t:Number):Quaternion{

                var q10:Quaternion = slerp(q00, q01, t);
                var q11:Quaternion = slerp(q01, q02, t);
                var q12:Quaternion = slerp(q02, q03, t);
                var q20:Quaternion = slerp(q10, q11, t);
                var q21:Quaternion = slerp(q11, q12, t);

                return slerp(q20, q21, t);
            }

            private function slerp(q1:Quaternion, q2:Quaternion, t:Number):Quaternion{

                var nq1:Quaternion = new Quaternion(q1.x, q1.y, q1.z, q1.w);
                var nq2:Quaternion = new Quaternion(q2.x, q2.y, q2.z, q2.w);
                nq1.normalize();
                nq2.normalize();
                var dot:Number = Quaternion.dot(nq1,nq2);
                var angle:Number = Math.acos(dot);

                if(isNaN(angle) || Math.abs(dot) >= 1.0 || angle == 0){
                    return new Quaternion(q1.x, q1.y, q1.z, q1.w);
                }

                var t1:Number = Math.sin((1-t)*angle) / Math.sin(angle);
                var t2:Number = Math.sin(t*angle) / Math.sin(angle);

                var q:Quaternion =  new Quaternion(t1*q1.x + t2*q2.x, t1*q1.y + t2*q2.y, t1*q1.z + t2*q2.z,
                        t1*q1.w + t2*q2.w);

                return q;
            }
        }
}

import flash.display.*;

internal class Ball extends Sprite{
    public function Ball(color:int, radius:int){
        graphics.beginFill(color);
        graphics.drawCircle(0,0,radius);
        graphics.endFill();
    }
}
スポンサーサイト