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: code format="bb4w" PROCassemblepip code Once that has been done the test can be carried out simply by making the following **SYS** call: code format="bb4w" SYS `PtInPoly`, pt{(0)}, nvert%, x%, y% TO res% code This is directly equivalent to (but much faster than) this code using the Windows API: code format="bb4w" SYS "CreatePolygonRgn", pt{(0)}, nvert%, 1 TO hrgn% SYS "PtInRegion", hrgn%, x%, y% TO res% SYS "DeleteObject", hrgn% code 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): code format="bb4w" nvert% = 6 DIM pt{(nvert%-1) x%,y%} code Once created the vertices should be initialised with the appropriate coordinates, for example: code format="bb4w" FOR V% = 0 TO nvert%-1 READ pt{(V%)}.x%, pt{(V%)}.y%     NEXT code Here the coordinates are assumed to be contained within DATA statements.

Finally, here is the code of **PROCassemblepip**: code format="bb4w" 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 code