Code archives/Algorithms/point_in_polygon

This code has been declared by its author to be Public Domain code.

Download source code

point_in_polygon by big10p2007
Here's a simple demo of point_in_polygon() - a pretty fast, compact function I converted from a C function I found on the web.
Graphics 800,600,0,2
SetBuffer BackBuffer()

SeedRnd MilliSecs()

Const POLY_VERTS = 100

; Create banks to hold polygon's vertex X/Y coords.
bank_x = CreateBank(POLY_VERTS * 4)
bank_y = CreateBank(POLY_VERTS * 4)

; Create a random, convex polygon.
da# = 360.0 / POLY_VERTS
ang# = 0.0

For i = 0 To (BankSize(bank_x) - 1) Step 4
	dist# = Rnd(50,300)
	PokeFloat bank_x, i, (GraphicsWidth() / 2) + (Cos(ang) * dist)
	PokeFloat bank_y, i, (GraphicsHeight() / 2) + (Sin(ang) * dist)
	ang = ang + da
Next
	
; Main loop.
While Not KeyHit(1)

	Cls
		
	inside = point_in_polygon(MouseX(), MouseY(), bank_x, bank_y)
		
	If inside
		Color 255,0,0
		status$ = "INSIDE"
	Else
		Color 255,255,255
		status$ = "OUTSIDE"
	EndIf
		
	draw_polygon(bank_x, bank_y)
		
	Color 255,255,0
	Text 10,10,"Mouse is " + status$ + " polygon!"
		
	Flip 1

Wend

End


;
; Determines whether a point lies inside a convex polygon.
;
; Params:
; x,y    - Coords of point to check.
; vert_x - Float bank holding polygon vertex X coords.
; vert_y - Float bank holding polygon vertex Y coords.
;
; Returns:
; True if the point is inside the polygon, False otherwise.
;
Function point_in_polygon(x#, y#, vert_x, vert_y)

	in = False
	
	last_byte = BankSize(vert_x) - 1

	For i = 0 To last_byte Step 4
	
		If i Then j = (i - 4) Else j = (last_byte - 3)
		
		x1# = PeekFloat(vert_x,i)
		y1# = PeekFloat(vert_y,i)

		x2# = PeekFloat(vert_x,j)
		y2# = PeekFloat(vert_y,j)

		If ((((y1 <= y) And (y < y2)) Or ((y2 <= y) And (y < y1))) And (x < (((x2 - x1) * (y - y1)) / (y2 - y1)) + x1))
			in = Not in
		EndIf

	Next

	Return in
	
End Function


;
; Draws a polygon.
;
; Params:
; vert_x - Float bank holding polygon vertex X coords.
; vert_y - Float bank holding polygon vertex Y coords.
;
Function draw_polygon(vert_x, vert_y)

	last_byte = BankSize(vert_x) - 1

	For i = 0 To last_byte Step 4
	
		If i Then j = (i - 4) Else j = (last_byte - 3)
		
		x1# = PeekFloat(vert_x,i)
		y1# = PeekFloat(vert_y,i)

		x2# = PeekFloat(vert_x,j)
		y2# = PeekFloat(vert_y,j)

		Line x1, y1, x2, y2

	Next

End Function

Comments

Chroma2011
This is ace. I'm currently using this with verlet and it's works great. Very fast too.


big10p2011
Glad you found it useful. :)


JBR2011
I've converted it to arrays for a bit more speed.(37ms vs 57ms). Only downside is arrays are not passed to functions

Graphics 800,600,0,2
SetBuffer BackBuffer()

SeedRnd MilliSecs()

Const POLY_VERTS = 100

; Create arrays to hold polygon's vertex X/Y coords.

Dim A_x#( POLY_VERTS )
Dim A_y#( POLY_VERTS )


; Create a random, convex polygon.
da# = 360.0 / POLY_VERTS
ang# = 0.0

For i = 0 To  POLY_VERTS-1
	dist# = Rnd(50,300)
	A_x#( i ) = (GraphicsWidth() / 2) + (Cos(ang) * dist)
	A_y#( i ) = (GraphicsHeight() / 2) + (Sin(ang) * dist)
	ang = ang + da
Next

A_x# ( POLY_VERTS ) = A_x#(0)		; easy wrapping
A_y# ( POLY_VERTS ) = A_y#(0)
	
; Main loop.
While Not KeyHit(1)

	Cls
	
	x# = MouseX()
	y# = MouseY()
	time=MilliSecs()
	
	For i=1 To 10000
		
		inside = point_in_polygon(x#, y#)
		
	Next
	Text 0,0,MilliSecs()-time
		
	If inside
		Color 255,0,0
		status$ = "INSIDE"
	Else
		Color 255,255,255
		status$ = "OUTSIDE"
	EndIf
		
	draw_polygon()
		
	Color 255,255,0
	Text 10,10,"Mouse is " + status$ + " polygon!"
		
	Flip 1

Wend

End


;
; Determines whether a point lies inside a convex polygon.
;
; Params:
; x,y    - Coords of point to check.
;
; Returns:
; True if the point is inside the polygon, False otherwise.
;
Function point_in_polygon(x#, y#)

	in = False
	
	For i = 0 To POLY_VERTS-1
	
		x1# = A_x#(i)
		y1# = A_y#(i)

		x2# = A_x#(i+1)
		y2# = A_y#(i+1)

		If ((((y1 <= y) And (y < y2)) Or ((y2 <= y) And (y < y1))) And (x < (((x2 - x1) * (y - y1)) / (y2 - y1)) + x1))
			in = Not in
		EndIf

	Next

	Return in
	
End Function


;
; Draws a polygon.
Function draw_polygon()

	For i = 0 To POLY_VERTS-1
	
		x1# = A_x#(i)
		y1# = A_y#(i)

		x2# = A_x#(i+1)
		y2# = A_y#(i+1)

		Line x1, y1, x2, y2

	Next

End Function



Charrua2011
hi

"Only downside is arrays are not passed to functions"



use Blitz arrays, only restriction is that must be unidimensional

Local A#[10], i

For i=0 To 10
 A[i]=0.1*i
 DebugLog a[i]
Next

ClearArray(a)

For i=0 To 10
 DebugLog a[i]
Next

WaitKey

End

Function ClearArray(array#[10])
	Local i
	For i=0 To 10
		array[i]=0
	Next
End Function


Juan


Bobysait2015
Here is the most optimised version I get at the moment

(50-75% faster than JBR's code)




Flanker2016
Thanks guys for the code. Bobysait's version is lightning fast !


Bobysait2016
I didn't remember this code ...
Good to add to my library, here is a blitzmax conversion
(so much faster than blitz3d version -> like 2 or 3 times faster)




BlitzSupport2016
Wow, that's amazing, Bobysait -- your version has gone from ~14ms down to ~2ms -- the .bb version could easily take up most of a frame, but the .bmx version is way faster than "2 or 3 times" here!


Bobysait2016
Glad you like it, just take care that the speed is not due to you test outside of the bounding box, there is two tests instead of one :
- the first step is a simple bounding rect test, very fast while it removes lots of potential useless tests, it also does not show the worst cases.
So if you tested with cursor out of the bounding rect, you only get the first step.

But yeah, even in worst case, it's still really faster (and that's due to blitzmax essentially, the code has not really been improved, it's just a bit more OOP)

And BTW, this is a small improvment for the b3d version (around 30-40% faster if you use Float array, 100% if you use Int array)



using Int instead of float reduice by 2 the time compute the tests and using a static array to store polygon data (box and vertex count) is faster than using "Fields".
> convert the "%" to "#" to use floats instead of Ints, it will still work faster.



and here is a small improvment for the blitzmax version


It uses a float ptr to traverse the points in the test.


Flanker2016
I noticed that the routine will return false if we are actually on one edge. It can be fixed if necessary by using <= and >= in the point_in_polygon() function checks. I presume it uses the crossing method with odd and even numbers replaced by "in = Not in".


Code Archives Forum