=====Point-in-Polygon hit testing=====
//by Richard Russell, September 2010//\\ \\ A common requirement in graphics programming is to determine whether a point is inside or outside a polygon (which can be regular or irregular, concave or convex). Since any closed curve can be approximated by a polygon, given a sufficient number of vertices, this can be extended to testing whether a point is inside or outside an arbitrary boundary.\\ \\ One approach is to use the **Windows API**. The polygon can be converted to a //region// using the **CreatePolygonRgn** API and the hit test then carried out using the **PtInRegion** API; however this is a relatively slow operation. This article presents an alternative method using an assembly language routine to perform the test directly.\\ \\ To use this method the code must first be assembled, during the initialisation phase of your program, by calling **PROCassemblepip** as follows:
PROCassemblepip
Once that has been done the test can be carried out simply by making the following **SYS** call:
SYS `PtInPoly`, pt{(0)}, nvert%, x%, y% TO res%
This is directly equivalent to (but much faster than) this code using the Windows API:
SYS "CreatePolygonRgn", pt{(0)}, nvert%, 1 TO hrgn%
SYS "PtInRegion", hrgn%, x%, y% TO res%
SYS "DeleteObject", hrgn%
In both cases **pt{()}** is an array of POINT structures containing the vertices of the polygon, **nvert%** is the total number of vertices in the polygon and **x%,y%** defines the point to be tested. The returned value **res%** is set non-zero if the point is inside the polygon and zero if it is outside.\\ \\ The array of POINT structures can typically be created as follows (in this case for a hexagon):
nvert% = 6
DIM pt{(nvert%-1) x%,y%}
Once created the vertices should be initialised with the appropriate coordinates, for example:\\
FOR V% = 0 TO nvert%-1
READ pt{(V%)}.x%, pt{(V%)}.y%
NEXT
Here the coordinates are assumed to be contained within DATA statements.\\ \\ Finally, here is the code of **PROCassemblepip**:
DEF PROCassemblepip
LOCAL L%, P%, code%, pass%
DIM code% 120, L% -1
FOR pass% = 8 TO 10 STEP 2
P% = code%
[OPT pass%
.`PtInPoly`
mov edi,[esp+4] ; lpPoints
mov ecx,[esp+8] ; nCount
mov ebx,[esp+12] ; X
mov ebp,[esp+16] ; Y
mov esi,edi
xor eax,eax
dec ecx
.piploop
pushad
mov ecx,[edi]
mov edx,[edi+4]
mov esi,[edi+8]
mov edi,[edi+12]
call intersect
xor [esp+28],eax
popad
add edi,8
loop piploop
pushad
mov ecx,[edi]
mov edx,[edi+4]
mov edi,[esi+4]
mov esi,[esi]
call intersect
xor [esp+28],eax
popad
ret 16
.intersect
cmp edx,ebp ; y1 > y0 ?
setg al
cmp edi,ebp ; y2 > y0 ?
setg ah
xor al,ah ; eor
jz bailout
cmp edx,edi ; y1 > y2 ?
setg al
sub esi,ecx ; dx0 = x2 - x1
sub edi,edx ; dy0 = y2 - y1
sub ebx,ecx ; dx2 = x0 - x1
sub ebp,edx ; dy2 = y0 - y1
imul esi,ebp ; dx0 * dy2
imul edi,ebx ; dy0 * dx2
cmp esi,edi ; > ?
setg ah
xor al,ah ; eor
.bailout
movzx eax,al
ret
]
NEXT pass%
ENDPROC