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化(Kraft Type)

マウスで引いた線を立体にする
* マウスを押して動かしている間線を描いて
* マウスを離したら3D化
*----------------------------------------
* ToDo 鋭利な角度をつけた時におかしな表示となる
*      (現在の深度は3D座標のZ値による)
*      透視投影にする
*      ソースが散らかっている。。。。
*      etc. etc...
*
* “見た目以外には”線を引きながら交点を得る必要はない。
* 座標を整えるときに交点を入れる
*
* 先頭→交点、交点→交点、…、交点→終点の集合データとすると
* 交点は“柱”。“柱”間を壁でつなぐ
* リボンのような連続した画像を貼る時、面倒に…ならない! はず。
/**
 * Copyright Kay ( http://wonderfl.net/user/Kay )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/dpEG
 */

// forked from Kay's 平面に描いた線を3D化(暫定Ver)
// forked from Kay's ドラッグして描いた線の交差判定 
// forked from Kay's Basic お絵かき
/*
 * マウスで引いた線を立体にする
 * マウスを押して動かしている間線を描いて
 * マウスを離したら3D化
 *----------------------------------------
 * ToDo 鋭利な角度をつけた時におかしな表示となる
 *      (現在の深度は3D座標のZ値による)
 *      透視投影にする
 *      ソースが散らかっている。。。。
 *      etc. etc...
 *
 * “見た目以外には”線を引きながら交点を得る必要はない。
 * 座標を整えるときに交点を入れる
 *
 * 先頭→交点、交点→交点、…、交点→終点の集合データとすると
 * 交点は“柱”。“柱”間を壁でつなぐ
 * リボンのような連続した画像を貼る時、面倒に…ならない! はず。
 */

package {

	import flash.display.*;
	import flash.events.*;
	import flash.text.*;
	import flash.geom.*;

	[SWF(width=400,height=400,frameRate=30,backgroundColor=0x333333)]
	public class Take06 extends Sprite {

		public const CX:Number=stage.stageWidth/2;
		public const CY:Number=stage.stageHeight/2;

		public var vertices2D:Vector.<Vertex>;	// 2D座標
		public var vertices3D:Vector.<Number>;	// 3D座標
		public var uvt:Vector.<Number> = new Vector.<Number>();


		public var dragFlg:Boolean=false;
		public var beginPoint:Point;// 描画開始点
		public var lines:Vector.<Line> = new Vector.<Line>();
		public var canvas2D:Sprite = new Sprite();// 2D描画
		public var canvas3D:Sprite = new Sprite();// 3D描画
		public var crossPoint:Point = new Point();
		public var mtx:Matrix3D;
		public var normals:Vector.<Vector3D>;
		public var panels:Vector.<Panel>;

		public function Take06() {
			vertices2D = new Vector.<Vertex>;
			addChild(canvas3D);
			canvas3D.x=CX;
			canvas3D.y=CY;
			addChild(canvas2D);
			stage.addEventListener(MouseEvent.MOUSE_UP,   xStopDrag);
			stage.addEventListener(MouseEvent.MOUSE_MOVE, xDrawLine);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, xStartDrag);
		}

		// 描画開始
		public function xStartDrag(e:MouseEvent):void {
			if (dragFlg == false) {
				dragFlg=true;
				var nX:Number=mouseX;
				var nY:Number=mouseY;
				
				// カンバス初期化
				canvas3D.graphics.clear();
				canvas2D.graphics.clear();
				canvas2D.graphics.lineStyle(1,0xcccccc);
				canvas2D.graphics.moveTo(nX, nY);
	
				// Vector初期化
				lines.splice(0, lines.length);
				vertices2D.splice(0, vertices2D.length);
	
				// 描画開始点を記録
				beginPoint=new Point(nX,nY);
			}
		}

		// 描画中
		// マウスが動いたことを検知し
		// かつマウスが押されている場合(dragFlg==true)だけドラッグと判断する
		public function xDrawLine(e:MouseEvent):void {
			if (dragFlg) {
				xDrawAndRecord();
			}
		}

		// 描画終了
		public function xStopDrag(e:MouseEvent):void {
			dragFlg=false;
			xDrawAndRecord();

			// マウス反応を停止
			stage.removeEventListener(MouseEvent.MOUSE_UP,   xStopDrag);
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, xDrawLine);
			stage.removeEventListener(MouseEvent.MOUSE_DOWN, xStartDrag);

			// 描いたラインと点を徐々に消す
			stage.addEventListener(Event.ENTER_FRAME,xDeleteLine);

			// 頂点情報を整える
			xGainPoints();
			
			// 頂点座標の距離を総延長を合算してUVTデータに利用する

			// 2D頂点を3D座標に変換する
			vertices3D = new Vector.<Number>();
			panels = new Vector.<Panel>();	// 各面をパネルとして記録
			var vL:uint=vertices2D.length;
			trace('--------------');
			trace('V2D:' + vL);
			var max:int=0;
			for (var i:uint = 0; i < vL; i++) {
				vertices3D.push(vertices2D[i].x, vertices2D[i].y, 0);
				if (i < vL-1) {
					var panel:Panel = new Panel();
					var p1:int=i;
					var p2:int=i+1;
					var p3:int=vL+i;
					var p4:int=vL+i+1;
					if (p4 > max) max = p4;
					panel.ind = new Vector.<int>();
					panel.ind.push(p1,p2,p4, p4,p3,p1);
					// 法線基礎データを取得する
					// 2Dデータの開始点/終了点からxy値を得る(z=0)
					var dx:Number = vertices2D[i+1].x - vertices2D[i].x;
					var dy:Number = vertices2D[i+1].y - vertices2D[i].y;
					panel.normal = new Vector3D(dx,dy,0,0);
					panels.push(panel);
				}
			}
			trace('max:'+max);
			trace('panels:' + panels.length);
			trace('V3:' + vertices3D.length);

			var vUpper:Vector.<Number> = new Vector.<Number>();
			var vBottom:Vector.<Number> = new Vector.<Number>();

			var vMatrix:Matrix3D = new Matrix3D();

			vMatrix.prependTranslation(-CX,-CY,-60);
			vMatrix.transformVectors(vertices3D, vUpper);
			vMatrix.prependTranslation(0,0,120);
			vMatrix.transformVectors(vertices3D, vBottom);

			vertices3D=vUpper.concat(vBottom);
			trace('V3x2:' + vertices3D.length);


			// 3D化
			// ここまでの処理で交点(重複)を含むvertices2Dが取得できている
			// matrix3Dでtransformして遊ぶ手もあり…
			// 1枚ずつのパネルとして記録するか
			// 1枚ずつをdrawTrianglesで描くか <= this way

			// 3D描画処理
			// ※drawTrianglesを用いているが、
			// ※陰影を表現するためにパネルを1枚ずつ描くように変更
			mtx = new Matrix3D();
			stage.addEventListener(Event.ENTER_FRAME, xRotate);
		}

		// 頂点情報を間引く
		// 最初の点は必ず入れる
		// 最後の点は必ず入れる
		// 交点は必ず入れる
		// 大きくターンしている頂点は必ず入れる
		public function xGainPoints():void {
			// 2D頂点Vectorを作成
			// 最初に交点を取得しておく
			// 交点が見つかれば頂点に交点追加(元と先と2箇所とも)
			// 交点はひとつの線に複数入ることもあり、その順序は距離でソート
			var lLen:Number=lines.length;
			for (var l:uint = 0; l < lLen; l++) {
				// 次の線とは接しているが交差していると判断しない
				for (i = l+2; i < lLen; i++) {
					var targetLine:Line=lines[i];
					// 交点が見つかったら、lineとlines[i]双方に座標を記録
					if (getCrossPoint(lines[i],lines[l])) {
						// 今ひいているlineと対象のlines[i]に交点座標を追加
						var cp:Point=crossPoint.clone();
						lines[l].crossPoints.push(cp);
						lines[i].crossPoints.push(cp);
					}
				}
			}
			// linesからvertices2Dを作成
			// 開始点
			vertices2D.splice(0, vertices2D.length);	// 再初期化
			vertices2D.push(new Vertex(lines[0].p1.x, lines[0].p1.y));
			for (i = 0; i < lLen; i++) {
				// 交点が複数存在する場合は、開始点から近い順に取り出す
				var cpLen:int=lines[i].crossPoints.length;
				if (cpLen>0) {
					// ソートする
					var aSort:Array = new Array();
					var basePoint:Point=lines[i].p1;
					for (var p:uint = 0; p < cpLen; p++) {
						var tp:Point=lines[i].crossPoints[p];
						var dist:Number=Point.distance(basePoint,tp);
						aSort.push({num:p, point:tp, dist:dist});
					}
					aSort.sortOn("dist", Array.NUMERIC);
					// Vectorの内容を消去してからソートされたPointを入れる
					for (p = 0; p < cpLen; p++) {
						lines[i].crossPoints.push(lines[i].crossPoints[aSort[p]['num']]);
						vertices2D.push(new Vertex(lines[i].crossPoints[p].x, lines[i].crossPoints[p].y, 0, true));
					}
				}
				// Lineの末端
				vertices2D.push(new Vertex(lines[i].p2.x, lines[i].p2.y));
			}

			// 角度が大きく変わる点をみつける
			var vLen:Number=vertices2D.length;
			for (i = 1; i < vLen; i++) {
				var dx:Number=vertices2D[i-1].x-vertices2D[i].x;
				var dy:Number=vertices2D[i-1].y-vertices2D[i].y;
				vertices2D[i].r=Math.atan2(dy,dx);
				if (i>1) {
					vertices2D[i].d=getDifRad(vertices2D[i].r,vertices2D[i-1].r);
				}
			}

			var tempVertices2D:Vector.<Vertex> = new Vector.<Vertex>();
			tempVertices2D.push(new Vertex(vertices2D[0].x,vertices2D[0].y));
			var vTemp:Vertex=new Vertex(vertices2D[0].x,vertices2D[0].y);
			for (var i:uint = 1; i < vLen; i++) {
				// 頂点か大きな角度なら必ず記録
				if (vertices2D[i].v||vertices2D[i].d>0.8) {
					tempVertices2D.push(vertices2D[i]);
					vTemp=vertices2D[i];
				} else {
					dist=Point.distance(vTemp,vertices2D[i]);
					if (dist>10) {
						tempVertices2D.push(vertices2D[i]);
						vTemp=vertices2D[i];
					}
				}
			}
			vertices2D=tempVertices2D;
		}

		// 角度差(raian)を求める
		// -PI~PIを返す
		public function getDifRad(r1:Number=0, r2:Number=0):Number {
			var dr:Number=r1-r2;
			while (dr < 0) {
				dr+=Math.PI*2;
			}
			dr%=Math.PI*2;
			dr-=dr>Math.PI?Math.PI*2:0;
			return dr;
		}

		public function xRotate(e:Event):void {
			canvas3D.graphics.clear();
			mtx.appendRotation(3,Vector3D.X_AXIS);
			mtx.appendRotation(2,Vector3D.Y_AXIS);
			mtx.appendRotation(1,Vector3D.Z_AXIS);
			
			// vertices3Dにmtxを反映させたvmtx3Dを得る
			var vmtx3D:Vector.<Number> = new Vector.<Number>();
			mtx.transformVectors(vertices3D, vmtx3D);
			//trace([vmtx3D[0],vmtx3D[1],vmtx3D[2]]);

			var vout2D:Vector.<Number> = new Vector.<Number>();
			Utils3D.projectVectors(mtx,vertices3D,vout2D,uvt);
			
			// vmtXの値を元にパネルのz座標の合計zSumを得る
			var pLen:uint = panels.length;
			for (var p:uint = 0; p < pLen; p++) {
				// z座標だけではうまくいかない…
				panels[p].zSum = (vmtx3D[panels[p].ind[0]*3+2]+
								  vmtx3D[panels[p].ind[1]*3+2]+
								  vmtx3D[panels[p].ind[2]*3+2]+
								  vmtx3D[panels[p].ind[3]*3+2]);
			}
			
			// zSum値を元にpanelsをソート
			var func:Object = function(a:Panel,b:Panel):Number{
				if(a.zSum == b.zSum)	return 0;
				if(a.zSum < b.zSum)		return 1;
				return -1;
			}
			panels.sort(func);

			
			while(canvas3D.numChildren) {
				canvas3D.removeChildAt(0);
			}
			var lNormal:Vector3D = new Vector3D(-1,1,1,0);	// 光線ベクトル
			for (p = 0; p < pLen; p++) {
				var surface:Shape = new Shape();
				var rand:uint = Math.floor(Math.random()*0xffffff);
				//trace(panels[p].zSum);
				var vNormal:Vector3D = mtx.transformVector(panels[p].normal);
				var ratio:Number = (Vector3D.angleBetween(lNormal,vNormal) / Math.PI) / 5;
				canvas3D.addChild(surface);
				var nC:uint = Math.floor(ratio*0xff + 0x66);
				//trace(ratio);
				var nColor:uint = nC+0x22 << 16 | nC+0x22 << 8 | nC;
				surface.graphics.beginFill(nColor);
				surface.graphics.drawTriangles(vout2D, panels[p].ind, null, TriangleCulling.POSITIVE);
				surface.graphics.endFill();
				nColor = nC + 0x22 + 0 << 16 | nC+0x11 << 8 | nC - 0x11;

				surface.graphics.beginFill(nColor);
				surface.graphics.drawTriangles(vout2D, panels[p].ind, null, TriangleCulling.NEGATIVE);
				surface.graphics.endFill();
			}
		}

		// ラインを描画し、linesにLineを追加
		public function xDrawAndRecord():void {
			var nX:Number=mouseX;
			var nY:Number=mouseY;
			canvas2D.graphics.lineTo(nX,nY);

			// linesにlineを追加
			// 開始点と終了点をわかっていたい
			// Line作成時に角度を記録する
			var line:Line=new Line(beginPoint.x,beginPoint.y,nX,nY);
			lines.push(line);

			// 描画終了点を記録
			beginPoint=new Point(nX,nY);
		}

		// 2本の直線の交点を計算
		public function getCrossPoint(lineA:Line, lineB:Line):Boolean {
			var flg:Boolean=false;
			// det == 0 は平行
			var det:Number=lineB.f*lineA.g-lineA.f*lineB.g;
			if (det!=0) {
				var dx:Number=lineB.p1.x-lineA.p1.x;
				var dy:Number=lineB.p1.y-lineA.p1.y;
				var t1:Number = (lineB.f*dy - lineB.g*dx)/det;
				var t2:Number = (lineA.f*dy - lineA.g*dx)/det;
				// t1, t2の値が0~1の範囲外の場合、交点は延長線上に存在する
				if (t1>=0&&t1<=1&&t2>=0&&t2<=1) {
					crossPoint.x=lineA.p1.x+lineA.f*t1;
					crossPoint.y=lineA.p1.y+lineA.g*t1;
					flg=true;
				}
			}
			return flg;
		}

		// 画面に描いた線と点を消去するアニメーション
		public function xDeleteLine(e:Event):void {
			canvas2D.alpha-=0.1;
			// 消去完了
			if (canvas2D.alpha<=0.5) {
				// マウス反応を再開
				// ※タイミングに問題あり
				// alpha==0まで待つとうまくいかない…
				stage.addEventListener(MouseEvent.MOUSE_UP,   xStopDrag);
				stage.addEventListener(MouseEvent.MOUSE_MOVE, xDrawLine);
				stage.addEventListener(MouseEvent.MOUSE_DOWN, xStartDrag);
				canvas2D.graphics.clear();
				canvas2D.alpha=1;
				// このイベントを削除
				stage.removeEventListener(Event.ENTER_FRAME, xDeleteLine);
			}
		}
	}
}

import flash.geom.*;
import flash.display.*;
class Panel {
	public var dist:Number;
	public var normal:Vector3D;
	public var ind:Vector.<int>;
	public var zSum:Number;
	public var uvt:Vector.<Number> = new Vector.<Number>(1);
	public function Panel():void {
		// indexを記録
		// 法線データを記録
		// 視点からの距離
	}
}

class Vertex extends Point {
	public var r:Number=-1;// 前のVertexとの角度(マイナスは未測定)
	public var v:Boolean=false;// 交点であるか
	public var d:Number=0;// 角度差
	function Vertex(nX:Number = 0, nY:Number = 0, nR:Number = 0, bV:Boolean = false, nD:Number = 0):void {
		x=nX;
		y=nY;
		r=nR;
		v=bV;
		d=nD;
	}
}

class Line extends Shape {
	public var p1:Point;
	public var p2:Point;
	public var f:Number;
	public var g:Number;
	public var crossPoints:Vector.<Point> = new Vector.<Point>();
	public function Line(x1:Number=0, y1:Number=0, x2:Number=0, y2:Number=0):void {
		p1=new Point(x1,y1);
		p2=new Point(x2,y2);
		f=p2.x-p1.x;
		g=p2.y-p1.y;
	}
}