Beautifl - Flash Gallery

Preview

graphics.drawGraphicsData のための回転対称による美的表現
Aquioux 2009年8月27日 MIT License
?
graphics.drawGraphicsData のための回転対称による美的表現  
解説ページ:http://aquioux.blog48.fc2.com/blog-entry-597.html  
@author YOSHIDA, Akio
      package {
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    [SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#FFFFFF")]
    
    /**
     * graphics.drawGraphicsData のための回転対称による美的表現
     * 解説ページ:http://aquioux.blog48.fc2.com/blog-entry-597.html
     * @author YOSHIDA, Akio
     */
    public class Main extends Sprite {
        
        private var numOfAxis:uint;            // 対称軸数(この値によって360度を分割し、単位角度を求める)
        private var numOfNode:uint;            // 単位角度内に置かれる Node オブジェクトの数
        private var time:Number = 1.75;        // RotationalSymmetry クラスの Tweener の所要時間
        
        private var symmetry:RotationalSymmetry;
        private var textField:TextField;
        
        
        public function Main() {
            textField = new TextField();
            textField.autoSize = TextFieldAutoSize.LEFT;
            textField.selectable = false;
            addChild(textField);
            
            stage.addEventListener(MouseEvent.CLICK, clickHandler);
            clickHandler(null);
        }
        
        private function clickHandler(event:MouseEvent):void {
            if (symmetry) {
                symmetry.kill();
                removeChild(symmetry);
                symmetry = null;
            }
            
            numOfAxis = Math.random() * 12 + 2;
            numOfNode = Math.random() * 5 + 2;
            
            symmetry = new RotationalSymmetry(numOfAxis, numOfNode , time);
            addChild(symmetry);
            symmetry.build();
            symmetry.run();

            textField.text = "回転対称軸数:" + symmetry.numOfAxis + "\n単位角度内ノード数:" + symmetry.numOfNode;
        }
    }
}


import caurina.transitions.AuxFunctions;
import caurina.transitions.Tweener;
import flash.display.GraphicsEndFill;
import flash.display.GraphicsPath;
import flash.display.GraphicsPathCommand;
import flash.display.GraphicsSolidFill;
import flash.display.GraphicsStroke;
import flash.display.IGraphicsData;
import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.utils.Timer;
/**
 * graphics.drawGraphicsData を使った回転対称
 * @author Yoshida, Akio
 */
class RotationalSymmetry extends Sprite {
    private var _numOfAxis:uint;        // 対称軸数(この値によって360度を分割し、単位角度を求める)
    private var _numOfNode:uint;        // 単位角度内に置かれる Node オブジェクトの数
    public function get numOfAxis():uint { return _numOfAxis; }
    public function get numOfNode():uint { return _numOfNode; }
    
    // Node オブジェクトを格納する Vector
    private var nodeVector:Vector.<Node>;
    
    // Matrix.translate で使用
    private var offsetX:Number;                
    private var offsetY:Number;
    
    // Node 座標を変換するための Matrix
    private var convMatrix:Matrix;
    
    // graphicsData 用
    private var graphicsData:Vector.<IGraphicsData>;
    private var fill:GraphicsSolidFill;        // 塗り
    private var path:GraphicsPath;            // パス

    private var time:Number;    // Tweener の Tween 所要時間

    private var timer:Timer;
    
    public function RotationalSymmetry(numOfAxis:uint, numOfNode:uint, time:Number) {
        _numOfAxis = Math.max(2, numOfAxis);
        _numOfNode = Math.max(2, numOfNode);
        this.time  = Math.max(0.5, time);

        if (_numOfAxis == 2) {
            _numOfNode *= 2;
        }

        TweenerShortcuts.init();    // Tweener 用のカスタムショートカット
    }
    
    // 要素の構築
    public function build():void {
        if (stage) {
            buildNode();
            buildOffset();
            buildMatrix();
            buildGraphicsData();
        } else {
            throw new Error("いずれかの DisplayObjectContainer に addChild してください");
        }
    }
    
    // 実行
    public function run():void {
        timer = new Timer(time * 1.25 * 1000);
        timer.addEventListener(TimerEvent.TIMER, timerHandler);
        timer.start();
        timerHandler(null);
    }
    
    // 削除用
    public function kill():void {
        timer.stop();
        timer.removeEventListener(TimerEvent.TIMER, timerHandler);
    }
    

    // Node オブジェクト生成
    private function buildNode():void {
        var max:Number = stage.stageWidth / 2;
        var min:Number = 10;
        var n:uint = _numOfNode;
        nodeVector = new Vector.<Node>();
        for (var i:uint = 0; i < n; i++) {
            nodeVector.push(new Node(max, min));
        }
    }
    // オフセット計算
    private function buildOffset():void {
        offsetX = stage.stageWidth  / 2;
        offsetY = stage.stageHeight / 2;
    }
    // 変換マトリクス生成
    private function buildMatrix():void{
        convMatrix = new Matrix();
        convMatrix.translate( -offsetX, -offsetY);
        convMatrix.rotate(Math.PI * 2 / _numOfAxis);
        convMatrix.translate(offsetX, offsetY);
    }
    // graphics.drawGraphicsData の引数生成
    private function buildGraphicsData():void {
        // 塗りの定義
        fill = new GraphicsSolidFill(Math.random() * 0xFFFFFF);
        
        // commands の type を決定
        var commandType:int = GraphicsPathCommand.LINE_TO;
        var divider:uint       = 1;
        if ((_numOfAxis * _numOfNode) % 2 == 0) {    // 対称軸数とノード数の積が偶数(正偶数角形)のとき
            if (Math.random() < 0.75) {            // 75% の確立で CURVE_TO
                commandType = GraphicsPathCommand.CURVE_TO;
                divider = 2;
            }
        }
        
        // パスの定義
        // コマンドの定義
        var commands:Vector.<int> = new Vector.<int>();
        commands.push(GraphicsPathCommand.MOVE_TO);
        var n:uint = _numOfAxis * _numOfNode / divider;
        for (var i:uint = 0; i < n; i++) {
            commands.push(commandType);
        }
        // 座標データの定義
        var data:Vector.<Number> = new Vector.<Number>();
        // コマンド+座標データをパッケージング
        path = new GraphicsPath(commands, data);
        
        // 描画データをパッケージング
        graphicsData = new Vector.<IGraphicsData>();
        graphicsData.push(fill);
        graphicsData.push(path);
        graphicsData.push(new GraphicsEndFill());
    }
    

    // タイマーハンドラ(Tweener 定義)
    private function timerHandler(event:TimerEvent):void {
        // 色
        Tweener.addTween(fill, {
            color:0xFFFFFF * Math.random(),
            time:time,
            transition:"easeInCubic"
        });

        // 座標
        var node:Node = nodeVector[0];
        var next:Point = node.next;
        Tweener.addTween(node, {
            onUpdate:update,
            x:next.x,
            y:next.y,
            time:time,
            transition:"easeOutCubic"
        });
        var n:uint = nodeVector.length;
        for (var i:uint = 1; i < n; i++) {
            node = nodeVector[i];
            next = node.next;
            var transition:String = (i % 2) ? "easeInCubic" : "easeOutCubic";
            Tweener.addTween(node, {
                x:next.x,
                y:next.y,
                time:time,
                transition:transition
            });
        }
    }
    

    // Tweener が onUpdate のタイミングで呼び出す
    private function update():void {
        render(createPathData());
    }
    // path の data を計算
    private function createPathData():Vector.<Number> {
        var localConvMatrix:Matrix = convMatrix.clone();
        var resultPoint:Point;
        var data:Vector.<Number> = new Vector.<Number>();
        var prevXVector:Vector.<Number> = new Vector.<Number>();
        var prevYVector:Vector.<Number> = new Vector.<Number>();
        
        // 第1単位角度の座標
        var n:uint = nodeVector.length;
        for (var i:uint = 0; i < n; i++) {
            prevXVector[i] = nodeVector[i].x + offsetX;
            prevYVector[i] = nodeVector[i].y + offsetY;
            data.push(prevXVector[i], prevYVector[i]);
        }
        
        // 第2単位角度以降の座標
        var m:uint = n;
        n = _numOfAxis;
        for (i = 1; i < n; i++) {
            for (var j:uint = 0; j < m; j++) {
                resultPoint = localConvMatrix.transformPoint(new Point(prevXVector[j], prevYVector[j]));
                prevXVector[j] = resultPoint.x;
                prevYVector[j] = resultPoint.y;
                data.push(prevXVector[j], prevYVector[j]);
            }
        }
        data.push(data[0], data[1]);
        
        return data;
    }
    private function render(data:Vector.<Number>):void {
        path.data = data;
        graphics.clear();
        graphics.drawGraphicsData(graphicsData);
    }
}


import flash.geom.Point;

class Node {
    
    public var x:Number = 0.0;
    public var y:Number = 0.0;
    private var max:int;
    private var min:int;
    
    public function Node(max:int, min:int) {
        this.max = max;
        this.min = min;
    }

    public function get next():Point {
        var radius:uint = randomInt(max, min);
        var degree:uint = randomInt(360);
        return Point.polar(radius, degree * Math.PI / 180);
    }
    
    private function randomInt(max:int, min:int = 0):int {
        return Math.random() * (max - min) + min;
    }
}


import caurina.transitions.Tweener;
import caurina.transitions.AuxFunctions;
import flash.display.GraphicsSolidFill;

class TweenerShortcuts {

    public function TweenerShortcuts () {}

    public static function init(): void {
        // Tweener にカスタムメソッドを追加(GraphicsSolidFill 用)
        // caurina.transitions.properties.TextShortcut.as を参考にした
        Tweener.registerSpecialPropertySplitter("color", _generic_splitter, ["r", "g", "b"]);
        Tweener.registerSpecialProperty("r", _property_get, _property_set, ["r"]);
        Tweener.registerSpecialProperty("g", _property_get, _property_set, ["g"]);
        Tweener.registerSpecialProperty("b", _property_get, _property_set, ["b"]);
    }
    
    
    // Tweener カスタムメソッド(GraphicsSolidFill 用)
    // caurina.transitions.properties.TextShortcut.as を参考にした
    public static function _generic_splitter(p_value:Number, p_parameters:Array):Array {
        var nArray:Array = new Array();
        nArray.push({name:p_parameters[0], value:AuxFunctions.numberToR(p_value)});
        nArray.push({name:p_parameters[1], value:AuxFunctions.numberToG(p_value)});
        nArray.push({name:p_parameters[2], value:AuxFunctions.numberToB(p_value)});
        return nArray;
    }
    public static function _property_get (p_obj:Object, p_parameters:Array, p_extra:Object = null):Number {
        var fill:GraphicsSolidFill = GraphicsSolidFill(p_obj);
        var colorComponent:String  = p_parameters[0];
        if (colorComponent == "r") return AuxFunctions.numberToR(fill.color);
        if (colorComponent == "g") return AuxFunctions.numberToG(fill.color);
        if (colorComponent == "b") return AuxFunctions.numberToB(fill.color);
        return NaN;
    }
    public static function _property_set(p_obj:Object, p_value:Number, p_parameters:Array, p_extra:Object = null):void {
        var fill:GraphicsSolidFill = GraphicsSolidFill(p_obj);
        var colorComponent:String  = p_parameters[0];
        if (colorComponent == "r") fill.color = (fill.color & 0x00ffff) | (p_value << 16);
        if (colorComponent == "g") fill.color = (fill.color & 0xff00ff) | (p_value << 8);
        if (colorComponent == "b") fill.color = (fill.color & 0xffff00) | p_value;
    }
}