graphics.drawGraphicsData のための回転対称による美的表現
graphics.drawGraphicsData のための回転対称による美的表現
解説ページ:http://aquioux.blog48.fc2.com/blog-entry-597.html
@author YOSHIDA, Akio
/**
* Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/akdA
*/
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;
}
}