完・パーフェクトシャッフルは何回で元に戻るか (AS3版)

前回は直線の始点と終点だけを円上に配置して直線で結んでいたが、直線の中間点の点も円上になるように変換してみた。

ついでに、表示サイズの最終調整も行って満足いく仕上がりになった。

長々と3回に渡ってやってきたけど、「続: パーフェクトシャッフルは何回で元に戻るか - 西尾泰和のはてなダイアリー の直線が円になるところをアニメーションさせてみたい」という当初の欲望は果たせたので満足した。

以下、ソース(104行)。

package {
import flash.display.*;
import flash.events.Event;
import flash.geom.Point;
import flash.filters.BlurFilter;
import org.libspark.betweenas3.BetweenAS3;
import org.libspark.betweenas3.tweens.ITween;
import org.libspark.betweenas3.easing.*;
import frocessing.color.ColorHSV;

[SWF(backgroundColor="0x000000")]
public class PerfectShuffleVisualization3 extends Sprite {
    public var r:Number = 6400;
    public var d:Number = 300;
    public var angle:Number = 3;
    private var canvas:Sprite = new Sprite();
    private const NUM:int = 5;
    private const SIZE:int = 30;
    private const POINT_PER_LINE:Number = 10;
    private const CANVAS_SIZE:Number = 475;

    public function PerfectShuffleVisualization3() {
        stage.scaleMode = "noScale";
        stage.align = "TL";

        // draw background
        graphics.beginFill(0x000000);
        graphics.drawRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);
        graphics.endFill(); 

        addChild(canvas);
        canvas.x = CANVAS_SIZE / 2;
        canvas.y = CANVAS_SIZE / 2 + d / 2;
        canvas.filters = [new BlurFilter(3, 3)];
        draw();

        var tween:ITween = BetweenAS3.parallel(
            BetweenAS3.tween(this, { r: 30 }, null, 5, Quint.easeOut),
            BetweenAS3.tween(this, { d: 160 }, null, 5, Cubic.easeOut),
            BetweenAS3.tween(this, { angle: 360 }, null, 5, Sine.easeIn),
            BetweenAS3.tween(canvas, { y: CANVAS_SIZE / 2 }, null, 5)
        );
        tween.onUpdate = draw;
        stage.addEventListener("click", function(event:Event):void { tween.gotoAndPlay(0); });
        tween.play();
    }

    private function draw():void {
        var p:Point = new Point();
        var g:Graphics = canvas.graphics;
        g.clear();

        // draw lines
        for (var yy:int = 0; yy < SIZE; yy++) {
            var num:int = yy, prev_num:Number = yy;
            g.lineStyle(3, new ColorHSV(yy * 270 / SIZE, .7).value, .7);
            p.x = 0; p.y = num; getXY(p);
            g.moveTo(p.x, p.y);
            for (var xx:int = 0; xx < NUM; xx++) {
                num = getNext(num);
                for (var i:int = 0; i < POINT_PER_LINE; i++) {
                    var ratio:Number = (i + 1.0) / POINT_PER_LINE;
                    p.x = xx + ratio;
                    p.y = prev_num * (1 - ratio) + num * ratio;
                    getXY(p);
                    g.lineTo(p.x, p.y);
                }
                prev_num = num;
            }
        }
    }

    // get next position after perfect shuffle
    private function getNext(num:int):int {
        if (num < SIZE / 2) {
            return num * 2 + 1;
        } else {
            return (num - SIZE / 2) * 2;
        }
    }

    private function getXY(pt:Point):Point {
        var rad:Number = (-angle / 2.0 + angle * pt.x / NUM) / 180.0 * Math.PI;
        pt.x =  (r + d / (SIZE - 1) * pt.y) * Math.sin(rad);
        pt.y = -(r + d / (SIZE - 1) * pt.y) * Math.cos(rad) + r * Math.cos(angle / 2 / 180 * Math.PI);
        return pt;
    }
}
}