Divide Polygon
/**
* Copyright Kay ( http://wonderfl.net/user/Kay )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/y3sg
*/
// 多角形をポリゴンで自動分割してみるTest
// >頂点をドラッグすると計算をはじめます
// ・すべての点を繋ぐ線を描画しています
// ・分割候補となる多角形の中に収まる線は太線
// ・いちばん目立つ線が分割線です。
// ?精度誤差回避したくない
package {
import flash.display.Sprite;
import flash.display.Shape;
import flash.events.Event;
import flash.geom.Point;
public class SeparatePolygon 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 SeparatePolygon():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;
}
// ポイント繋いだ図形を描画
obj.graphics.clear();
obj.graphics.lineStyle(2,0xccccff,0.5);
obj.graphics.beginFill(0xccccff,0.5);
obj.graphics.drawPath(commands,datas);
obj.graphics.endFill();
// 外周線と進行角度を配列に格納
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 SeparateLine(pFrom,pTo));
vectors.push(getVector(pFrom,pTo));
}
// すべての接続線を描く
var separateLines: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]);
graphics.lineStyle(0,0xeeeeee);
graphics.moveTo(pFrom.x,pFrom.y);
graphics.lineTo(pTo.x,pTo.y);
// 外周線と交差しないかチェック
var outFlg:Boolean = false;
var myLine:SeparateLine=new SeparateLine(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))) {
separateLines.push(myLine);
graphics.lineStyle(2,0xdddddd);
graphics.moveTo(pFrom.x,pFrom.y);
graphics.lineTo(pTo.x,pTo.y);
}
}
}
// 分割線を短いものから確定していく
var resultLines:Array = new Array();
separateLines.sortOn("distance", Array.NUMERIC);
while (separateLines.length > 0) {
var shortLine:SeparateLine = (separateLines.splice(0,1))[0];
resultLines.push(shortLine);
// 短い線と交わる線を配列から取り除く
var targetInd:int=0;
while (separateLines.length > targetInd) {
if (getCrossPoint(shortLine,separateLines[targetInd])) {
separateLines.splice(targetInd,1);
} else {
targetInd++;
}
}
}
dispLines(resultLines);
}
// 図形の内向きの線チェック
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:SeparateLine, lineB:SeparateLine):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 SeparateLine {
public var distance:Number;
public var f:Number;
public var g:Number;
public var pA:Point;
public var pB:Point;
public function SeparateLine(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);
}
}