! Geometric Utilities for EDWIN, capitalising on device specific features.

!############################################################################
!#                                                                          #
!#  This is a module from the EDWIN Graphics Package, which was developed   #
!#  in the Department of Computer Science, at Edinburgh University, from    #
!#  1978 to the present day, release 5 of EDWIN in October 1984.            #
!#                                                                          #
!#  The principal author of the EDWIN Graphics Package was J Gordon Hughes, #
!#  while working for the Edinburgh University Computer Sceince Department. #
!#  Parts of EDWIN have been produced by many different people, too many    #
!#  too mention, working for different departments of Edinburgh and Leeds   #
!#  Universities.                                                           #
!#                                                                          #
!#  This module is regarded as being in the public domain, and the authors  #
!#  and accept no responsibility regarding the uses to which the software   #
!#  will be put.                                                            #
!#                                                                          #
!############################################################################

record format POINTFM (integer X, Y)
record format LINEFM (long real A, B, C)

! IMP Maths routines
from Imp include maths

! Routines from EDWIN
from Edwin include consts
from Edwin include specs
from Edwin include shapes
from Edwin include iprocs
from Edwin include icodes

const integer MAX POINT = 360

external routine RECTANGLE alias "EDWIN_RECTANGLE" (integer XL, YL, XU, YU)
   ! This draws a rectangle.
   integer SHADE MODE

   PDF INSERT (13, XL, YL);   PDF INSERT (13, XU, YU)

   GET ATTRIBUTE (ATT SHADE MODE, SHADE MODE)
   if SHADE MODE = OUTLINE start
       CLIP (XL, YL, 0)
       CLIP (XU, YL, 1);   CLIP (XU, YU, 1)
       CLIP (XL, YU, 1);   CLIP (XL, YL, 1)
   else
       MAP TO DEVICE COORDS (XL, YL)
       DRIVE DEVICE (12, XL, YL)
       MAP TO DEVICE COORDS (XU, YU)
       DRIVE DEVICE (13, XU, YU)
   finish
end

! This is a routine for drawing clipped polygons, using the Sutherland-
! Hodgman algorithm, CACM Vol 17, Page 32, Jan 74.

external routine POLYGON alias "EDWIN_POLYGON" (integer NUM E, record (POINTFM) array name AP)
   const integer LAST = 3
   integer PTR, ANY OUT, STAGE, XL, XR, YB, YT, SHADE MODE, L, W
   record (POINTFM) FIRST PT, C, D
   record (POINTFM) array F, S (0:3)
   byte integer array FIRST OF, OUT (0:3)

   routine GET RID OF (record (POINTFM) name P)
      if ANY OUT=FALSE start
          DRIVE DEVICE (10, 3, 0) if SHADE MODE#OUTLINE
          CLIP (P_X, P_Y, 0)
          FIRST PT = P
          ANY OUT = TRUE
      finish
      CLIP (P_X, P_Y, 1)
   end

   routine spec DEAL WITH POINT (record (POINTFM) name P)

   routine OUTPUT (record (POINTFM) name P)
      OUT (STAGE) = TRUE
      GET RID OF (P) and return if STAGE = LAST
      STAGE = STAGE + 1
      DEAL WITH POINT (P)
      STAGE = STAGE - 1
   end

   integer fn INTERSECT (record (POINTFM) name S,P)
      ! Note if point is on the line it is assumed to intersect it.
      switch SW(0:3)
      -> SW(STAGE)

   SW(0): ! XL
          result = TRUE if S_X<=XL<P_X or P_X<=XL<S_X
          result = FALSE

   SW(2): ! XR
          result = TRUE if S_X<=XR<P_X or P_X<=XR<S_X
          result = FALSE
   SW(1): ! YB
          result = TRUE if S_Y<=YB<P_Y or P_Y<=YB<S_Y
          result = FALSE

   SW(3): ! YT
          result = TRUE if S_Y<=YT<P_Y or P_Y<=YT<S_Y
          result = FALSE
   end

   integer fn visible (record (POINTFM) name S)
      switch SW(0:3)
      -> SW(STAGE)

   SW(0): result = TRUE if S_X>=XL
          result = FALSE

   SW(2): result = TRUE if S_X<=XR
          result = FALSE

   SW(1): result = TRUE if S_Y>=YB
          result = FALSE

   SW(3): result = TRUE if S_Y<=YT
          result = FALSE
   end

   routine compute intersect (record (POINTFM) name I, P, S)
      ! Computes intersect I from points P and S.
      switch SW(0:3)
      -> SW(STAGE)

   SW(0): I_X = XL
          I_Y = MUL DIV (P_Y-S_Y, XL-S_X, P_X-S_X) + S_Y
          return

   SW(2): I_X = XR
          I_Y = MUL DIV (P_Y-S_Y, XR-S_X, P_X-S_X) + S_Y
          return

   SW(1): I_X = MUL DIV (P_X-S_X, YB-S_Y, P_Y-S_Y) + S_X
          I_Y = YB
          return

   SW(3): I_X = MUL DIV (P_X-S_X, YT-S_Y, P_Y-S_Y) + S_X
          I_Y = YT
   end

   routine DEAL WITH INTERSECT (record (POINTFM) name P)
       record (POINTFM) I

       if FIRST OF (STAGE)=TRUE start
           COMPUTE INTERSECT (I, P, S(STAGE)) and OUTPUT (I) if INTERSECT (P, S(STAGE)) = TRUE
       else
           F(STAGE) = P
           FIRST OF (STAGE) = TRUE
       finish
   end

   routine DEAL WITH POINT (record (POINTFM) name P)
       DEAL WITH INTERSECT (P)
       S(STAGE) = P
       OUTPUT (P) if VISIBLE (P) = TRUE
   end

   ANY OUT = FALSE
   GET ATTRIBUTE (ATT SHADE MODE, SHADE MODE)
   NUM E = NUM E - 1 if AP(NUM E)_X=AP(1)_X and AP(NUM E)_Y=AP(1)_Y
   if NUM E = 4 start
       ! It is 4 sided, but is it worth making it an orthogonal box?
       if AP(1)_X = AP(2)_X and AP(2)_Y = AP(3)_Y and -
           AP(3)_X = AP(4)_X and AP(4)_Y = AP(1)_Y start
           L = |AP(2)_X - AP(3)_X|
           W = |AP(1)_Y - AP(2)_Y|
           if REM(L,2)=0 and REM(W,2)=0 start { No rounding would arise }
               if AP(2)_X > AP(3)_X then C_X = AP(3)_X else C_X = AP(2)_X
               C_X = C_X + L//2
               if AP(2)_Y > AP(1)_Y then C_Y = AP(1)_Y else C_Y = AP(2)_Y
               C_Y = C_Y + W//2
               -> Do As Polygon if L=0 or W=0 { Co-incident points }
               Rectangle (AP(1)_x, AP(1)_y, AP(3)_x, AP(3)_y)
               return
           finish
       else if AP(1)_Y = AP(2)_Y and AP(2)_X = AP(3)_X and -
                 AP(3)_Y = AP(4)_Y and AP(4)_X = AP(1)_X
           L = |AP(1)_X - AP(2)_X|
           W = |AP(2)_Y - AP(3)_Y|
           if REM(L,2)=0 and REM(W,2)=0 start { No rounding would arise }
               if AP(2)_X > AP(1)_X then C_X = AP(1)_X else C_X = AP(2)_X
               C_X = C_X + L//2
               if AP(2)_Y > AP(3)_Y then C_Y = AP(3)_Y else C_Y = AP(2)_Y
               C_Y = C_Y + W//2
               -> Do As Polygon if L=0 or W=0 { Co-incident points }
               Rectangle (AP(1)_x, AP(1)_y, AP(3)_x, AP(3)_y)
               return
           finish
       finish
   finish
   Do as Polygon:
   if STORING>=0 start;   ! Optimise the adding of the points to the PDF
       STAGE = OUTPUT STREAM
       SELECT OUTPUT (STORING)
       WRITE (6, 1);   WRITE (NUME, 1)
       for PTR = 1, 1, NUME cycle
          WRITE (AP(PTR)_X, 1)
          WRITE (AP(PTR)_Y, 1)
          NEWLINE if PTR&7=0
       repeat
       NEWLINE
       SELECT OUTPUT (STAGE)
   finish
   if Shade Mode = Outline start
       CLIP (AP(1)_X, AP(1)_Y, 0)
       for PTR = 1, 1, NUME cycle
          CLIP (AP(PTR)_X, AP(PTR)_Y, 1)
       repeat
       CLIP (AP(1)_X, AP(1)_Y, 1)
   else
       if CLIPPING>=0 start;   ! Only clip if the user asks to
           INQUIRE WINDOW (XL,XR,YB,YT)
           STAGE = 0
           FIRST OF (PTR) = FALSE and OUT(PTR) = FALSE for PTR=0,1,3
           DEAL WITH POINT (AP(PTR)) for PTR = 1,1,NUM E
           for STAGE = 0,1,3 cycle
               DEAL WITH INTERSECT (F(STAGE)) if OUT(STAGE) = TRUE
           repeat
           return unless ANYOUT=TRUE
       else
           GET RID OF (AP(PTR)) for PTR = 1,1,NUM E
       finish
       GET RID OF (FIRST PT); ! To close the polygon.
       DRIVE DEVICE (10, 1, 0)
   finish
end

external routine CIRCLE alias "EDWIN_CIRCLE" (integer RAD)
   ! RAD is the radius.
   ! The circle is INSIDE the polygon.
   ! The circle drawing routine is based on an original one by JRCC.

   const real D TO R = 57.2958;   ! Magic number converts degrees to rads.
   integer I, X, Y, W, XL, XR, YB, YT, PX, PY, DEVICE, CHORD STEP, STORE
   integer SHADE MODE
   long real RA, RR, CONT
   record (POINTFM) array PTS (1:361)

   PDF INSERT (14, RAD, 0)

   if CLIPPING>=0 start
       INQUIRE POSITION (PX, PY)
       INQUIRE WINDOW (XL, XR, YB, YT)
       return unless XL-RAD <= PX <= XR+RAD and YB-RAD <= PY <= YT+RAD
   finish

   DEVICE = DEVICE DATA_DEV NO
   GET ATTRIBUTE (ATT SHADE MODE, SHADE MODE)
   if DEVICE=HP Plotter or DEVICE=X5A or DEVICE=Versatec or c
       DEVICE=Postscript {%or DEVICE=??? %or DEVICE=???} start
       I = 0;   Y = 0
       MAP TO DEVICE COORDS (I, Y)
       MAP TO DEVICE COORDS (RAD, Y)
       DRIVE DEVICE (14, |I-RAD|, 0)      { Hardware circle }
       return
   finish

   GET ATTRIBUTE (ATT CHORD STEP, CHORD STEP)
   STORE = STORING;   STORING = -1
   INQUIRE POSITION (PX, PY)
   RR = RAD * (2 - Cos(CHORD STEP/2/DtoR))
   PTS(1)_X = PX + INT(RR)
   PTS(1)_Y = PY
   CONT = CHORD STEP
   I = 2
   cycle
      RA =  CONT / DtoR
      PTS(I)_X = PX + INT(RR * COS(RA))
      PTS(I)_Y = PY + INT(RR * SIN(RA))
      CONT = CONT + CHORD STEP
      exit if CONT>360
      I = I + 1
   repeat
   POLYGON (I, PTS)
   MOVE ABS (PX, PY)
   STORING = STORE
end

! Utilties to draw ARCs and SECTORs

routine FIND POINTS (integer RAD, CX, CY, SA, FA, integer name PTR,
                      record (POINTFM) array name AP)
   integer A, STEP

   GET ATTRIBUTE (ATT CHORD STEP, STEP)
   A = SA
   cycle
      AP(PTR)_X = int(Rad * Cos(A/DtoR)) + CX
      AP(PTR)_Y = int(Rad * Sin(A/DtoR)) + CY
      exit if A = FA
      PTR = PTR + 1
      if SA < FA start
          A = A + STEP
          A = FA if A > FA
      else
          A = A - STEP
          A = FA if A < FA
      finish
   repeat 
end

external routine ARC alias "EDWIN_ARC" (integer OX, OY, RAD, START ANG, END ANG)
   record (POINTFM) array P (1:361)
   integer PTR, I
   PTR = 1
   FIND POINTS (RAD, OX, OY, START ANG, END ANG, PTR, P)
   MOVE ABS (P(1)_X, P(1)_Y)
   LINE ABS (P(I)_X, P(I)_Y) for I=2,1,PTR
end

external routine SECTOR alias "EDWIN_SECTOR" (integer OX, OY, RAD, START ANG, END ANG)
   record (POINTFM) array P (1:363)
   integer PTR
   P(1)_X = OX;   P(1)_Y = OY
   PTR = 2
   FIND POINTS (RAD, OX, OY, START ANG, END ANG, PTR, P)
   PTR = PTR + 1
   P (PTR)_X = OX;  P(PTR)_Y = OY
   POLYGON (PTR, P)
end

end of file