Point in Poly?

BlitzMax Forums/BlitzMax Programming/Point in Poly?

Gabriel(Posted 2006) [#1]
I've been thinking about this for interface stuff, avoiding making everything rectangular really.

Here's my proposed method ( technique, not even pseudo-code at this point ) and I'd be interested to hear if anyone has a better ( faster! ) method.

For each edge in the polygon, calculate a normal pointing directly outward from the center of the poly. When it comes to testing the point, calculate the dot product of the point relative to one of the edge's vertices and if it's positive, return false. If none of the edges return false, it's inside.

Perhaps coupled with some kind of basic distance check to eliminate precise testing.

How's that?


sswift(Posted 2006) [#2]
You can check point in poly with 100% precision by testing which side of each line the point is on. If it is on the inside side of all three, then the point is in the poly.

I think there is a point in poly gunction in the code archives that I put there. But I know there is a function I put there which tells you which side of a line you are on. The code is B3D code, but it is easy enough for you to port.

Actually, going back over it again, the method you describe sounds pretty similar to this method. But you test the dot product of the point and the line's normal, not the vertices. A distance check might speed things up a little, but if you're talking an interface then it is totally uneccessary to optimize it. There's no way you'd test even 1/100000th of the number of polygons you would need to for it to matter.


skidracer(Posted 2006) [#3]
Here's my InsidePoly routine.


TartanTangerine (was Indiepath)(Posted 2006) [#4]
Here is mine, the vertexes are already transformed, X & Y are the screen position of the poly.

Method PointInside(x#,y#)
			Local i, j
			Local bInPoly = 0
			Local xt1#,xt2#,yt1#,yt2#
			j = Self.Col_VertexCount - 1 
			For i = 0 To Self.Col_VertexCount - 1 
				xt1 = Self.ColVertex[j].fx + self.x
				xt2 = Self.ColVertex[i].fx + self.x
				yt1 = Self.ColVertex[j].fy + self.y
				yt2 = Self.ColVertex[i].fx + Self.y
				If x < ( (xt1 - xt2) * (y - yt2) / (yt1 - yt2) + xt2 ) And ((yt2 <= y And y < yt1) Or (yt1 <= y And y < yt2)) Then  bInPoly = 1 - bInPoly
				j = i
			Next
			Return bInPoly
		End Method



Gabriel(Posted 2006) [#5]
Thanks guys, it looks like I was on the right track, but not quite there. I'll try them out and see which is the best fit, and it's good to know that speed won't be an issue for an interface.


Brucey(Posted 2006) [#6]
I use a bounding box test first, and if it's in that I run another test based on the following :
http://www.geometryalgorithms.com/Archive/algorithm_0103/algorithm_0103.htm
which looks like the same method that Indiepath posted.


Oddball(Posted 2006) [#7]
Don't know if it's faster, but here's my PointInPoly function.


SpaceAce(Posted 2006) [#8]
Below is my implementation of the Winding Number method. I ripped this out of my SA_GameMod libraries, so it used to be a method of my one of my GameMod types and it requires a simple struct-style "Point" which I will also post below.

  ' Function arguments: 
  ' _x = Mouse X position
  ' _y = Mouse Y position
  ' Points = An array of SA_GameObject_Point objects
  ' Returns:
  ' 0 if point _x, _y is outside polygon
  ' 1 if point _x, _y is inside polygon
  Function IsIn%(_x#, _y#, Points:SA_GameObject_Point[])
    Local xs% = 0

    For Local i% = 0 To Points.Length-2
    	If (( (Points[i].y <= _y) And (Points[i+1].y > _y) ) Or ( (Points[i].y > _y) And (Points[i+1].y <= _y) ))
    		Local sect# = Float((_y - Points[i].y) / (Points[i+1].y - Points[i].y))
    		If (_x < Points[i].x + sect*(Points[i+1].x - Points[i].x)) xs :+ 1
    	End If
    Next
    Return xs&1
  End Method


The Point type:
' ========================================
' SA_GameObject_Point - A "point" on the
' drawing surface or board defined by X, Y.
' Mainly used to define polygons.
' ========================================

Type SA_GameObject_Point
	Field x#, y#
	
       ' Creates and returns an SA_GameObject_Point
       ' of _x, _y
	Function Create:SA_GameObject_Point(_x#, _y#)
	  Local p:SA_GameObject_Point = new SA_GameObject_Point
		p.x = _x
		p.y = _y
		Return p
	End Function
End Type


Like I said, I ripped this from my SA_GameMod files and made a few quick edits to make it stand-alone but I haven't tested it as stand-alone. It works perfectly in my game mod, though.

SpaceAce