Vector3D/Matrix3Dの練習

fp10_3d
>> demo
(※要FlashPlayer10 マウス操作で回転を制御できます)

ここ最近のエントリーはフーリエ変換とか小難しい内容が多かったので少し息抜き。
遅ればせながらFlashPlayer10で追加された3D関連の機能を使う練習です。
今回はVector3D/Matrix3Dクラスを使いました。

Matrix3Dクラスには平行移動/回転/拡大・縮小などの基本的な操作を行うメソッドが用意されていて、
変換行列の前掛けと後掛けでprepend~、append~に分かれています。
今回のデモのように、立方体の重心を基準点にして回転させたい場合は、
・親インスタンス(DisplayObject)の基準点に平行移動 → 回転 → 元の位置に戻す
という手順で操作します。
詳しくはFlash Player 10で新たに加わった3Dの行列とベクトルを使ってみる – F-siteセミナーを参考に。

空間充填曲線なので次数を上げれば線だけで面を塗ることもできますが、やはり高次の曲線で描くと重いです。
(曲線の頂点数は6次:約24000、7次:約98000、8次:約390000)

今回使ったメソッド以外にもいろいろ用意されてるようなので、それらについても調べていこうと思います。
Papervision3Dも最近は全く触ってないんですが、すっかり中身が変わってたらどうしよう。。

・ソースコード
(Ralph Hauwertさん作の SimpleZSorterクラスを利用しています)

package {
	import __AS3__.vec.Vector;
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;

	import com.theflashblog.fp10.SimpleZSorter;

	[SWF(width = "400", height = "400", backgroundColor = "#ffffff", frameRate = "12")]

	public class Test3D extends Sprite {
		private var container:Sprite;
		private var material:Vector.<Hilbert>;
		private var size:int = Hilbert.SIZE/2;
		private var nX:Number;
		private var nY:Number;

		public function Test3D():void {
			container = new Sprite();
			nX = container.x = (this.stage.stageWidth/2) - size;
			nY = container.y = (this.stage.stageHeight/2) - size;
			container.z = 0;
			createHilbertCube(4);
			stage.addChild(container);
			addEventListener(Event.ENTER_FRAME, rotate);
		}
		private function rotate(e:Event):void {
			var rotX:Number = (mouseY - size - nY)*0.1;
			var rotY:Number = (mouseX - size - nX)*0.1;
			var mat:Matrix3D = container.transform.matrix3D;
			mat.appendTranslation(-nX-size, -nY-size, -size);
			mat.appendRotation(rotX, Vector3D.X_AXIS);
			mat.appendRotation(-rotY, Vector3D.Y_AXIS);
			mat.appendTranslation(nX+size, nY+size, size);
			container.transform.matrix3D = mat;
			SimpleZSorter.sortClips(container, true);
		}
		private function createHilbertCube(order:int):void {
			material = new Vector.<Hilbert>(6, true);
			var color:Vector.<uint> = Vector.<uint>([0xff7fbf, 0xbf7fff, 0x7fffff, 0x7fbfff, 0x7fffbf, 0xffbf7f]);
			for(var i:int=0; i<material.length; i++) {
				material[i] = new Hilbert(order, color[i]);
				material[i].z = 0;
			}
			material[1].transform.matrix3D.prependRotation(90, Vector3D.X_AXIS);
			material[2].transform.matrix3D.prependRotation(-90, Vector3D.Y_AXIS);
			for(var i:int=3; i<material.length; i++) {
				material[i].transform.matrix3D.prependTranslation(size<<1, size<<1, size<<1);
			}
			material[3].transform.matrix3D.prependRotation(180, Vector3D.X_AXIS);
			material[3].transform.matrix3D.prependRotation(180, Vector3D.Y_AXIS);
			material[4].transform.matrix3D.prependRotation(90, Vector3D.Y_AXIS);
			material[4].transform.matrix3D.prependRotation(-90, Vector3D.X_AXIS);
			material[5].transform.matrix3D.prependRotation(90, Vector3D.Y_AXIS);
			material[5].transform.matrix3D.prependRotation(180, Vector3D.X_AXIS);
			for(var i:int=0; i<material.length; i++) {
				container.addChild(material[i]);
			}
		}
	}
}

import flash.display.GraphicsPathCommand;
import flash.display.Shape;
import flash.geom.Point;

class Hilbert extends Shape {
	public static const SIZE:int = 200;
	private var order:int; // 次数
	private var h:Number; // 刻み
	private var pt:Point;
	private var commands:Vector.<int>;
	private var data:Vector.<Number>;
	private var iterCommands:int = 0;
	private var iterData:int = 0;

	public function Hilbert(order:int, color:uint = 0x00ff99, fill:Boolean = false) {
		commands = new Vector.<int>();
		data = new Vector.<Number>();
		this.order = order;
		this.pt = new Point();
		graphics.lineStyle(2, color);
		h = 1;
		for(var i:int = 2; i <= order; i++) h = h / (2 + h);
		h *= SIZE;
		rightUpLeft(order); // 基礎形:コの字
		if(fill) graphics.beginFill(color);
		graphics.drawPath(commands, data);
		graphics.endFill();
	}
	private function rightUpLeft(i:int):void {
		if(i == 0) return;
		upRightDown(i - 1); drawRelative(h, 0);
		rightUpLeft(i - 1); drawRelative(0, h);
		rightUpLeft(i - 1); drawRelative(-h, 0);
		downLeftUp(i - 1);
	}
	private function downLeftUp(i:int):void {
		if(i == 0) return;
		leftDownRight(i - 1); drawRelative(0, -h);
		downLeftUp(i - 1); drawRelative(-h, 0);
		downLeftUp(i - 1); drawRelative(0, h);
		rightUpLeft(i - 1);
	}
	private function leftDownRight(i:int):void {
		if(i == 0) return;
		downLeftUp(i - 1); drawRelative(-h, 0);
		leftDownRight(i - 1); drawRelative(0, -h);
		leftDownRight(i - 1); drawRelative(h, 0);
		upRightDown(i - 1);
	}
	private function upRightDown(i:int):void {
		if(i == 0) return;
		rightUpLeft(i - 1); drawRelative(0, h);
		upRightDown(i - 1); drawRelative(h, 0);
		upRightDown(i - 1); drawRelative(0, -h);
		leftDownRight(i - 1);
	}
	private function drawRelative(dx:Number, dy:Number):void {
		commands[iterCommands++] = GraphicsPathCommand.LINE_TO;
		data[iterData++] = pt.x + dx;
		data[iterData++] = pt.y + dy;
		pt.x += dx;
		pt.y += dy;
	}
}
 

Tags: ,

Comments

No comments so far.

  • Leave a Reply
     
    Your gravatar
    Your Name