In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

3D ドーナッツ

Get Adobe Flash player
by muta244 17 Dec 2008
package
{
    import flash.display.*;
    import flash.events.*;
    import flash.text.*;
    import flash.ui.Keyboard;
    import flash.utils.getTimer;
    
    [SWF(width="465", height="465",
        backgroundColor="0x000000", frameRate="30")]
    public class Donuts02 extends Sprite
    {
        private var _donuts:Donuts;
        private var _matrix:Matrix3D;
        private var _render:Render;
        
        private var _textField:TextField;
        
        private var _rx:Number = 0;
        private var _ry:Number = 0;
        private var _rz:Number = 0;
        
        private var _isAutoPlay:Boolean = false;
        
        private const STAGE_WIDTH:Number = stage.stageWidth;
        private const STAGE_HEIGHT:Number = stage.stageHeight;
        
        public function Donuts02()
        {
            configure();
            
            var pi:Number = Math.PI;
            var start:int;
            var i:int;
            
            _donuts = new Donuts(16, 0.7);
            _matrix = new Matrix3D();
            _render = new Render(graphics, _matrix);
            
            addTextField();
            
            mainLoop(null);
            
            stage.addEventListener(MouseEvent.CLICK, changePlayMode);
            
            _textField.text =
                "Partitions : " + _donuts.numPartitions + "\n" +
                "Vertices : " + _donuts.vertices.length + "\n" +
                "Stretch : " + _donuts.stretch;
        }
        
        private function configure():void
        {
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.showDefaultContextMenu = false;
        }
        
        private function addTextField():void
        {
            _textField = new TextField();
            _textField.defaultTextFormat = new TextFormat(
                null, null, 0xFFFFFF, null, null, null, null, null, TextFormatAlign.RIGHT);
            _textField.autoSize = TextFieldAutoSize.RIGHT;
            _textField.background = true;
            _textField.backgroundColor = 0x000000;
            _textField.selectable = false;
            _textField.x = STAGE_WIDTH - _textField.width;
            
            addChild(_textField);
            
            var announce:TextField = new TextField();
            announce.defaultTextFormat = new TextFormat(
                null, null, 0xFFFFFF, null, null, null, null, null, TextFormatAlign.LEFT);
            announce.autoSize = TextFieldAutoSize.LEFT;
            announce.background = true;
            announce.backgroundColor = 0x000000;
            announce.selectable = false;
            announce.text =
                "\"DOWN\" : Partitions -\n" +
                "\"UP\" : Partitions +\n" +
                "\"LEFT\" : Stretch -\n" +
                "\"RIGHT\" : Stretch +\n" +
                "\"Z\" : Polygon on/off\n" +
                "\"X\" : Wireframe on/off";
            announce.y = STAGE_HEIGHT - announce.height;
            
            addChild(announce);
        }
        
        private function mainLoop(event:Event):void
        {
            _matrix.identity();
            _matrix.scale(50, 50, 50);
            _matrix.rotateX(_rx);
            _matrix.rotateY(_ry);
            _matrix.rotateZ(_rz);
            _matrix.translate(STAGE_WIDTH / 2, STAGE_HEIGHT / 2, 0);
            
            graphics.clear();
            
            _render.drawIndexedPrimitive(
                Render.PRIMITIVE_POLYGON, _donuts.vertices, _donuts.indices);
            
            _rx += 0.04;
            _ry += 0.08;
            _rz += 0.02;
        }
        
        private function changePlayMode(event:MouseEvent):void
        {
            if (_isAutoPlay)
            {
                removeEventListener(Event.ENTER_FRAME, mainLoop);
                stage.removeEventListener(KeyboardEvent.KEY_DOWN, changeDonuts);
            }
            else
            {
                addEventListener(Event.ENTER_FRAME, mainLoop);
                stage.addEventListener(KeyboardEvent.KEY_DOWN, changeDonuts);
            }
            
            _isAutoPlay = !_isAutoPlay;
        }
        
        private function changeDonuts(event:KeyboardEvent):void
        {
            var numPartitions:int = _donuts.numPartitions;
            
            switch (event.keyCode)
            {
                case Keyboard.UP:
                    if (numPartitions >= 0)
                    {
                        _donuts.numPartitions += 1;
                    }
                    break;
                case Keyboard.DOWN:
                    if (numPartitions > 0)
                    {
                        _donuts.numPartitions -= 1;
                    }
                    break;
                case Keyboard.LEFT:
                    _donuts.stretch -= 0.02;
                    break;
                case Keyboard.RIGHT:
                    _donuts.stretch += 0.02;
                    break;
                case 88:
                    //trace("X");
                    _render.wireframeEnabled = !_render.wireframeEnabled;
                    break;
                case 90:
                    //trace("Z");
                    _render.polygonEnabled = !_render.polygonEnabled;
                    break;
            }
            
            _textField.text =
                "Partitions : " + _donuts.numPartitions + "\n" +
                "Vertices : " + _donuts.vertices.length + "\n" +
                "Stretch : " + _donuts.stretch.toString().substr(0, 4);
        }
    }
}

import flash.display.Graphics;

class Render
{
	public static const PRIMITIVE_POLYGON:int = 1;
	
	private var _graphics:Graphics;
	private var _matrix:Matrix3D;
	private var _mode:int;
	
	private var _polygonEnabled:Boolean = true;
	private var _wireframeEnabled:Boolean = false;
	
	// 光源ベクトル
	private var _light:Vector3D   = new Vector3D( 0.0,  0.0, -1.0);
	
	// アンビエント(環境光)の色
	private var _ambient:Vector3D = new Vector3D( 0.2,  0.2,  0.2);
	// ディフューズ(拡散光)の色
	private var _diffuse:Vector3D = new Vector3D( 0.8,  0.8,  0.8);
	
	/**
	* 新しい Render インスタンスを作成します。
	*/
	public function Render(
		graphics:Graphics = null, matrix:Matrix3D = null, mode:int = 1):void
	{
		_graphics = graphics;
		_matrix = matrix;
		_mode = mode;
	}
	
	public function drawIndexedPrimitive(type:int, vertices:Array, indices:Array):void
	{
		var i:int;
		var l:int;
		
		var tv:Array = [];
		
		// 座標を一次変換
		for (i = 0, l = vertices.length; i < l; ++i)
		{
			tv.push(_matrix.transform(vertices[i]));
		}
		
		// ラスタライズ
		switch (type)
		{
			case PRIMITIVE_POLYGON:
				
				var triangles:Array = [];
				
				for (i = 0, l = indices.length; i < l; i += 3)
				{
					triangles.push(new Triangle3D(
						tv[indices[i    ]],
						tv[indices[i + 1]],
						tv[indices[i + 2]]
					));
				}
				
				// 深度ソート
				triangles.sortOn("depth", Array.DESCENDING | Array.NUMERIC);
				
				for each (var triangle:Triangle3D in triangles)
				{
					drawPolygon(triangle);
				}
				
				break;
		}
	}
	
	private function drawPolygon(triangle:Triangle3D):void
	{
		if (polygonEnabled)
		{
			// 面の法線ベクトルを求める。
			var n:Vector3D = triangle.normal();
			
			// 光源と法線の内積を取る。
			var w:Number = n.dot(_light);
			
			// 背面カリング処理。
			// 内積が負なら背面なので描画しない。(ただしパースをつけた場合は正確な判定方法ではない。)
			if (w < 0)
			{
				return;
			}
			
			var r:int = (_ambient.x + w * _diffuse.x) * 255;
			var g:int = (_ambient.y + w * _diffuse.y) * 255;
			var b:int = (_ambient.z + w * _diffuse.z) * 255;
			
			r = (r < 0) ? 0 : (r > 255) ? 255 : r;
			g = (g < 0) ? 0 : (g > 255) ? 255 : g;
			b = (b < 0) ? 0 : (b > 255) ? 255 : b;
			
			_graphics.beginFill(r << 16 | g << 8 | b);
		}
		
		if (wireframeEnabled)
		{
			_graphics.lineStyle(0, 0xFF0000);
		}
		
		_graphics.moveTo(triangle.v1.x, triangle.v1.y);
		_graphics.lineTo(triangle.v2.x, triangle.v2.y);
		_graphics.lineTo(triangle.v3.x, triangle.v3.y);
		_graphics.lineTo(triangle.v1.x, triangle.v1.y);
		
		_graphics.endFill();
	}
	
	public function get graphics():Graphics
	{
		return _graphics;
	}
	
	public function set graphics(value:Graphics):void
	{
		_graphics = value;
	}
	
	public function get matrix():Matrix3D
	{
		return _matrix;
	}
	
	public function set matrix(value:Matrix3D):void
	{
		_matrix = value;
	}
	
	public function get polygonEnabled():Boolean
	{
		return _polygonEnabled;
	}
	
	public function set polygonEnabled(value:Boolean):void
	{
		_polygonEnabled = value;
	}
	
	public function get wireframeEnabled():Boolean
	{
		return _wireframeEnabled;
	}
	
	public function set wireframeEnabled(value:Boolean):void
	{
		_wireframeEnabled = value;
	}
}

class Donuts
{
	private var _vertices:Array;
	private var _indices:Array;
	private var _numPartitions:int;
	private var _stretch:Number;
	
	/**
	 * @param numPartitions 分割数
	 * @param stretch 拡がり
	 */
	public function Donuts(numPartitions:int = 16, stretch:Number = 1):void
	{
		_numPartitions = numPartitions;
		_stretch = stretch;
		
		setup(numPartitions, stretch);
	}
	
	/**
	 * @private
	 */
	private function setup(numPartitions:int, stretch:Number):void
	{
		var n:int = numPartitions;
		
		var i:int;
		var j:int;
		
		var a:Number = 0;
		var c:Number = 0;
		var addc:Number = Math.PI * 2 / n;
		
		var s:Number;
		var z:Number;
		var x:Number;
		var y:Number;
		
		_vertices = [];
		
		for (i = 0; i < n; ++i)
		{
			s = Math.sin(c) + 2;
			z = Math.cos(c);
			
			for (j = 0; j < n; ++j)
			{
				x = Math.cos(a);
				y = Math.sin(a);
				
				_vertices.push(new Vector3D(
					x * s * stretch,
					y * s * stretch,
					z     * stretch
				));
				
				a += Math.PI / n * 2;
			}
			
			c += addc;
		}
		
		var m:int = n * n;
		var off:Number;
		
		_indices = [];
		
		for (i = 0; i < n; ++i)
		{
			off = i * n;
			
			for (j = 0; j < n; ++j)
			{
				_indices.push((off + n) % m + (j + 1) % n);
				_indices.push( off          + (j + 1) % n);
				_indices.push( off          +  j         );
				_indices.push((off + n) % m +  j         );
				_indices.push((off + n) % m + (j + 1) % n);
				_indices.push( off          +  j         );
			}
		}
	}
	
	public function get vertices():Array
	{
		return _vertices.concat();
	}
	
	public function get indices():Array
	{
		return _indices.concat();
	}
	
	public function get numPartitions():int
	{
		return _numPartitions;
	}
	
	public function set numPartitions(value:int):void
	{
		_numPartitions = value;
		
		setup(value, stretch);
	}
	
	public function get stretch():Number
	{
		return _stretch;
	}
	
	public function set stretch(value:Number):void
	{
		_stretch = value;
		
		setup(numPartitions, value);
	}
}

class Triangle3D
{
    private var _v1:Vector3D;
    private var _v2:Vector3D;
    private var _v3:Vector3D;
    
    /**
    * 新しい Triangle3D インスタンスを作成します。
    */
    public function Triangle3D(v1:Vector3D, v2:Vector3D, v3:Vector3D):void
    {
        _v1 = v1;
        _v2 = v2;
        _v3 = v3;
    }
    
    public function get v1():Vector3D
    {
        return _v1;
    }
    
    public function set v1(value:Vector3D):void
    {
        _v1 = value;
    }
    
    public function get v2():Vector3D
    {
        return _v2;
    }
    
    public function set v2(value:Vector3D):void
    {
        _v2 = value;
    }
    
    public function get v3():Vector3D
    {
        return _v3;
    }
    
    public function set v3(value:Vector3D):void
    {
        _v3 = value;
    }
    
    /**
     * 各ベクトルの z の値を比較し、最も低い数値を返します。
     * 深度ソート時に Array オブジェクトの sortOn と合わせて使用します。
     */
    public function get depth():Number
    {
        var min:Number = (v1.z < v2.z) ? v1.z : v2.z;
        return (min < v3.z) ? min : v3.z;
    }
    
    /**
     * このトライアングルを面とする法線ベクトルを返します。
     * 
     * @return このトライアングルを面とする法線ベクトル
     */
    public function normal():Vector3D
    {
        var n:Vector3D = new Vector3D();
        n.cross(v1.distance(v2), v1.distance(v3));
        n.normalize();
        
        return n;
    }
    
    /**
     * このトライアングルのストリング表現を返します。
     * 
     * @return このトライアングルのストリング表現
     */
    public function toString():String
    {
        var temp:String = "Triangle3D {\n" +
            "\tv1 : " + v1 + "\n" +
            "\tv2 : " + v2 + "\n" +
            "\tv3 : " + v3 + "\n}";
        
        return temp;
    }
}

class Matrix3D
{
    private var _matrix:Array;
    
    /**
    * 新しい Matrix3D インスタンスを作成します。
    */
    public function Matrix3D():void
    {
        identity();
    }
    
    public function identity():void
    {
        _matrix = [
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1
        ];
    }
    
    public function scale(sx:Number, sy:Number, sz:Number)
    {
        _matrix = _multiply(_matrix, [
            sx,  0,  0,  0,
             0, sy,  0,  0,
             0,  0, sz,  0,
             0,  0,  0,  1
        ]);
    }
    
    public function translate(dx:Number, dy:Number, dz:Number)
    {
        _matrix = _multiply(_matrix, [
             1,  0,  0,  0,
             0,  1,  0,  0,
             0,  0,  1,  0,
            dx, dy, dz,  1
        ]);
    }
    
    public function rotateX(angleRadians:Number)
    {
        var sin = Math.sin(angleRadians);
        var cos = Math.cos(angleRadians);
        
        _matrix = _multiply(_matrix, [
            1,    0,   0, 0,
            0,  cos, sin, 0,
            0, -sin, cos, 0,
            0,    0,   0, 1
        ]);
    }
    
    public function rotateY(angleRadians:Number)
    {
        var sin = Math.sin(angleRadians);
        var cos = Math.cos(angleRadians);
        
        _matrix = _multiply(_matrix, [
            cos, 0, -sin, 0,
              0, 1,    0, 0,
            sin, 0,  cos, 0,
              0, 0,    0, 1
        ]);
    }
    
    public function rotateZ(angleRadians:Number)
    {
        var sin = Math.sin(angleRadians);
        var cos = Math.cos(angleRadians);
        
        _matrix = _multiply(_matrix, [
             cos, sin, 0, 0,
            -sin, cos, 0, 0,
               0,   0, 1, 0,
               0,   0, 0, 1
        ]);
    }
    
    /**
     * このマトリクスで指定のベクトルを一次変換します。
     * 
     * @return 一次変換されたベクトル
     */
    public function transform(v:Vector3D):Vector3D
    {
        var temp:Vector3D = new Vector3D();
        var m:Array = _matrix;
        
        temp.x = m[0] * v.x + m[4] * v.y + m[ 8] * v.z + m[12];
        temp.y = m[1] * v.x + m[5] * v.y + m[ 9] * v.z + m[13];
        temp.z = m[2] * v.x + m[6] * v.y + m[10] * v.z + m[14];
        
        return temp;
    }
    
    /**
     * このマトリクスと指定マトリクスの成分を乗算(合成)します。
     * 
     * @param m 対象の Matrix3D オブジェクト
     */
    public function multiply(m:Matrix3D):void
    {
        _matrix = _multiply(m.toArray(), _matrix);
    }
    
    /**
     * @private
     */
    private function _multiply(m1:Array, m2:Array):Array
    {
        var temp:Array = [];
        var i:int = 0;
        
        for (var y:int = 0; y < 4; ++y)
        {
            for (var x:int = 0; x < 4; ++x)
            {
                temp[x + i] =
                    m1[i    ] * m2[x     ] +
                    m1[i + 1] * m2[x +  4] +
                    m1[i + 2] * m2[x +  8] +
                    m1[i + 3] * m2[x + 12];
            }
            i += 4;
        }
        
        return temp;
    }
    
    /**
     * このマトリクスの成分配列を返します。
     * 
     * @return このマトリクスの成分配列
     */
    public function toArray():Array
    {
        return _matrix.concat();
    }
    
    /**
     * このマトリクスのストリング表現を返します。
     * 
     * @return このマトリクスのストリング表現
     */
    public function toString():String
    {
        var temp:String = "Matrix3D [\n";
        
        for (var i:int = 0, l:int = _matrix.length; i < l; ++i)
        {
            if (i % 4 == 0)
            {
                if (i != 0)
                {
                    temp += "\n";
                }
                temp += "\t";
            }
            if (i != l - 1)
            {
                temp += _matrix[i] + ", ";
            }
            else
            {
                temp += _matrix[i] + "\n]";
            }
        }
        
        return temp;
    }
}

class Vector3D
{
    private var _x:Number;
    private var _y:Number;
    private var _z:Number;
    
    /**
    * 新しい Vector3D インスタンスを作成します。
    */
    public function Vector3D(x:Number = 0, y:Number = 0, z:Number = 0):void
    {
        _x = x;
        _y = y;
        _z = z;
    }
    
    public function get x():Number
    {
        return _x;
    }
    
    public function set x(value:Number):void
    {
        _x = value;
    }
    
    public function get y():Number
    {
        return _y;
    }
    
    public function set y(value:Number):void
    {
        _y = value;
    }
    
    public function get z():Number
    {
        return _z;
    }
    
    public function set z(value:Number):void
    {
        _z = value;
    }
    
    /**
     * このベクトルを正規化して単位ベクトルにします。
     * 単位ベクトルとは大きさが 1 のベクトルのことです。
     */
    public function normalize():void
    {
        var l:Number = 1 / Math.sqrt(x * x + y * y + z * z);
        
        x *= l;
        y *= l;
        z *= l;
    }
    
    /**
     * 指定された 2 つのベクトルの外積をこのベクトルに設定します。
     * 外積は主に、面に垂直な法線ベクトルを求めるために使用します。
     * 
     * @param v1 1 つ目のベクトル
     * @param v1 2 つ目のベクトル
     */
    public function cross(v1:Vector3D, v2:Vector3D):void
    {
        x = v1.y * v2.z - v1.z * v2.y;
        y = v1.z * v2.x - v1.x * v2.z;
        z = v1.x * v2.y - v1.y * v2.x;
    }
    
    /**
     * このベクトルと指定ベクトルの内積を返します。
     * 内積を取るとベクトルとベクトルのなす角度が求まります。
     * 
     * @param v ベクトル
     * @return このベクトルと指定ベクトルの内積
     */
    public function dot(v:Vector3D):Number
    {
        return x * v.x + y * v.y + z * v.z;
    }
    
    /**
     * 指定された 2 つのベクトルの内積を返します。
     * 内積を取るとベクトルとベクトルのなす角度が求まります。
     * 
     * @param v1 1 つ目のベクトル
     * @param v1 2 つ目のベクトル
     * @return 指定された 2 つのベクトルの内積
     */
    public static function dot(v1:Vector3D, v2:Vector3D):Number
    {
        return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
    }
    
    /**
     * ベクトルを反転します。
     */
    public function invert():void
    {
        x *= -1;
        y *= -1;
        z *= -1;
    }
    
    /**
     * このベクトルと指定ベクトルとの差分を表すベクトルを返します。
     * 
     * @return 差分を表すベクトル
     */
    public function distance(v:Vector3D):Vector3D
    {
        return new Vector3D(x - v.x, y - v.y, z - v.z);
    }
    
    /**
     * このベクトルのコピーを返します。
     * 
     * @return このベクトルのコピー
     */
    public function clone():Vector3D
    {
        return new Vector3D(x, y, z);
    }
    
    /**
     * このベクトルのストリング表現を返します。
     * 
     * @return このベクトルのストリング表現
     */
    public function toString():String
    {
        return "Vector3D {x:" + x + ", y:" + y + ", z:" + z + "}";
    }
}