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

線描速度の推定

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

// forked from knd's 曲線による補間
package 
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.utils.getTimer;
    
    /**
     * 補間して曲線を引きます
     * 任意の時点の速度が推定できるので、筆圧のような効果を出してます。
     * @author @kndys
     */
    [SWF(frameRate="12")]
    public class Main extends Sprite 
    {
        
        public function Main():void 
        {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
            var curve:Curve = new Curve();
            var lastX:Number = mouseX, lastY:Number = mouseY;
            var lastT:int;
            var currX:Number = lastX, currY:Number = lastY;
            var currT:int;
            var press:Boolean = false;
            var lineNum:uint = 10;
            var position:Point = new Point();
            var velocity:Point = new Point();
            var draw:Function = function():void
            {
                graphics.lineStyle(0, 0x110022);
                if (curve.getPoint(position, 0.0))
                {
                    graphics.moveTo(position.x, position.y);
                    for (var u:uint = 1; u <= lineNum; u++)
                    {
                        var ratio:Number = Number(u) / lineNum;
                        curve.getPoint(position, ratio);
                        curve.getVelocity(velocity, ratio);
                        graphics.lineStyle(velocity.length * 5.0);
                        graphics.lineTo(position.x, position.y);
                    }
                }
            };
            // MOUSE_MOVE ではなく ENTER_FRAME を使う。
            stage.addEventListener(Event.ENTER_FRAME, 
            function(e:Event):void 
            { 
                currX = mouseX, currY = mouseY, currT = getTimer();;
                if (press)
                {
                    graphics.lineStyle(0, 0x4488ff, 0.5);
                    graphics.moveTo(lastX, lastY);
                    graphics.lineTo(currX, currY);
                    graphics.drawCircle(currX, currY, 5);
                    
                    curve.push(currX, currY, currT); // 点(x,y,t)を追加
                    
                    draw();
                }
                lastX = currX, lastY = currY, lastT = currT;
            } );
                
            stage.addEventListener(MouseEvent.MOUSE_DOWN, 
            function (e:Event):void 
            {
                if (!press)
                {
                    currX = mouseX, currY = mouseY, currT = getTimer();;
                    graphics.lineStyle(0, 0x4488ff, 0.5);
                    graphics.drawCircle(currX, currY, 5);
                    curve.push(lastX, lastY, lastT); // 開始点の速度を推測するためのダミー点
                    curve.push(currX, currY, currT); // 開始点
                    lastX = currX, lastY = currY, lastT = currT;
                    press = true;
                }
            } );
                
            stage.addEventListener(MouseEvent.MOUSE_UP, 
            function (e:Event):void 
            {
                if (press)
                {
                    currX = mouseX, currY = mouseY, currT = getTimer();;
                    curve.push(currX, currY, currT); //
                    draw();
                    curve.reset();
                    lastX = currX, lastY = currY, lastT = currT;
                    press = false;
                }
            } );
        }
    }
}

    import flash.geom.Point;
    /**
     * 動点にはたらく加速度が時間tの1次関数になっているという前提のもと
     * 離散点(x,y,t)間を曲線で補間する方法を思いつきました
     * どうせ車輪の再発明でしょうけど
     * @author @kndys
     */
    internal class Curve 
    {
        // (x,y,t) 3点と 初速(u0,v0) から曲線の式を求める
        private var _x0:Number, _y0:Number,
            _x1:Number, _y1:Number,
            _x2:Number, _y2:Number;
        private var _t0:Number, _t1:Number, _t2:Number;
        private var _u0:Number, _v0:Number;
        
        // dt = t - t0 として
        // 位置を x(dt) = ax*dt^3 + bx*dt^2 + u0*dt + x0
        // 速度を u(dt) = 3*ax*dt^2 + 2*bx*dt + u0;
        // という関数でフィッティングする
        private var _ax:Number, _bx:Number,
            _ay:Number, _by:Number;
        // 上記係数が求まると次回計算の初速も求まる
        private var _u1:Number, _v1:Number;
        
        // pushされた点の数
        private var _sampleNum:uint;
            
        public function Curve() 
        {
            reset();
        }
        
        public function push(x:Number, y:Number, t:int):void
        {
            _x0 = _x1;
            _x1 = _x2;
            _x2 =  x;
            _y0 = _y1;
            _y1 = _y2;
            _y2 =  y;
            _t0 = _t1;
            _t1 = _t2;
            _t2 =  t;
            _sampleNum++;
            
            if (_sampleNum < 3) return; // 3点目までは計算しない
            
            _u0 = _u1;
            _v0 = _v1;
            
            var dx1:Number = _x1 - _x0;
            var dx2:Number = _x2 - _x0;
            var dy1:Number = _y1 - _y0;
            var dy2:Number = _y2 - _y0;
            var dt1:Number = _t1 - _t0;
            var dt2:Number = _t2 - _t0;
            if (dt2 == 0) dt2 = 1.0;
            if (dt1 == 0) dt1 = 0.5 * dt2;
            var k1:Number = 1.0 / (dt1 * dt1 * (dt1 - dt2)); 
            var k2:Number = 1.0 / (dt2 * dt2 * (dt2 - dt1)); 
            
            if (_sampleNum == 3)
            {
                _u0 = 0.0;
                _v0 = 0.0;
            }
            
            var p1:Number = dx1 - _u0 * dt1;
            var p2:Number = dx2 - _u0 * dt2;
            var q1:Number = dy1 - _v0 * dt1;
            var q2:Number = dy2 - _v0 * dt2;
            _ax = p1 * k1 + p2 * k2;
            _ay = q1 * k1 + q2 * k2;
            _bx = - p1 * k1 * dt2 - p2 * k2 * dt1;
            _by = - q1 * k1 * dt2 - q2 * k2 * dt1;
            // 次回の初速を求めておく
            _u1 = 3 * _ax * dt1 * dt1 + 2 * _bx * dt1 + _u0;
            _v1 = 3 * _ay * dt1 * dt1 + 2 * _by * dt1 + _v0;
        }
        
        public function getPoint(dst:Point, ratio:Number):Boolean
        {
            if (_sampleNum < 3) return false;
            _calcPoint(dst, _calcTime(ratio));
            return true;
        }
        
        public function getVelocity(dst:Point, ratio:Number):Boolean
        {
            if (_sampleNum < 3) return false;
            _calcVelocity(dst, _calcTime(ratio));
            return true;
        }
        
        private function _calcTime(ratio:Number):Number
        {
            var dt:Number;
            if (ratio <= 1.0)
            {
                dt = (_t1 - _t0) * ratio;
            }
            else
            {
                dt = _t1 + (_t2 - _t1) * (ratio - 1.0);
            }
            return dt;
        }
        
        private function _calcPoint(dst:Point, dt:Number):void
        {
            var dt2:Number = dt * dt;
            var dt3:Number = dt2 * dt;
            dst.x = _ax * dt3 + _bx * dt2 + _u0 * dt + _x0;
            dst.y = _ay * dt3 + _by * dt2 + _v0 * dt + _y0;
        }
        
        private function _calcVelocity(dst:Point, dt:Number):void
        {
            var dt2:Number = dt * dt;
            dst.x = 3 * _ax * dt2 + 2 * _bx * dt + _u0;
            dst.y = 3 * _ay * dt2 + 2 * _by * dt + _v0;
        }
        
        //public function draw(g:Graphics):void
        //{
            //if (_sampleNum < 3) return; // 線が引けない
            //
            //var dx1:Number = _x1 - _x0;
            //var dy1:Number = _y1 - _y0;
            //var dt1:Number = _t1 - _t0;
            //
            // 曲線の分割数 直線距離ピクセル数の半分とした
            //var n:int = (0.5 * Math.sqrt(dx1 * dx1 + dy1 * dy1) | 0) + 1;
            //
            //g.moveTo(_x0, _y0);
            //for (var i:int = 1; i <= n; i++)
            //{
                //var dti:Number = dt1 * i / n;
                //var dti2:Number = dti * dti;
                //var dti3:Number = dti2 * dti;
                //var xt:Number = _ax * dti3 + _bx * dti2 + _u0 * dti + _x0;
                //var yt:Number = _ay * dti3 + _by * dti2 + _v0 * dti + _y0;
                //g.lineTo(xt, yt);
            //}
            //
        //}
        //
        //public function drawEnd(g:Graphics):void
        //{
            //if (_sampleNum < 2) return;
            //else if (_sampleNum == 2)
            //{
                //g.moveTo(_x0, _y0);
                //g.lineTo(_x1, _y1);
                //return;
            //}
            //
            // 最後の1点まで線を引くために、前回計算した係数の値をそのまま利用する。
            //var dx:Number = _x2 - _x1;
            //var dy:Number = _y2 - _y1;
            //var dt10:Number = _t1 - _t0;
            //var dt21:Number = _t2 - _t1;
            //var n:int = (0.5 * Math.sqrt(dx * dx + dy * dy) | 0) + 1;
            //g.moveTo(_x1, _y1);
            //for (var i:int = 1; i <= n; i++)
            //{
                //var dti:Number = dt10 + (dt21 * i / n);
                //var dti2:Number = dti * dti;
                //var dti3:Number = dti2 * dti;
                //var xt:Number = _ax * dti3 + _bx * dti2 + _u0 * dti + _x0;
                //var yt:Number = _ay * dti3 + _by * dti2 + _v0 * dti + _y0;
                //g.lineTo(xt, yt);
            //}
        //}
        
        public function reset():void
        {
            _sampleNum = 0;
        }
    }