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

多角形の内外判定

マウスが画面に描画されている多角形の内側(白い部分)かどうかを判定
します

方針)
 ・対象点から任意の方向にレイを発射し、多角形の線分との交差数をカ
  ウントする。
 ・交差数が偶数の場合、外側。奇数の場合は、内側になる。

また、線分の交差判定のアルゴリズムは下記の文献を参考にしています。

参考)
ゲームプログラミングのためのリアルタイム衝突判定(Real-Time Collision Detection)
http://www.amazon.co.jp/dp/493900791X
/**
 * Copyright potix2 ( http://wonderfl.net/user/potix2 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/84wa
 */

/**
マウスが画面に描画されている多角形の内側(白い部分)かどうかを判定
します

方針)
 ・対象点から任意の方向にレイを発射し、多角形の線分との交差数をカ
  ウントする。
 ・交差数が偶数の場合、外側。奇数の場合は、内側になる。

また、線分の交差判定のアルゴリズムは下記の文献を参考にしています。

参考)
ゲームプログラミングのためのリアルタイム衝突判定(Real-Time Collision Detection)
http://www.amazon.co.jp/dp/493900791X
*/
package {
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.text.TextField;
	import flash.utils.Timer;
	
	[SWF(width = 465, height = 465, backgroundColor = 0x0, frameRate = 60)]
	public class IsPointInPolygonTest extends Sprite {
	private static const CANVAS_WIDTH:int = 1000;
	private static const CANVAS_HEIGHT:int = 1000;
	private static const DEFAULT_NUM_OF_VERTICES:int = 3;
	private static const REFRESH_INTERVAL:int = 5000; //5秒ごとに図形を再描画
	private var console:TextField;
	private var canvas:Sprite;
	private var vertices:Array;
	private var refreshTimer:Timer;
	private var numOfVertices:int;
	public function IsPointInPolygonTest()
	{
		numOfVertices = DEFAULT_NUM_OF_VERTICES;
		addEventListener(Event.ADDED_TO_STAGE, initialize);
	}
	
	private function setup():void {
		console = new TextField();
		console.width = stage.stageWidth;
		console.height = stage.stageHeight / 3;
		console.textColor = 0xFFFFFF;
		console.y = stage.stageHeight / 3 * 2;
		addChild(console);
	}
	
	private function initialize(event:Event):void {
		removeEventListener(Event.ADDED_TO_STAGE, initialize);
		setup();
		
		//多角形の頂点を生成
		initCanvas();
		initVertices();
		initListeners();
		initTimer();
		draw();
	}

	private function initCanvas():void {
		canvas = addChild(new Sprite()) as Sprite;
		canvas.width = stage.stageWidth;
		canvas.height = stage.stageHeight * 2 / 3;
	}
	
	private function initVertices():void {
		vertices = new Array();
		for (var i:int = 0; i < numOfVertices; i++)
		{
			vertices.push(new Point(Math.random() * 465, Math.random() * 310));
		}
	}
	
	private function initListeners():void {
		stage.addEventListener(MouseEvent.MOUSE_MOVE, check);
	}

	private function initTimer():void {
	    refreshTimer = new Timer(REFRESH_INTERVAL, 0);
	    refreshTimer.addEventListener(TimerEvent.TIMER, refresh);
	    refreshTimer.start();
    }

	private function draw():void {
		var g:Graphics = this.graphics;
		g.clear();
		g.beginFill(0xFFFFFF);
		g.moveTo(vertices[0].x, vertices[0].y);
		for (var i:int = 1; i < vertices.length; i++)
		{
			g.lineTo(vertices[i].x, vertices[i].y);
		}
		g.lineTo(vertices[0].x, vertices[0].y);
		g.endFill();
	}

	private function check(event:MouseEvent):void {
		var clickPoint:Point = new Point(event.stageX, event.stageY);
		_debug((isPointInPolygon(this.vertices, clickPoint) ? 'in' : 'out') + ':' +clickPoint.toString());
	}
	
	private function refresh(event:TimerEvent):void {
		numOfVertices++;
		initVertices();
		draw();
	}
	
	//geometry functions
	public static function signed2DTriArea(a:Point, b:Point, c:Point):Number {
		return (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x);
	}
	
	public static function test2DSegmentSegment(a:Point, b:Point, c:Point, d:Point):Point {
		var a1:Number = signed2DTriArea(a, b, d);
		var a2:Number = signed2DTriArea(a, b, c);
		if ( a1 * a2 < 0.0 ) {
			var a3:Number = signed2DTriArea(c, d, a);
			var a4:Number = a3 + a2 - a1;
			if  ( a3 * a4 < 0.0 ) {
				var t:Number = a3  / (a3 - a4);
				var p:Point = new Point(a.x + t * (b.x - a.x), a.y + t * (b.y - a.y));
				return p;
			}
		}
		
		return null;
	}
	
	public static function isPointInPolygon(polygonVertices:Array, target:Point):Boolean {
		//レイを発射する方向
		var destPoint:Point = new Point(CANVAS_WIDTH, CANVAS_HEIGHT);
		var crossCount:uint = 0;
		for ( var i:int = 0; i < polygonVertices.length; i++ ) {
			var a:Point = polygonVertices[i] as Point;
			var b:Point = polygonVertices[(i + 1) % polygonVertices.length] as Point;
			var crossPoint:Point = test2DSegmentSegment(a, b, target, destPoint);
			if ( crossPoint != null ) {
				crossCount++;
			}
		}
		return (crossCount % 2) == 1;
	}
	
	//helpers
	private function _debug(message:String):void {
		console.text = message + "\n" + console.text;
	}
}

}