Vctr3D

Vctr3DFlashPlayer10,FlashCS4から追加されたクラスにVector3Dというのがある。
内部的には値を保持して計算しているだけなので、勉強がてらに同じ振る舞いをするクラスを作ってみた。

▼結論
自作すると3割~5割くらいの早く計算できることがわかった。
速度向上という意味では、実際にはあんま意味ないけどね。

▼Vector3D
3Dといっても、これで直接オブジェクトを描画するようなものではなくて、主に座標計算に使うクラスだ。以前からあるPointクラスの3D版だと思ったほうが良い。だからPoint3Dとかの名前にしておいた方が既存のユーザーからも理解しやすかっただろうが、座標だけじゃなくてベクトルを扱っていたり、他の言語との兼ね合いという面もあったりで、この名前にしたのだろう。
ちなみに同じくFlashPlayer10から追加されたVectorクラスとは全然が違うので注意。

Vecto3Dと同じ機能の関数を作って、ベンチマークテストをしてみた。
同機能関数の方が若干早いが、そもそもが十分早いので
これでスムーズな再生が可能!とかのモノではない。

▼結果詳細

Vector3Dの関数と同じ処理の関数を10万回実行した時の処理時間(ミリ秒)
今回はnew Vector3D(Math.random(),Math.random(),Math.random());で
10万個のVector3Dを生成して計測。

同じ機能の関数を作った方が早いみたい。

Vctr3D.nearEquals()は暫定版。
また、除算などの場合、小数の最後の桁の数字が正確には一致しないことがある。

さんざん検証したが、実際の制作ではほぼ意味がないレベルの最適化。
Flashにおいて10万頂点数ってまずないし、それでも数十mm/secの違いしかない。
多くても数百〜千頂点程度だから、30fpsの場合1秒あたり3万頂点数。
30フレームあたり20mm/secの最適化ができたとしても、
29.0fpsだったものが29.6fpsになるくらいのもの。

基本的に描画の段階がボトルネックになっているので描画オブジェクトを減らしたり、
アルファを使うべきか見直したり、小さくした方がはるかに最適化の効果を得られるはず。

むしろ速度面の効果より、FlashPlayer9でもほぼ同じクラスが使える、という意義のほうがあるかもしれない。
 

▼Wonderfl

▼ActionScript3
[sourcecode language=”as3″]
/**
Vector3Dの関数と同じ処理の関数を10万回実行した時の処理時間(ミリ秒)
今回はnew Vector3D(Math.random(),Math.random(),Math.random());で
10万個のVector3Dを生成して計測。
keim_at_Siの突っ込みを参考にしたけど、コレで合ってるかな。

同じ機能の関数を作った方が早いみたい。

Vctr3D.nearEquals()は暫定版。
また、除算などの場合、小数の最後の桁の数字が正確には一致しないことがある。

さんざん検証したが、実際の制作ではほぼ意味がないレベルの最適化。
Flashにおいて10万頂点数ってまずないし、それでも数十mm/secの違いしかない。
多くても数百〜千頂点程度だから、30fpsの場合1秒あたり3万頂点数。
30フレームあたり20mm/secの最適化ができたとしても、
29.0fpsだったものが29.6fpsになるくらいのもの。

基本的に描画の段階がボトルネックになっているので描画オブジェクトを減らしたり、
アルファを使うべきか見直したり、小さくした方がはるかに最適化の効果を得られるはず。

それらを一通りやった後だったら、気休め程度にはいいかも、、、。

MacBookPro2.4Gh,OSX 10.5.6
_Vector3D_0:39:length
_Vctr3D_0:23:Vctr3D.length()
_Vector3D_1:25:lengthSquared
_Vctr3D_1:10:Vctr3D.lengthSquared()
_Vector3D_2:76:add()
_Vctr3D_2:65:Vctr3D.add()
_Vector3D_3:95:angleBetween()
_Vctr3D_3:57:Vctr3D.angleBetween()
_Vector3D_4:75:clone()
_Vctr3D_4:65:Vctr3D.clone()
_Vector3D_5:79:crossProduct()
_Vctr3D_5:65:Vctr3D.crossProduct()
_Vector3D_6:23:decrementBy()
_Vctr3D_6:14:Vctr3D.decrementBy()
_Vector3D_7:105:distance()
_Vctr3D_7:27:Vctr3D.distance()
_Vector3D_8:27:dotProduct()
_Vctr3D_8:14:Vctr3D.dotProduct()
_Vector3D_9:24:equals()
_Vctr3D_9:15:Vctr3D.equals()
_Vector3D_10:23:incrementBy()
_Vctr3D_10:14:Vctr3D.incrementBy()
_Vector3D_11:28:nearEquals()
_Vctr3D_11:26:Vctr3D.nearEquals()
_Vector3D_12:22:negate()
_Vctr3D_12:11:Vctr3D.negate()
_Vector3D_13:53:normalize()
_Vctr3D_13:27:Vctr3D.normalize()
_Vector3D_14:22:project()
_Vctr3D_14:15:Vctr3D.project()
_Vector3D_15:26:scaleBy()
_Vctr3D_15:14:Vctr3D.scaleBy()
_Vector3D_16:75:subtract()
_Vctr3D_16:64:Vctr3D.subtract()
_Vector3D_17:1760:toString()
_Vctr3D_17:1753:Vctr3D.ToString()
_a99:9:対照用に0を返すだけの関数

*/
package {
import flash.display.Sprite;
import flash.geom.Vector3D;
import flash.text.TextField;
public class vecor3Dtest extends Sprite {
public var _a:Vector.<Vector3D>=new Vector.<Vector3D>(100000,true);
public var _b:Vector.<Vector3D>=new Vector.<Vector3D>(100000,true);
private var Vctr3D:Vctr3DClass = new Vctr3DClass();
public function vecor3Dtest():void {
var tf1:TextField = new TextField();
tf1.width=stage.stageWidth/2;
tf1.wordWrap = true;
var tf2:TextField = new TextField();
tf2.width=tf2.x=stage.stageWidth/2;
tf1.height=tf2.height=stage.stageHeight;
stage.addChild(tf1);
stage.addChild(tf2);
for (var i:int=0; i<100000; i++) {
_a[i]=new Vector3D(Math.random(),Math.random(),Math.random(),Math.random());
_b[i]=new Vector3D(Math.random(),Math.random(),Math.random(),Math.random());
}

var _str1:String = new String();
var _str2:String = new String();
_str1="Vector3Dの関数と同じ処理の関数を10万回実行した時の処理時間(ミリ秒)\r";
//順番による影響を減らすために、一度実行。一番目は遅くなるっぽい
benchMarkj(_Vector3D_0);
benchMarkj(_Vctr3D_0);
benchMarkj(_Vector3D_1);
benchMarkj(_Vctr3D_1);
benchMarkj(_Vector3D_2);
benchMarkj(_Vctr3D_2);
benchMarkj(_Vector3D_3);
benchMarkj(_Vctr3D_3);
benchMarkj(_Vector3D_4);
benchMarkj(_Vctr3D_4);
benchMarkj(_Vector3D_5);
benchMarkj(_Vctr3D_5);
benchMarkj(_Vector3D_6);
benchMarkj(_Vctr3D_6);
benchMarkj(_Vector3D_7);
benchMarkj(_Vctr3D_7);
benchMarkj(_Vector3D_8);
benchMarkj(_Vctr3D_8);
benchMarkj(_Vector3D_9);
benchMarkj(_Vctr3D_9);
benchMarkj(_Vector3D_10);
benchMarkj(_Vctr3D_10);
benchMarkj(_Vector3D_11);
benchMarkj(_Vctr3D_11);
benchMarkj(_Vector3D_12);
benchMarkj(_Vctr3D_12);
benchMarkj(_Vector3D_13);
benchMarkj(_Vctr3D_13);
benchMarkj(_Vector3D_14);
benchMarkj(_Vctr3D_14);
benchMarkj(_Vector3D_15);
benchMarkj(_Vctr3D_15);
benchMarkj(_Vector3D_16);
benchMarkj(_Vctr3D_16);
benchMarkj(_Vector3D_17);
benchMarkj(_Vctr3D_17);
benchMarkj(_a99);

_str1+="_Vector3D_0:"+benchMarkj(_Vector3D_0)+":length\r";//39
_str1+="_Vctr3D_0:"+benchMarkj(_Vctr3D_0)+":Vctr3D.length()\r";//23
_str1+="_Vector3D_1:"+benchMarkj(_Vector3D_1)+":lengthSquared\r";//25
_str1+="_Vctr3D_1:"+benchMarkj(_Vctr3D_1)+":Vctr3D.lengthSquared()\r";//10
_str1+="_Vector3D_2:"+benchMarkj(_Vector3D_2)+":add()\r";//76
_str1+="_Vctr3D_2:"+benchMarkj(_Vctr3D_2)+":Vctr3D.add()\r";//65
_str1+="_Vector3D_3:"+benchMarkj(_Vector3D_3)+":angleBetween()\r";//95
_str1+="_Vctr3D_3:"+benchMarkj(_Vctr3D_3)+":Vctr3D.angleBetween()\r";//57
_str1+="_Vector3D_4:"+benchMarkj(_Vector3D_4)+":clone()\r";//75
_str1+="_Vctr3D_4:"+benchMarkj(_Vctr3D_4)+":Vctr3D.clone()\r";//65
_str1+="_Vector3D_5:"+benchMarkj(_Vector3D_5)+":crossProduct()\r";//79
_str1+="_Vctr3D_5:"+benchMarkj(_Vctr3D_5)+":Vctr3D.crossProduct()\r";//65
_str1+="_Vector3D_6:"+benchMarkj(_Vector3D_6)+":decrementBy()\r";//23
_str1+="_Vctr3D_6:"+benchMarkj(_Vctr3D_6)+":Vctr3D.decrementBy()\r";//14
_str1+="_Vector3D_7:"+benchMarkj(_Vector3D_7)+":distance()\r";//105
_str1+="_Vctr3D_7:"+benchMarkj(_Vctr3D_7)+":Vctr3D.distance()\r";//27
_str1+="_Vector3D_8:"+benchMarkj(_Vector3D_8)+":dotProduct()\r";//27
_str1+="_Vctr3D_8:"+benchMarkj(_Vctr3D_8)+":Vctr3D.dotProduct()\r";//14
_str1+="_Vector3D_9:"+benchMarkj(_Vector3D_9)+":equals()\r";//24
_str1+="_Vctr3D_9:"+benchMarkj(_Vctr3D_9)+":Vctr3D.equals()\r";//15
_str2+="_Vector3D_10:"+benchMarkj(_Vector3D_10)+":incrementBy()\r";//23
_str2+="_Vctr3D_10:"+benchMarkj(_Vctr3D_10)+":Vctr3D.incrementBy()\r";//14
_str2+="_Vector3D_11:"+benchMarkj(_Vector3D_11)+":nearEquals()\r";//28
_str2+="_Vctr3D_11:"+benchMarkj(_Vctr3D_11)+":Vctr3D.nearEquals()\r";//26
_str2+="_Vector3D_12:"+benchMarkj(_Vector3D_12)+":negate()\r";//22
_str2+="_Vctr3D_12:"+benchMarkj(_Vctr3D_12)+":Vctr3D.negate()\r";//11
_str2+="_Vector3D_13:"+benchMarkj(_Vector3D_13)+":normalize()\r";//53
_str2+="_Vctr3D_13:"+benchMarkj(_Vctr3D_13)+":Vctr3D.normalize()\r";//27
_str2+="_Vector3D_14:"+benchMarkj(_Vector3D_14)+":project()\r";//22
_str2+="_Vctr3D_14:"+benchMarkj(_Vctr3D_14)+":Vctr3D.project()\r";//15
_str2+="_Vector3D_15:"+benchMarkj(_Vector3D_15)+":scaleBy()\r";//26
_str2+="_Vctr3D_15:"+benchMarkj(_Vctr3D_15)+":Vctr3D.scaleBy()\r";//14
_str2+="_Vector3D_16:"+benchMarkj(_Vector3D_16)+":subtract()\r";//75
_str2+="_Vctr3D_16:"+benchMarkj(_Vctr3D_16)+":Vctr3D.subtract()\r";//64
_str2+="_Vector3D_17:"+benchMarkj(_Vector3D_17)+":toString()\r";//1760
_str2+="_Vctr3D_17:"+benchMarkj(_Vctr3D_17)+":Vctr3D.ToString()\r";//1753
_str2+="_a99:"+benchMarkj(_a99)+":対照用に0を返すだけの関数\r";//9
tf1.text=_str1;
tf2.text=_str2;
}

//10万回関数を実行して、かかった時間をtrace
private function benchMarkj(_fn:Function):int {
var time:Number = (new Date()).getTime();
_fn(100000);
return (new Date()).getTime() – time;
}

private function _Vector3D_0(n:uint):void {
for (var i:int = 0; i < n; i++) {
_a[i].length;
}
}
private function _Vctr3D_0(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.length(_a[i]);
}
}
private function _Vector3D_1(n:uint):void {
for (var i:int = 0; i < n; i++) {
_a[i].lengthSquared;
}
}
private function _Vctr3D_1(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.lengthSquared(_a[i]);
}
}
private function _Vector3D_2(n:uint):void {
for (var i:int = 0; i < n; i++) {
_a[i].add(_b[i]);
}
}
private function _Vctr3D_2(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.add(_a[i],_b[i]);
}
}
private function _Vector3D_3(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vector3D.angleBetween(_a[i],_b[i]);
}
}
private function _Vctr3D_3(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.angleBetween(_a[i],_b[i]);
}
}
private function _Vector3D_4(n:uint):void {
for (var i:int = 0; i < n; i++) {
_a[i].clone();
}
}
private function _Vctr3D_4(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.clone(_a[i]);
}
}
private function _Vector3D_5(n:uint):void {
for (var i:int = 0; i < n; i++) {
_a[i].crossProduct(_b[i]);
}
}
private function _Vctr3D_5(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.crossProduct(_a[i],_b[i]);
}
}
private function _Vector3D_6(n:uint):void {
for (var i:int = 0; i < n; i++) {
_a[i].decrementBy(_b[i]);
}
}
private function _Vctr3D_6(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.decrementBy(_a[i],_b[i]);
}
}
private function _Vector3D_7(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vector3D.distance(_a[i],_b[i]);
}
}
private function _Vctr3D_7(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.distance(_a[i],_b[i]);
}
}
private function _Vector3D_8(n:uint):void {
for (var i:int = 0; i < n; i++) {
_a[i].dotProduct(_b[i]);
}
}
private function _Vctr3D_8(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.dotProduct(_a[i],_b[i]);
}
}
private function _Vector3D_9(n:uint):void {
for (var i:int = 0; i < n; i++) {
_a[i].equals(_b[i],true);
}
}
private function _Vctr3D_9(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.equals(_a[i],_b[i],true);
}
}
private function _Vector3D_10(n:uint):void {
for (var i:int = 0; i < n; i++) {
_a[i].incrementBy(_b[i]);
}
}
private function _Vctr3D_10(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.incrementBy(_a[i],_b[i]);
}
}
private function _Vector3D_11(n:uint):void {
for (var i:int = 0; i < n; i++) {
_a[i].nearEquals(_b[i],1,true);
}
}
private function _Vctr3D_11(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.nearEquals(_a[i],_b[i],1,true);
}
}
private function _Vector3D_12(n:uint):void {
for (var i:int = 0; i < n; i++) {
_a[i].negate();
}
}
private function _Vctr3D_12(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.negate(_a[i]);
}
}
private function _Vector3D_13(n:uint):void {
for (var i:int = 0; i < n; i++) {
_a[i].normalize();
}
}
private function _Vctr3D_13(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.normalize(_a[i]);
}
}
private function _Vector3D_14(n:uint):void {
for (var i:int = 0; i < n; i++) {
_a[i].project();
}
}
private function _Vctr3D_14(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.project(_a[i]);
}
}
private function _Vector3D_15(n:uint):void {
for (var i:int = 0; i < n; i++) {
_a[i].scaleBy(3.1);
}
}
private function _Vctr3D_15(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.scaleBy(_a[i],3.1);
}
}
private function _Vector3D_16(n:uint):void {
for (var i:int = 0; i < n; i++) {
_a[i].subtract(_b[i]);
}
}
private function _Vctr3D_16(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.subtract(_a[i],_b[i]);
}
}
private function _Vector3D_17(n:uint):void {
for (var i:int = 0; i < n; i++) {
_a[i].toString();
}
}
private function _Vctr3D_17(n:uint):void {
for (var i:int = 0; i < n; i++) {
Vctr3D.ToString(_a[i]);
}
}
private function _a99(n:uint):void {
for (var i:int = 0; i < n; i++) {
zero();
}
}
private function zero():Number {
return 0;
}
}
}

import flash.display.Sprite;
class Vctr3DClass extends Sprite {
import flash.geom.Vector3D;
public function length(_a:Vector3D):Number {
return Math.sqrt(_a.x*_a.x+_a.y*_a.y+_a.z*_a.z);
}
public function lengthSquared(_a:Vector3D):Number {
return _a.x*_a.x+_a.y*_a.y+_a.z*_a.z;
}
public function add(_a:Vector3D,_b:Vector3D):Vector3D {
return new Vector3D(_a.x+_b.x,_a.y+_b.y,_a.z+_b.z);
}
public function angleBetween(_a:Vector3D,_b:Vector3D):Number {
return Math.acos((_a.x*_b.x+_a.y*_b.y+_a.z*_b.z)/(Math.sqrt(_a.x*_a.x+_a.y*_a.y+_a.z*_a.z)*Math.sqrt(_b.x*_b.x+_b.y*_b.y+_b.z*_b.z)));
}
public function clone(_a:Vector3D):Vector3D {
return new Vector3D(_a.x,_a.y,_a.z,_a.w);
}
public function isHiddenCrossProduct(_a:Vector3D,_b:Vector3D):Boolean {
return _a.x*_b.y-_a.y*_b.x > 0;
}
public function crossProduct(_a:Vector3D,_b:Vector3D):Vector3D {
return new Vector3D(_a.y*_b.z-_a.z*_b.y,_a.z*_b.x-_a.x*_b.z,_a.x*_b.y-_a.y*_b.x,1);
}
public function decrementBy(_a:Vector3D,_b:Vector3D):void {
_a.x-=_b.x;
_a.y-=_b.y;
_a.z-=_b.z;
}
public function distance(_a:Vector3D,_b:Vector3D):Number {
return Math.sqrt((_a.x-_b.x)*(_a.x-_b.x)+(_a.y-_b.y)*(_a.y-_b.y)+(_a.z-_b.z)*(_a.z-_b.z));
}
public function dotProduct(_a:Vector3D,_b:Vector3D):Number {
return _a.x*_b.x+_a.y*_b.y+_a.z*_b.z;
}
public function equals(_a:Vector3D,_b:Vector3D,_is:Boolean = false):Boolean {
return _a.x == _b.x && _a.y == _b.y && _a.z == _b.z && (!_is || _a.w == _b.w);
}
public function incrementBy(_a:Vector3D,_b:Vector3D):void {
_a.x+=_b.x;
_a.y+=_b.y;
_a.z+=_b.z;
}
public function nearEquals(_a:Vector3D,_b:Vector3D,_d:Number,_is:Boolean = false):Boolean {
return Math.abs(_a.x – _b.x) < _d && Math.abs(_a.y – _b.y) < _d && Math.abs(_a.z – _b.z) < _d && (!_is || Math.abs(_a.w – _b.w) < _d);
}
public function negate(_a:Vector3D):void {
_a.x = -_a.x;
_a.y = -_a.y;
_a.z = -_a.z;
}
public function normalize(_a:Vector3D):Number {
var _len:Number = Math.sqrt(_a.x*_a.x+_a.y*_a.y+_a.z*_a.z);
_a.x /= _len;
_a.y /= _len;
_a.z /= _len;
return _len;
}
public function project(_a:Vector3D):void {
var _w:Number = _a.w;
_a.x /= _w;
_a.y /= _w;
_a.z /= _w;
}
public function scaleBy(_a:Vector3D,_s:Number):void {
_a.x *= _s;
_a.y *= _s;
_a.z *= _s;
}
public function subtract(_a:Vector3D,_b:Vector3D):Vector3D {
return new Vector3D(_a.x-_b.x,_a.y-_b.y,_a.z-_b.z);
}
public function ToString(_a:Vector3D):String {
return "Vector3D("+_a.x+", "+_a.y+", "+_a.z+")";
}
}

[/sourcecode]

▼ Vctr3D Class(Vector3Dと同機能のクラス)
[sourcecode language=”as3″]
package {
public class Vctr3D extends Object {
public var w:Number;
public var x:Number;
public var y:Number;
public var z:Number;
public static const X_AXIS:Vctr3D=new Vctr3D(1,0,0);
public static const Y_AXIS:Vctr3D=new Vctr3D(0,1,0);
public static const Z_AXIS:Vctr3D=new Vctr3D(0,0,1);
function Vctr3D(_x:Number=0.,_y:Number=0.,_z:Number=0.,_w:Number=0.) {
w=_w;
x=_x;
y=_y;
z=_z;
}
public function get length():Number {
return Math.sqrt(x*x+y*y+z*z);
}
public function get lengthSquared():Number {
return x*x+y*y+z*z;
}
public function add(a:Vctr3D):Vctr3D {
return new Vctr3D(a.x+x,a.y+y,a.z+z);
}
public static function angleBetween(a:Vctr3D,b:Vctr3D):Number {
return Math.acos(a.x*b.x+a.y*b.y+a.z*b.z/Math.sqrt(a.x*a.x+a.y*a.y+a.z*a.z)*Math.sqrt(b.x*b.x+b.y*b.y+b.z*b.z));
}
public function clone():Vctr3D {
return new Vctr3D(x,y,z,w);
}
public function crossProduct(a:Vctr3D):Vctr3D {
return new Vctr3D(y*a.z-z*a.y,z*a.x-x*a.z,x*a.y-y*a.x,1);
}
public function decrementBy(a:Vctr3D):void {
x-=a.x;
y-=a.y;
z-=a.z;
}
public static function distance(pt1:Vctr3D,pt2:Vctr3D):Number {
return Math.sqrt(pt1.x-pt2.x*pt1.x-pt2.x+pt1.y-pt2.y*pt1.y-pt2.y+pt1.z-pt2.z*pt1.z-pt2.z);
}
public function dotProduct(a:Vctr3D):Number {
return x*a.x+y*a.y+z*a.z;
}
public function equals(toCompare:Vctr3D,allFour:Boolean=false):Boolean {
return x==toCompare.x&&y==toCompare.y&&z==toCompare.z&&! allFour||w==toCompare.w;
}
public function incrementBy(a:Vctr3D):void {
x+=a.x;
y+=a.y;
z+=a.z;
}
public function nearEquals(toCompare:Vctr3D,tolerance:Number,allFour:Boolean=false):Boolean {
return Math.abs(x-toCompare.x)<tolerance&&Math.abs(y-toCompare.y)<tolerance&&Math.abs(z-toCompare.z)<tolerance&&! allFour||Math.abs(w-toCompare.w)<tolerance;
}
public function negate():void {
x=- x;
y=- y;
z=- z;
}
public function normalize():Number {
var _len:Number=Math.sqrt(x*x+y*y+z*z);
x/=_len;
y/=_len;
z/=_len;
return _len;
}
public function project():void {
x/=w;
y/=w;
z/=w;
}
public function scaleBy(s:Number):void {
x*=s;
y*=s;
z*=s;
}
public function subtract(a:Vctr3D):Vctr3D {
return new Vctr3D(x-a.x,y-a.y,z-a.z);
}
public function toString():String {
return "Vctr3D("+x+", "+y+", "+z+")";
}
// おまけ
public function isHiddenCrossProduct(a:Vctr3D):Boolean {
return x*a.y-y*a.x>0;
}
}
}
[/sourcecode]

▼【参考】Vector3D
http://help.adobe.com/ja_JP/AS3LCR/Flash_10.0/flash/geom/Vector3D.html