/**
* 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;
}
}