Flex4でマウス座標から3次ベジエ曲線を描く

by etorov
マウス座標で制御点を決めて3次ベジエ曲線を描く。
Spriteの扱い方, addChild, Bindableとか勉強になった。
♥0 | Line 96 | Modified 2011-01-29 13:28:28 | MIT License
play

ActionScript3 source code

/**
 * Copyright etorov ( http://wonderfl.net/user/etorov )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/xjXe
 */

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
               xmlns:s="library://ns.adobe.com/flex/spark" 
               xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
    <fx:Script>
        <![CDATA[
        /* 4点を指定してベジエ曲線を描くサンプル
         * OpenGL/GLUT のソースコードがベースでdisplay()で消して描くを繰り返してます。ので(?)すごく重い。。。
         * メモ:
         *    * AS3での円のプロットの仕方 => beginFill()->drawCircle->endFill (参照: getCircle())
         *    * FlexでのSpriteのaddChildの仕方 => <s:SpriteVisualElement> (参照: myCanvas)
         *    * 複数のSpriteの管理の仕方 => Vector.<Sprite>をメンバ変数で。 (参照: inputDot, PlotDot)
         *    * Bindableの使い方 => バインド用変数を用意・[Bindable]を変数定義の前に付けて、変更するときにバインド用
         *      変数を更新。コンポーネントでは{}で囲って使う (参照: currentInputDotNum, currentStartPointPos)
         */
         
        /* 定義 */
            /* マウスの座標を入れるベクトルと、その座標にプロットする点を入れるベクトル */
            private var pt:Vector.<Point>=new Vector.<Point>();
            private var inputDot:Vector.<Sprite>=new Vector.<Sprite>();
            [Bindable]
            private var currentInputDotNum:int=pt.length;

            /* 最初の制御点の番号 */
            private var startPoint:int=0;
            [Bindable]
            private var currentStartPointPos:int=startPoint;
            
            /* 線となる点を入れるベクトル。座標はdisplay()が呼び出されたときに計算して決める */
            private var plotDot:Vector.<Sprite>=new Vector.<Sprite>();
        
        /* 道具 */
            /* 点を返す関数 */
            private function getCircle(radius:int):Sprite{
                var sp:Sprite = new Sprite;
                var g:Graphics = sp.graphics;
                
                g.beginFill(0);
                g.drawCircle(0, 0, radius);
                g.endFill();
                
                return sp;
            }
            
         /* 表示用関数 */
            protected function display():void
            {
                var i:int;
                /* display()が呼び出されるたびに座標のベクトルが初期化される */
                var c:Vector.<Point>=new Vector.<Point>();
                
                /* 1. 今までの制御点のプロットを消す */
                for(i=0; i<inputDot.length; i++){
                    myCanvas.removeChild(inputDot[i]);
                }
                
                /* 2. 制御点の座標(マウスで今まで指定してきたもの)に点をプロットするように準備する */
                for(i=0; i<pt.length;i++){
                    var sp:Sprite=getCircle(10);
                    sp.x=pt[i].x; sp.y=pt[i].y;
                    inputDot.push(sp);
                }
                
                /* 3. 制御点をプロット */
                for(i=0; i<inputDot.length; i++){
                    myCanvas.addChild(inputDot[i]);
                }
                
                if(inputDot.length>3 && ((inputDot.length-1)%3)==0){
                    /* 1. 今までプロットした線を消す */
                    for(i=0; i<plotDot.length; i++){
                        myCanvas.removeChild(plotDot[i]);
                    }
                    
                    /* 2. プロットする点の座標を設定 */
                    for(var t:Number=0; t<1.0; t+=0.001){
                        /* 4点から3次のベジエ曲線の座標を計算する */
                        c.push(
                            new Point(
                                Math.pow((1-t),3)*pt[startPoint].x + 3*t*Math.pow((1-t), 2)*pt[startPoint+1].x + 3*(1-t)*Math.pow(t, 2)*pt[startPoint+2].x + Math.pow(t, 3)*pt[startPoint+3].x,
                                Math.pow((1-t),3)*pt[startPoint].y + 3*t*Math.pow((1-t), 2)*pt[startPoint+1].y + 3*(1-t)*Math.pow(t, 2)*pt[startPoint+2].y + Math.pow(t, 3)*pt[startPoint+3].y
                            ));
                    }
                    startPoint+=3;
                    currentStartPointPos=startPoint;
                    
                    /* 3. 点のプロットの準備 */
                    for(i=0; i<c.length; i++){
                        var sp2:Sprite=getCircle(2);
                        sp2.x=c[i].x; sp2.y=c[i].y;
                        plotDot.push(sp2);
                    }
                    
                    /* 4. 点をプロット */
                    for(i=0; i<plotDot.length; i++){
                        myCanvas.addChild(plotDot[i]);
                    }
                }
            }
            
        /* リアクションする関数 */
            protected function drawArea_clickHandler(event:MouseEvent):void
            {
                pt.push(new Point(event.localX,event.localY));
                currentInputDotNum=pt.length;
                display();
            }
            
            protected function clear_clickHandler(event:MouseEvent):void
            {
                var i:int=0;
                
                /* 制御点を消す */
                for(i=0;i<inputDot.length;i++){
                    myCanvas.removeChild(inputDot[i]);
                }
                inputDot=new Vector.<Sprite>();
                currentInputDotNum=inputDot.length;
                /* 制御点の座標を初期化 */
                pt=new Vector.<Point>();
                
                /* 開始制御点を0番目にする */
                startPoint=0;
                currentStartPointPos=startPoint;
                
                /* 線を消す */
                for(i=0;i<plotDot.length;i++){
                    myCanvas.removeChild(plotDot[i]);
                }
                plotDot=new Vector.<Sprite>();
                
                /* 線の座標を初期化 */
                /* for, if のすべての条件を満たさないので、displayでのベクトルcだけ初期化される */
                display();
            }
        ]]>
    </fx:Script>
    
    <s:VGroup x="10" y="10">
        <s:BorderContainer id="drawArea" height="400" width="400" click="drawArea_clickHandler(event)">
            <s:SpriteVisualElement id="myCanvas"/> <!-- SpriteVisualElement ではonClickイベントが送出されない? -->
        </s:BorderContainer>
        <s:HGroup>
            <s:Button label="クリア" id="clear" click="clear_clickHandler(event)"/>
            <s:Label id="currentInputDot" text="現在の制御点の数: {currentInputDotNum}" baselineShift="-5"/>
            <s:Label id="currentStartPoint" text="曲線の最初の制御点の番号: {currentStartPointPos+1}" baselineShift="-5"/>
        </s:HGroup>
    </s:VGroup>
</s:Application>