forked from: Divide Polygon
多角形をポリゴンで自動分割してみるTest(2)
>頂点をドラッグすると再計算をはじめます
>分割された多角形を三角形にする
?精度誤差回避したくない
極めてアナログチックな手法で数学的なエレガントさはカケラもありません。。
/**
* Copyright Kay ( http://wonderfl.net/user/Kay )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/1NdW
*/
// forked from Kay's Divide Polygon
// 多角形をポリゴンで自動分割してみるTest(2)
// >頂点をドラッグすると再計算をはじめます
// >分割された多角形を三角形にする
// ?精度誤差回避したくない
// 極めてアナログチックな手法で数学的なエレガントさはカケラもありません。。
package {
import flash.display.Sprite;
import flash.display.Shape;
import flash.events.Event;
import flash.geom.Point;
public class DividePolygon extends Sprite {
private const SW:Number=stage.stageWidth;
private const SH:Number=stage.stageHeight;
private const NP:int=13;
private var commands:Vector.<int>=new Vector.<int>(NP+1,true);
private var datas:Vector.<Number> = new Vector.<Number>((NP+1)*2,true);
private var vectors:Vector.<Number>;
private var outLines:Array;
private var obj:Shape = new Shape();
public function DividePolygon():void {
//-----------------------------------------------
// ポイントで囲まれた図形の形を示す
addChild(obj);
//-----------------------------------------------
// コントロールできるポイントをNP点配置する
var radian:Number=Math.PI*2/NP;
for (var i:int=0; i < NP; i++) {
// 外周点とdrawPathで描くための情報を登録
var point:DraggablePoint = new DraggablePoint();
point.x=Math.round(SW/2+100*Math.cos(radian*i));// 精度誤差回避
point.y=Math.round(SH/2+100*Math.sin(radian*i));// 精度誤差回避
point.name=i.toString();
point.addEventListener("PointMove", changeHandler);
datas[i*2+0]=point.x;
datas[i*2+1]=point.y;
commands[i]=i==0?1:2;
addChild(point);
}
render();
}
private function changeHandler(e:Event):void {
render(e.target);
}
// ポイントを繋ぐ線を描く
private function render(updatePoint:Object=null):void {
graphics.clear();
if (updatePoint) {
// 移動した点の位置情報を更新
var point:Object=updatePoint;
var num:int=point.name;
point.x=Math.round(point.x);// 精度誤差回避
point.y=Math.round(point.y);// 精度誤差回避
datas[num*2+0]=point.x;
datas[num*2+1]=point.y;
}
// 外周線と進行角度を配列に格納
vectors = new Vector.<Number>();
outLines = new Array();
for (var o:int = 0; o < NP; o++) {
var offset:int=o*2;
var pFrom:Point=new Point(datas[offset+0],datas[offset+1]);
if (o==NP-1) {
offset=-2;
}
var pTo:Point=new Point(datas[offset+2],datas[offset+3]);
outLines.push(new DivideLine(pFrom,pTo));
vectors.push(getVector(pFrom,pTo));
}
// すべての接続線を描く
var divideLines:Array = new Array();
for (var i:int = 0; i < NP; i++) {
for (var t:int = i+2; t < NP; t++) {
// 終了点と開始点を結ぶ線は除外しておく
if (i == 0 && t == NP-1) break;
// 全ての接続線を表示(目視確認用)
pFrom=new Point(datas[i*2],datas[i*2+1]);
pTo=new Point(datas[t*2],datas[t*2+1]);
// 外周線と交差しないかチェック
var outFlg:Boolean = false;
var myLine:DivideLine=new DivideLine(pFrom,pTo);
for (o = 0; o < NP; o++) {
if (getCrossPoint(myLine,outLines[o])==true) {
outFlg=true;
break;
}
}
// 図形の内向きの線チェック
if (outFlg == false && checkInner(i,getVector(pFrom,pTo))) {
divideLines.push(myLine);
}
}
}
// 分割線を短いものから確定していく
var resultLines:Array = new Array();
divideLines.sortOn("distance", Array.NUMERIC);
while (divideLines.length > 0) {
var shortLine:DivideLine = (divideLines.splice(0,1))[0];
resultLines.push(shortLine);
// 短い線と交わる線を配列から取り除く
var targetInd:int=0;
while (divideLines.length > targetInd) {
if (getCrossPoint(shortLine,divideLines[targetInd])) {
divideLines.splice(targetInd,1);
} else {
targetInd++;
}
}
}
//dispLines(resultLines);
// 総ての線を一つの配列にまとめる
// 分割線は2度使う
// 分割線から走査を開始
var triangles:Vector.<Point> = new Vector.<Point>();
resultLines = resultLines.concat(resultLines);
resultLines = resultLines.concat(outLines);
obj.graphics.clear();
while(resultLines.length > 0) {
// resultLinesから走査対象線を抽出
myLine = resultLines.splice(0,1)[0];
// 三角形の2点が確定
var pA:Point = myLine.pA;
var pB:Point = myLine.pB;
// 対象の線と接する線を探す
var numLength:int = resultLines.length;
var completeFlg:Boolean = false;
for (i = 0; i < numLength; i++) {
var pC:Point; // 残る1点の候補
var resultPoint:Point; // 接続しないほうの点
var matchFlg:Boolean = false;
var linesArr:Array;
// 同じ線は対象外
if ( (pA.equals(resultLines[i].pA) && pB.equals(resultLines[i].pB)) ||
(pA.equals(resultLines[i].pB) && pB.equals(resultLines[i].pA))) {
continue;
}
if (pA.equals(resultLines[i].pA)) {
pC = resultLines[i].pB;
resultPoint = pB;
matchFlg = true;
} else if (pA.equals(resultLines[i].pB)) {
pC = resultLines[i].pA;
resultPoint = pB;
matchFlg = true;
} else if (pB.equals(resultLines[i].pA)) {
pC = resultLines[i].pB;
resultPoint = pA;
matchFlg = true;
} else if (pB.equals(resultLines[i].pB)) {
pC = resultLines[i].pA;
resultPoint = pA;
matchFlg = true;
}
// 三角形をつくる候補となる線を探す
if (!matchFlg) continue;
//resultPointとpCを結ぶ線があるか?
for (var j:int = 0; j < numLength; j++) {
if ( (pA.equals(resultLines[j].pA) && pB.equals(resultLines[j].pB)) ||
(resultLines[i].pA.equals(resultLines[j].pA) && resultLines[i].pB.equals(resultLines[j].pB)) ) {
continue;
}
if ( (pC.equals(resultLines[j].pA) && resultPoint.equals(resultLines[j].pB)) ||
(pC.equals(resultLines[j].pB) && resultPoint.equals(resultLines[j].pA)) ) {
// 三角形発見!
//trace(pA,pB,pC);
completeFlg = true;
// pA,pB,pCを三角形として登録候補にあげる
// 既に出来上がっている三角形と同じものでないか調べる
var numTriangles:int = triangles.length/3;
for (var k:int = 0; k < numTriangles; k++) {
if ( (pA.equals(triangles[k*3]) && pB.equals(triangles[k*3+1]) && pC.equals(triangles[k*3+2])) ||
(pA.equals(triangles[k*3]) && pC.equals(triangles[k*3+1]) && pB.equals(triangles[k*3+2])) ||
(pB.equals(triangles[k*3]) && pA.equals(triangles[k*3+1]) && pC.equals(triangles[k*3+2])) ||
(pB.equals(triangles[k*3]) && pC.equals(triangles[k*3+1]) && pA.equals(triangles[k*3+2])) ||
(pC.equals(triangles[k*3]) && pA.equals(triangles[k*3+1]) && pB.equals(triangles[k*3+2])) ||
(pC.equals(triangles[k*3]) && pB.equals(triangles[k*3+1]) && pA.equals(triangles[k*3+2])) ){
completeFlg = false;
break;
}
}
if (completeFlg) {
linesArr = new Array(i,j);
triangles.push(pA,pB,pC);
break;
}
}
}
// 三角形が出来上がったら
if (completeFlg) {
linesArr = linesArr.sort(Array.NUMERIC | Array.DESCENDING);
resultLines.splice(linesArr[0],1);
resultLines.splice(linesArr[1],1);
obj.graphics.lineStyle(NaN);
obj.graphics.beginFill(Math.random()*0xffffff);
obj.graphics.moveTo(pA.x, pA.y);
obj.graphics.lineTo(pB.x, pB.y);
obj.graphics.lineTo(pC.x, pC.y);
break;
}
}
}
}
// 図形の内向きの線チェック
private function checkInner(ind:int, rT:Number):Boolean {
var innerFlg:Boolean=false;
// 描画オブジェクトの範囲内となる角度を得る
var beforeInd:int=ind>0?ind-1:NP-1;
var afterInd:int=ind;
// この頂点に向かうベクトルを反転して
var rBefore:Number=vectors[beforeInd]-Math.PI;
var rAfter:Number=vectors[afterInd];
// 正の値にしておく
if (rBefore < 0) {
rBefore+=Math.PI*2;
}
// 単純に計算できるのはrBefor>rToの場合
// 誰かもっとエレガントにしてください
if (rBefore >= rAfter) {
if (rBefore>rT&&rT>rAfter) {
innerFlg=true;
}
} else {
if (rBefore < rT && rT < rAfter) {
} else {
innerFlg=true;
}
}
return innerFlg;
}
// pFromからpToの角度を0~Math.PI*2の範囲で取得する
private function getVector(pFrom:Point, pTo:Point):Number {
var radian:Number = Math.atan2((pTo.y-pFrom.y),(pTo.x-pFrom.x));
if (radian<0) {
radian+=Math.PI*2;
}
return radian;
}
// 配列に含まれる線を描画
private function dispLines(lines:Array, t:Number=4, c:int=0):void {
var numLines:int=lines.length;
for (var i:int = 0; i < numLines; i++) {
graphics.lineStyle(t,c);
graphics.moveTo(lines[i].pA.x,lines[i].pA.y);
graphics.lineTo(lines[i].pB.x,lines[i].pB.y);
}
}
// 2本の直線の交点を計算
// 交点が存在する場合trueを返す
private function getCrossPoint(lineA:DivideLine, lineB:DivideLine):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.pA.x-lineA.pA.x;
var dy:Number=lineB.pA.y-lineA.pA.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) {
var crossPoint:Point = new Point();
crossPoint.x=lineA.pA.x+lineA.f*t1;
crossPoint.y=lineA.pA.y+lineA.g*t1;
// 交点が始点か終点と同じなら交差してないことにする
if (crossPoint.equals(lineA.pA)||crossPoint.equals(lineA.pB)) {
} else {
flg=true;
}
}
}
return flg;
}
}
}
import flash.geom.Point;
class DivideLine {
public var distance:Number;
public var f:Number;
public var g:Number;
public var pA:Point;
public var pB:Point;
public function DivideLine(pointA:Point, pointB:Point):void {
pA = pointA;
pB = pointB;
distance = Point.distance(pA,pB);
f=pB.x-pA.x;
g=pB.y-pA.y;
}
}
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.Event;
class DraggablePoint extends Sprite {
public function DraggablePoint(radius:Number=5, color:int=0xff0000, num:String=null):void {
graphics.beginFill(color);
graphics.drawCircle(0,0,radius);
graphics.endFill();
addEventListener(MouseEvent.MOUSE_DOWN, dragStart);
}
private function dragStart(e:MouseEvent):void {
startDrag();
stage.addEventListener(MouseEvent.MOUSE_UP, dragStop);
e.target.removeEventListener(MouseEvent.MOUSE_DOWN, dragStart);
addEventListener(MouseEvent.MOUSE_MOVE, xMove);
}
private function dragStop(e:MouseEvent):void {
stopDrag();
stage.removeEventListener(MouseEvent.MOUSE_UP, dragStop);
e.target.addEventListener(MouseEvent.MOUSE_DOWN, dragStart);
//removeEventListener(MouseEvent.MOUSE_MOVE, xMove);
}
private function xMove(e:MouseEvent):void {
var myEventObject:Event = new Event("PointMove");
dispatchEvent(myEventObject);
}
}