Beautifl - Flash Gallery

Thumbnail : graphics.drawGraphicsData のための回転対称による美的表現
graphics.drawGraphicsData のための回転対称による美的表現
Aquioux 2009-08-27 MIT License

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

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;
	}
}