Sutherland - Hodgeman polygon clip

Discuss any general programming issues here
Post Reply
User avatar
lazi
Posts: 625
Joined: Thu Feb 24, 2011 11:08 pm

Sutherland - Hodgeman polygon clip

Post by lazi »

Code: Select all

;###########################################################
;#                                                         #
;#     Sutherland-Hodgeman polygon clipping algorithm      #
;#                        1.0                              #
;#   Ported to Hollywood by Lazi from wikipedia article:   #
;#                                                         #
;###########################################################



Const #_INSIDE = 0; // 0000
Const #_LEFT = 1;   // 0001
Const #_RIGHT = 2;  // 0010
Const #_BOTTOM = 4; // 0100
Const #_TOP = 8;    // 1000



;// Compute the bit code For a point (x, y) using the clip rectangle
;// bounded diagonally by (xmin, ymin), and (xmax, ymax)


;// ASSUME THAT xmax , xmin , ymax and ymin are global constants.

Function ComputeOutCode(x, y, idx)
        Local code = #_INSIDE;       // initialised as being inside of clip window

        If (x < w_xmin)                  ; To the left of clip window
                code = code | #_LEFT
        ElseIf (x > w_xmax)      ; To the right of clip window
                code = code | #_RIGHT
        EndIf
        If (y < w_ymin)                  ; below the clip window
                code = code | #_TOP
        ElseIf (y > w_ymax)      ; above the clip window
                code = code | #_BOTTOM
        EndIf

        If idx Then code=code &idx

        Return (code)
EndFunction

; Cohen&Sutherland clipping algorithm clips a line from
; P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with
; diagonal from (xmin, ymin) to (xmax, ymax).

Function CohenSutherlandLineClip(x0, y0, x1, y1,idx)

        ; compute outcodes for P0, P1, and whatever point lies outside the clip rectangle
        Local outcode0 = ComputeOutCode(x0, y0,idx)
        Local outcode1 = ComputeOutCode(x1, y1,idx)


        Local accept = False

        Repeat
                If ((outcode0 | outcode1) = 0)  ; Bitwise OR is 0. Trivially accept and get out of loop
                        accept = True
                        Break
                ElseIf (outcode0 & outcode1)  ; Bitwise AND is not 0. Trivially reject and get out of loop
                        Break
                Else
                        ; failed both tests, so calculate the line segment to clip
                        ; from an outside point to an intersection with clip edge
                        Local x, y

                        ; At least one endpoint is outside the clip rectangle; pick it.
                        Local outcodeout=IIf(outcode0, outcode0, outcode1)

                        ; Now find the intersection point;
                        ; use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
                        
                        If (outcodeOut & #_TOP)  ;// point is below the clip rectangle
                                x = x0 + (x1 - x0) * (w_ymin - y0) / (y1 - y0)
                                y = w_ymin
                        ElseIf (outcodeOut & #_BOTTOM)            ;// point is above the clip rectangle
                                x = x0 + (x1 - x0) * (w_ymax - y0) / (y1 - y0)
                                y = w_ymax

                        ElseIf (outcodeOut & #_RIGHT)   ;// point is To the right of clip rectangle
                                y = y0 + (y1 - y0) * (w_xmax - x0) / (x1 - x0)
                                x = w_xmax
                        ElseIf (outcodeOut & #_LEFT)    ;// point is To the left of clip rectangle
                                y = y0 + (y1 - y0) * (w_xmin - x0) / (x1 - x0)
                                x = w_xmin
                        EndIf
                        ; Now we move outside point to intersection point to clip
                        ; and get ready for next pass.
                        If (outcodeOut = outcode0)
                                x0 = x
                                y0 = y
                                outcode0 = ComputeOutCode(x0, y0,idx)
                        Else
                                x1 = x
                                y1 = y
                                outcode1 = ComputeOutCode(x1, y1, idx)
                        EndIf
                EndIf
        Forever
        If (accept)
               Return(x0, y0, x1, y1)
        Else
               Return(False)
        EndIf
EndFunction



Function PolygonClipping(vertex,x,y)

        Local cropped={}
        Local n=ListItems(vertex)/2


        Local idxs={#_TOP,#_BOTTOM,#_LEFT,#_RIGHT}
        Local i, idx, nxt
        Local x1,y1,x2,y2

        nxt,idx=NextItem(idxs,nil)

        While GetType(nxt) <> #NIL

             If n

                InsertItem(vertex,vertex[0])
                InsertItem(vertex,vertex[1])
                  For i=0 To n-1

                      If not ComputeOutCode (vertex[i*2]+x , vertex[i*2+1]+y, idx) ;#_INSIDE
                          InsertItem (cropped , vertex[i*2])
                          InsertItem (cropped , vertex[i*2+1])

                          If ComputeOutCode (vertex[(i+1)*2]+x , vertex[(i+1)*2+1]+y, idx) ; <> #_INSIDE

                             x1,y1,x2,y2 = CohenSutherlandLineClip (vertex[i*2]+x , vertex[i*2+1]+y , vertex[(i+1)*2]+x , vertex[(i+1)*2+1]+y, idx)

                             InsertItem (cropped , x2-x)
                             InsertItem (cropped , y2-y)

                          EndIf
                      Else
                          If not ComputeOutCode (vertex[(i+1)*2]+x , vertex[(i+1)*2+1]+y, idx) ;= #_INSIDE
                             x1,y1,x2,y2 = CohenSutherlandLineClip (vertex[i*2]+x , vertex[i*2+1]+y , vertex[(i+1)*2]+x , vertex[(i+1)*2+1]+y, idx)

                             InsertItem (cropped , x1-x)
                             InsertItem (cropped , y1-y)
                          
                          EndIf
                      EndIf
                  Next
             EndIf
             nxt,idx=NextItem(idxs,nxt)
             n=ListItems(cropped)/2
             vertex=copytable(cropped)
             cropped={}
        Wend

        Return(vertex,n)
EndFunction


;cropping window position

w_xmax=GetAttribute(#DISPLAY,0,#ATTRWIDTH) -30
w_ymax=GetAttribute(#DISPLAY,0,#ATTRHEIGHT)-30
w_xmin=30
w_ymin=30

vertex={0,0,20,0,20,80,60,50,60,-20,100,120,-20,120,0,0}

Repeat

x=MouseX()
y=MouseY()

SetFillStyle(#FILLNONE)
Box(w_xmin,w_ymin,w_xmax-w_xmin,w_ymax-w_ymin,#YELLOW)

SetFillStyle(#FILLCOLOR)
Polygon(x,y,vertex,8,#WHITE)

cropped,points=PolygonClipping(CopyTable(vertex),x,y)

If points>0 Then Polygon(x,y,cropped,points,#RED)

Wait(2)
Cls
Forever                                     
Post Reply