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

ベジェを等分してアニメーション

バウンディングボックスの取得とMatrixの適用とパスの分割に対応させたので、再テストです。

1.ベジェパスを作成
2.境界を調べてMatrixを適用してセンターへ
3.パスを等間隔で分割
4.分割されたパスの境界を調べる
5.パスは(0,0)で始まるようにMatrixを適用
6.シェイプの(0,0)に描画してシェイプを元のパスの場所に移動
7.アニメーション

って事をやってます。
Get Adobe Flash player
by kacchan6 08 Jan 2011
package
{
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.filters.BlurFilter;
    import flash.filters.GlowFilter;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;

    [SWF(backgroundColor=0x000000, width=465, height=465, frameRate=60)]
    public class path_test2 extends Sprite
    {

        public function path_test2()
        {
            var path:Path = new Path();

            //イラレで作ったSVGから抜粋
            var data:XML = <data>
                    <path d="M16.781,41.216L4.71,55.643L0,50.932l7.948-8.831c3.925-4.515,6.868-8.538,8.833-12.07v-7.36L2.943,26.202l-1.765-6.184l15.604-2.942V0h5.298c2.158,0.395,2.355,1.181,0.589,2.355v12.071l4.416,2.649c1.96,0.982,1.667,2.063-0.883,3.238l-3.533,5.005v3.533c8.636-6.869,16.879-10.304,24.729-10.304c5.886,0,10.401,1.57,13.542,4.71c3.728,3.73,5.593,8.539,5.593,14.425c0,6.085-1.668,10.795-5.003,14.131c-4.32,4.32-10.699,8.049-19.137,11.188l-2.945-5.888c7.458-2.748,12.856-5.791,16.193-9.127c2.746-2.748,4.121-6.183,4.121-10.305c0-4.121-1.375-7.554-4.121-10.305c-2.16-2.157-5.105-3.237-8.833-3.237c-7.065,0-15.114,4.123-24.14,12.363v30.913h-5.888V41.216z"/>
                </data>;

            var x:Number = 0, y:Number = 0;
            for each (var p:XML in data.path)
            {
                var tokens:String = p.@d;
                for each (var token:String in tokens.match(/([cChHlLMVv][^a-z]+)|z/ig))
                {
                    var command:String = token.charAt(0);
                    var args:Array = token.substring(1).match(/[-0-9.][0-9.]*/g);
                    args.every(function():Boolean
                    {
                        args[arguments[1]] = Number(arguments[0]);
                        return true;
                    });

                    switch (command)
                    {
                        case "c":
                            path.curveTo(x + args[0], y + args[1], x + args[2], y + args[3], x + args[4], y + args[5]);
                            x += args[4];
                            y += args[5];
                            break;
                        case "C":
                            path.curveTo(args[0], args[1], args[2], args[3], args[4], args[5]);
                            x = args[4];
                            y = args[5];
                            break;
                        case "l":
                            path.lineTo(x + args[0], y + args[1]);
                            x += args[0];
                            y += args[1];
                            break;
                        case "L":
                            path.lineTo(args[0], args[1]);
                            x = args[0];
                            y = args[1];
                            break;
                        case "v":
                            path.lineTo(x, y + args[0]);
                            y += args[0];
                            break;
                        case "V":
                            path.lineTo(x, args[0]);
                            y = args[0];
                            break;
                        case "h":
                            path.lineTo(x + args[0], y);
                            x += args[0];
                            break;
                        case "H":
                            path.lineTo(args[0], y);
                            x = args[0];
                            break;
                        case "M":
                            path.moveTo(args[0], args[1]);
                            x = args[0];
                            y = args[1];
                            break;
                        case "z":
                            path.close();
                            break;
                    }
                }
            }

            //境界を取得してセンターへ移動
            var rect:Rectangle = new Rectangle();
            var matrix:Matrix = new Matrix();
            path.getBounds(rect);
            matrix.translate(rect.width / -2, rect.height / -2);
            matrix.scale(3, 3);
            matrix.translate(rect.width / 2, rect.height / 2);
            matrix.translate((465 - rect.width) / 2 - rect.x, (465 - rect.height) / 2 - rect.y);
            path.applyMatrix(matrix);

            //パスを分割してシェイプへ描画
            var paths:Array = [];
            var shapes:Array = [];
            for (var i:int = 1; i < 40; i++)
            {
                path.split(1 / (40 - (i - 1)), paths);
                draw(shapes, paths[0], rect, matrix);
                path = paths[1];
            }
            draw(shapes, path, rect, matrix);

            var r:int = 0;
            addEventListener(Event.ENTER_FRAME, function():void
            {
                r += 3;
                if (r === 360)
                {
                    r = 0;
                }

                for (var i:int = 0, len:uint = shapes.length; i < len; i++)
                {
                    var shape:Shape = shapes[i];
                    shape.rotation = (r / 360) * (r / 360) * 360;
                }
            });
        }

        private function draw(shapes:Array, path:Path, rect:Rectangle, matrix:Matrix):void
        {
            var shape:Shape = new Shape();
            shape.filters = [new GlowFilter(0xffffff, 0.5, 16, 16, 1), new BlurFilter()];
            addChild(shape);

            path.getBounds(rect);
            matrix.identity();
            matrix.translate(-rect.x, -rect.y);
            path.applyMatrix(matrix);
            shape.x = rect.x;
            shape.y = rect.y;

            shape.graphics.lineStyle(1.5, Math.random() * 0x1000000);
            path.debug(shape.graphics);
            shapes.push(shape);
        }
    }
}

//以下自作ライブラリから抜粋
//http://sweezy.googlecode.com/svn/trunk/sweezy-anim/src/sweezy/anim/util/Path.as
import flash.display.Graphics;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;

class Path
{

    private static const QUAD_TO_CUBIC_RATIO:Number = 2 / 3;

    private var _interpolate:uint;

    private var _lastX:Number;

    private var _lastY:Number;

    private var _open:Boolean;

    private var _pathInfos:Array;

    private var _totalLength:Number;

    private var _x:Number;

    private var _y:Number;

    public function Path(interpolate:uint = 0)
    {
        _x = 0;
        _y = 0;
        _lastX = 0;
        _lastY = 0;
        _pathInfos = [];
        _open = false;
        _interpolate = interpolate;
    }

    public function add(path:Path):void
    {
        if (path === null)
        {
            return;
        }

        add0(path, _pathInfos.concat(path._pathInfos));
    }

    public function applyMatrix(matrix:Matrix):void
    {
        if (matrix === null)
        {
            return;
        }

        var a:Number = matrix.a;
        var b:Number = matrix.b;
        var c:Number = matrix.c;
        var d:Number = matrix.d;
        var tx:Number = matrix.tx;
        var ty:Number = matrix.ty;
        var x:Number, y:Number, info:PathInfo;

        x = _x;
        y = _y;
        _x = x * a + y * c + tx;
        _y = x * b + y * d + ty;
        x = _lastX;
        y = _lastY;
        _lastX = x * a + y * c + tx;
        _lastY = x * b + y * d + ty;

        for (var i:int = 0, len:uint = _pathInfos.length; i < len; i++)
        {
            info = _pathInfos[i] as PathInfo;

            x = info.sx;
            y = info.sy;
            info.sx = x * a + y * c + tx;
            info.sy = x * b + y * d + ty;
            x = info.ex;
            y = info.ey;
            info.ex = x * a + y * c + tx;
            info.ey = x * b + y * d + ty;

            if (info.type !== 0)
            {
                x = info.cx1;
                y = info.cy1;
                info.cx1 = x * a + y * c + tx;
                info.cy1 = x * b + y * d + ty;
                x = info.cx2;
                y = info.cy2;
                info.cx2 = x * a + y * c + tx;
                info.cy2 = x * b + y * d + ty;
            }

            info.computeLength();
        }
    }

    public function close():void
    {
        if (!_open)
        {
            return;
        }
        if (_x === _lastX && _y === _lastY)
        {
            return;
        }

        lineTo(_lastX, _lastY);
    }

    public function curveTo(controlX1:Number, controlY1:Number, controlX2:Number, controlY2:Number, anchorX:Number, anchorY:Number):void
    {
        addPath(2, anchorX, anchorY, controlX1, controlY1, controlX2, controlY2);
    }

    public function debug(g:Graphics):void
    {

        var info:PathInfo;
        var point:Point = new Point();

        for (var i:int = 0, len:uint = _pathInfos.length; i < len; i++)
        {
            info = _pathInfos[i] as PathInfo;
            g.moveTo(info.sx, info.sy);

            if (info.type === 0)
            {
                g.lineTo(info.ex, info.ey);
            }
            else
            {
                for (var j:int = 0; j <= 20; j++)
                {
                    info.pointForT(j / 20, point);
                    g.lineTo(point.x, point.y);
                }
            }
        }
    }

    public function getBounds(rect:Rectangle):void
    {
        var info:PathInfo;
        var xBounds:Array = [];
        var yBounds:Array = [];

        for (var i:int = 0, len:uint = _pathInfos.length; i < len; i++)
        {
            info = _pathInfos[i] as PathInfo;
            info.getBounds(xBounds, yBounds);
        }

        trace(xBounds);
        trace(yBounds);
        rect.left = Math.min.apply(null, xBounds);
        rect.right = Math.max.apply(null, xBounds);
        rect.top = Math.min.apply(null, yBounds);
        rect.bottom = Math.max.apply(null, yBounds);
    }

    public function getLength():Number
    {
        if (isNaN(_totalLength))
        {
            _totalLength = 0;
            for (var i:int = 0, len:uint = _pathInfos.length; i < len; i++)
            {
                _totalLength += (_pathInfos[i] as PathInfo).len;
            }
        }

        return _totalLength;
    }

    public function lineTo(x:Number, y:Number):void
    {
        if (_x === x && _y === y)
        {
            return;
        }

        addPath(0, x, y, NaN, NaN, NaN, NaN);
    }

    public function moveTo(x:Number, y:Number):void
    {
        if (_x === x && _y === y)
        {
            return;
        }

        _x = x;
        _y = y;
        _lastX = x;
        _lastY = y;
    }

    public function pointForT(t:Number, point:Point):void
    {
        if (isNaN(t) || t < 0 || t > 1 || point === null)
        {
            return;
        }

        var target:Number = getLength() * t;
        var start:Number = 0;
        var end:Number = 0;
        var info:PathInfo;
        for (var i:int = 0, len:uint = _pathInfos.length; i < len; i++)
        {
            info = _pathInfos[i] as PathInfo;
            start = end;
            end += info.len;

            if (start <= target && target <= end)
            {
                break;
            }
        }

        info.pointForT(1 - (end - target) / info.len, point);
    }

    public function quadTo(controlX:Number, controlY:Number, anchorX:Number, anchorY:Number):void
    {
        addPath(1, anchorX, anchorY, controlX, controlY, NaN, NaN);
    }

    public function split(t:Number, array:Array):int
    {
        if (isNaN(t) || t < 0 || t > 1 || array === null)
        {
            return 0;
        }

        var target:Number = getLength() * t;
        var start:Number = 0;
        var end:Number = 0;
        var info:PathInfo;
        var index:int = 0;
        for (var i:int = 0, len:uint = _pathInfos.length; i < len; i++)
        {
            index = i;
            info = _pathInfos[i] as PathInfo;
            start = end;
            end += info.len;

            if (start <= target && target <= end)
            {
                break;
            }
        }

        t = 1 - (end - target) / info.len;
        var path0:Path, path1:Path;

        if (t === 0)
        {
            if (index === 0)
            {
                path0 = new Path();
                path0.add(this);
                array[0] = path0;
                return 1;
            }
            else
            {
                path0 = new Path();
                path0.add0(this, _pathInfos.slice(0, index));
                array[0] = path0;

                path1 = new Path();
                path1.add0(this, _pathInfos.slice(index));
                array[1] = path1;

                return 2;
            }
        }
        else if (t === 1)
        {
            if (index === _pathInfos.length - 1)
            {
                path0 = new Path();
                path0.add(this);
                array[0] = path0;
                return 1;
            }
            else
            {
                path0 = new Path();
                path0.add0(this, _pathInfos.slice(0, index));
                array[0] = path0;

                path1 = new Path();
                path1.add0(this, _pathInfos.slice(index));
                array[1] = path1;

                return 2;
            }
        }

        info.split(t, array);

        var a:Array = _pathInfos.slice(0, index);
        a.push(array[0]);
        path0 = new Path();
        path0.add0(this, a);

        a = _pathInfos.slice(index + 1);
        a.unshift(array[1]);
        path1 = new Path();
        path1.add0(this, a);

        array[0] = path0;
        array[1] = path1;

        return 2;
    }

    private function add0(path:Path, infos:Array):void
    {
        _pathInfos = infos;
        _totalLength = NaN;
        _x = path._x;
        _y = path._y;
        _lastX = path._lastX;
        _lastY = path._lastY;
        _open = path._open;
    }

    private function addPath(type:uint, endX:Number, endY:Number, cx1:Number, cy1:Number, cx2:Number, cy2:Number):void
    {
        var info:PathInfo = new PathInfo();
        info.type = type;
        info.sx = _x;
        info.sy = _y;
        info.ex = endX;
        info.ey = endY;
        info.interpolate = _interpolate === 0 ? 50 : _interpolate;

        if (type === 1)
        {
            info.cx1 = _x + (cx1 - _x) * QUAD_TO_CUBIC_RATIO;
            info.cy1 = _y + (cy1 - _y) * QUAD_TO_CUBIC_RATIO;
            info.cx2 = endX + (cx1 - endX) * QUAD_TO_CUBIC_RATIO;
            info.cy2 = endY + (cy1 - endY) * QUAD_TO_CUBIC_RATIO;
        }
        else
        {
            info.cx1 = cx1;
            info.cy1 = cy1;
            info.cx2 = cx2;
            info.cy2 = cy2;
        }

        info.computeLength();
        if (isNaN(info.len))
        {
            return;
        }

        _pathInfos[_pathInfos.length] = info;

        _x = endX;
        _y = endY;
        _open = !(_lastX === endX && _lastY === endY);
        _totalLength = NaN;
    }
}

class PathInfo
{

    public var cx1:Number;

    public var cx2:Number;

    public var cy1:Number;

    public var cy2:Number;

    public var ex:Number;

    public var ey:Number;

    public var interpolate:uint;

    public var len:Number;

    public var px:Number;

    public var py:Number;

    public var sx:Number;

    public var sy:Number;

    public var type:uint;

    public function computeLength():void
    {
        if (type === 0)
        {
            len = Math.sqrt((sx - ex) * (sx - ex) + (sy - ey) * (sy - ey));
        }
        else
        {
            len = getBezierLength(0, 1);
        }
    }

    public function getBounds(xBounds:Array, yBounds:Array):void
    {
        if (type === 0)
        {
            xBounds[xBounds.length] = sx;
            yBounds[yBounds.length] = sy;
            xBounds[xBounds.length] = ex;
            yBounds[yBounds.length] = ey;
        }
        else
        {
            xBounds[xBounds.length] = sx;
            yBounds[yBounds.length] = sy;
            xBounds[xBounds.length] = ex;
            yBounds[yBounds.length] = ey;

            var bx:Number = 6 * sx - 12 * cx1 + 6 * cx2;
            var by:Number = 6 * sy - 12 * cy1 + 6 * cy2;
            var ax:Number = -3 * sx + 9 * cx1 - 9 * cx2 + 3 * ex;
            var ay:Number = -3 * sy + 9 * cy1 - 9 * cx2 + 3 * ey;
            var cx:Number = 3 * cx1 - 3 * sx;
            var cy:Number = 3 * cy1 - 3 * sy;
            var t:Number;

            if (ax === 0)
            {
                if (bx !== 0)
                {
                    t = -cx / bx;
                    if (0 < t && t < 1)
                    {
                        bezierPointForT(t);
                        xBounds[xBounds.length] = px;
                    }
                }
            }
            else
            {
                var b2acx:Number = bx * bx - 4 * cx * ax;
                if (b2acx >= 0)
                {
                    t = (-bx + Math.sqrt(b2acx)) / (ax * 2);
                    if (0 < t && t < 1)
                    {
                        bezierPointForT(t);
                        xBounds[xBounds.length] = px;
                    }
                    t = (-bx - Math.sqrt(b2acx)) / (ax * 2);
                    if (0 < t && t < 1)
                    {
                        bezierPointForT(t);
                        xBounds[xBounds.length] = px;
                    }
                }
            }

            if (ay === 0)
            {
                if (by !== 0)
                {
                    t = -cy / by;
                    if (0 < t && t < 1)
                    {
                        bezierPointForT(t);
                        yBounds[yBounds.length] = py;
                    }
                }
            }
            else
            {
                var b2acy:Number = by * by - 4 * cy * ay;
                if (b2acy >= 0)
                {
                    t = (-by + Math.sqrt(b2acy)) / (ay * 2);
                    if (0 < t && t < 1)
                    {
                        bezierPointForT(t);
                        yBounds[yBounds.length] = py;
                    }
                    t = (-by - Math.sqrt(b2acy)) / (ay * 2);
                    if (0 < t && t < 1)
                    {
                        bezierPointForT(t);
                        yBounds[yBounds.length] = py;
                    }
                }
            }
        }
    }

    public function pointForT(t:Number, point:Point):void
    {
        if (type === 0)
        {
            point.x = sx + (ex - sx) * t;
            point.y = sy + (ey - sy) * t;
        }
        else
        {
            t = getCoordFromLength(len * t);
            bezierPointForT(t);
            point.x = px;
            point.y = py;
        }
    }

    public function split(t:Number, array:Array):void
    {
        var info0:PathInfo = new PathInfo();
        var info1:PathInfo = new PathInfo();

        info0.type = type;
        info1.type = type;
        info0.interpolate = interpolate;
        info1.interpolate = interpolate;
        info0.sx = sx;
        info1.ex = ex;
        info0.sy = sy;
        info1.ey = ey;

        if (type === 0)
        {
            info0.ex = info1.sx = sx + (ex - sx) * t;
            info0.ey = info1.sy = sy + (ey - sy) * t;
        }
        else
        {
            t = 1 - getCoordFromLength(len * t);
            var x:Number = cx2 + (cx1 - cx2) * t;
            var y:Number = cy2 + (cy1 - cy2) * t;
            info0.cx1 = cx1 + (sx - cx1) * t;
            info0.cy1 = cy1 + (sy - cy1) * t;
            info1.cx2 = ex + (cx2 - ex) * t;
            info1.cy2 = ey + (cy2 - ey) * t;
            info0.cx2 = x + (info0.cx1 - x) * t;
            info0.cy2 = y + (info0.cy1 - y) * t;
            info1.cx1 = info1.cx2 + (x - info1.cx2) * t;
            info1.cy1 = info1.cy2 + (y - info1.cy2) * t;
            info0.ex = info1.sx = info1.cx1 + (info0.cx2 - info1.cx1) * t;
            info0.ey = info1.sy = info1.cy1 + (info0.cy2 - info1.cy1) * t;
        }

        info0.computeLength();
        info1.computeLength();
        array[0] = info0;
        array[1] = info1;
    }

    private function bezierPointForT(t:Number):void
    {
        var x0:Number = cx1 - sx;
        var y0:Number = cy1 - sy;
        var x1:Number = cx2 - cx1 - x0;
        var y1:Number = cy2 - cy1 - y0;
        var x2:Number = ex - cx2 - x1 - cx2 + cx1;
        var y2:Number = ey - cy2 - y1 - cy2 + cy1;

        px = x2 * t * t * t + 3 * x1 * t * t + 3 * x0 * t + sx;
        py = y2 * t * t * t + 3 * y1 * t * t + 3 * y0 * t + sy;
    }

    private function getBezierLength(ta:Number, tb:Number):Number
    {
        var t:Number, x:Number, y:Number, ft:Number;
        var t2:Number = (tb - ta) / (2 * interpolate);
        var x0:Number = cx1 - sx;
        var y0:Number = cy1 - sy;
        var x1:Number = cx2 - cx1 - x0;
        var y1:Number = cy2 - cy1 - y0;
        var x2:Number = ex - cx2 - x1 - cx2 + cx1;
        var y2:Number = ey - cy2 - y1 - cy2 + cy1;

        x = 3 * (x2 * ta * ta + 2 * x1 * ta + x0);
        y = 3 * (y2 * ta * ta + 2 * y1 * ta + y0);
        ft = Math.sqrt(x * x + y * y);

        x = 3 * (x2 * tb * tb + 2 * x1 * tb + x0);
        y = 3 * (y2 * tb * tb + 2 * y1 * tb + y0);
        ft += Math.sqrt(x * x + y * y);

        for (var i:int = 1; i <= interpolate; i++)
        {
            t = ta + (2 * i - 1) * t2;
            x = 3 * (x2 * t * t + 2 * x1 * t + x0);
            y = 3 * (y2 * t * t + 2 * y1 * t + y0);
            ft += Math.sqrt(x * x + y * y) * 4;

            t = ta + 2 * i * t2;
            x = 3 * (x2 * t * t + 2 * x1 * t + x0);
            y = 3 * (y2 * t * t + 2 * y1 * t + y0);
            ft += i < interpolate ? Math.sqrt(x * x + y * y) * 2 : 0;
        }

        return t2 / 3 * ft;
    }

    private function getCoordFromLength(len:Number):Number
    {
        var x:Number, y:Number, v:Number, v2:Number;
        var t:Number = 1, d:Number = 0;
        var x0:Number = cx1 - sx;
        var y0:Number = cy1 - sy;
        var x1:Number = cx2 - cx1 - x0;
        var y1:Number = cy2 - cy1 - y0;
        var x2:Number = ex - cx2 - x1 - cx2 + cx1;
        var y2:Number = ey - cy2 - y1 - cy2 + cy1;

        if (len === 0)
        {
            return 0;
        }
        else if (len >= this.len)
        {
            return 1;
        }

        t = len / this.len;
        while (true)
        {
            v2 = getBezierLength(0, t);
            v = ((v = v2 - len) < 0 ? -v : v) / len;
            if (v <= 0.000001)
            {
                break;
            }

            x = 3 * (x2 * t * t + 2 * x1 * t + x0);
            y = 3 * (y2 * t * t + 2 * y1 * t + y0);
            d += x * x + y * y;
            t += (len - v2) / Math.sqrt(d);
        }

        return t;
    }
}