! EDWIN 5.2  Sept 1985

!############################################################################
!#                                                                          #
!#  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.                                                            #
!#                                                                          #
!############################################################################

from Imp include Connect
from Imp include Lognames
from Imp include Maths
from Edwin include consts
from Edwin include icodes
from Edwin include specs
external integer fn spec MULDIV alias "EDWIN___MUL_DIV" (integer A, B, C)

from Edwin include charspec

! Global Data for the EDWIN library -

external record (DEVICE DATA FM) DEV DATA alias "EDWIN___DEVICE_DATA" = 0

external record (DEVICE DATA FM) map D DATA alias "EDWIN_DEVICE_DATA"
   result == record(addr(DEV DATA))
end

external integer VIEWING alias "EDWIN___VIEWING" = 0
external integer STORING alias "EDWIN___STORING" = -1
external integer CLIPPING alias "EDWIN___CLIPPING" = 0
! These three variables are set up by the device drivers for Draft (curse)
external integer Font Width alias "CharX"
external integer Font Height alias "CharY"
external integer Font Descender alias "OffY"

!%include "EDCONFIG"

! control
const integer Max Int = 16_7FFFFFF { NB: 4 bits saved for multiplications }
own integer VIS = 0;        ! Current Char visibility
own integer DEVICE CHAR SIZE = 12
own integer array ATTRIBUTES (0:15) = -1 (*)
const integer array DEF ATTRIBUTES (0:15) = { Colour       }  1,
                                               { Line Style   }  0,
                                               { Char size    } 12,
                                               { Char rot     }  0,
                                               { Char quality }  0,
                                               { Char font    }  0,
                                               { Char slant   }  0,
                                               { Maker size   }  7,
                                               { Speed (max)  }  0,
                                               { Colour mode  }  0,
                                               { Shade mode   }  0,
                                               { Chord step   } 15,
                                               { Char Mirror  }  0,
                                               { Att 13       }  0,
                                               { Att 14       }  0,
                                               { Aspect ratio }  1

! Screen information
own integer XO = 0;        ! Origin (bottom left) of device window
own integer YO = 0
own integer XS = 1023;     ! Size of device window
own integer YS = 1023
own integer CX = 0;        ! Current virtual position
own integer CY = 0
own integer XV = 1023;     ! Size of virtual window
own integer YV = 1023
own integer XL = 0;        ! Origin of virtual window (Left edge)
own integer XR = 1023;     ! (Right edge)
own integer YB = 0;        ! (Bottom edge)
own integer YT = 1023;     ! (Top edge)
own integer OWXL=0, OWXR=1023, OWYB=0, OWYT=1023
! Old Window bounds for aspect ratioing retrospectivly.

routine SWOP (integer name A,B)
   integer C
   C=A;  A=B;  B=C
end

external routine MAP TO DEVICE COORDS  alias "EDWIN_MAP_TO_DCS" (integer name X, Y)
   X = MUL DIV (X-XL, XS, XV) + XO
   Y = MUL DIV (Y-YB, YS, YV) + YO
end

external routine MAP TO VIRTUAL COORDS alias "EDWIN_MAP_TO_VCS" (integer name X, Y)
   X = MUL DIV (X-XO, XV, XS) + XL
   Y = MUL DIV (Y-YO, YV, YS) + YB
end

routine VECTOR (integer FX, FY, TX, TY, V)
   ! Draw visible line from virtual coordinates (FX,FY) to (TX,TY).
   ! But if V = 0 just move to (TX,TY).
   own integer OTX = 0, OTY = 0

   MAP TO DEVICE COORDS (FX,FY)
   MAP TO DEVICE COORDS (TX,TY)

   if V#0 start
       DRIVE DEVICE (Dev move, FX, FY) if OTX#FX or OTY#FY
       DRIVE DEVICE (Dev Line, TX, TY)
   else
       DRIVE DEVICE (Dev Move, TX, TY)
   finish
   OTX = TX
   OTY = TY
end

external routine CLIP alias "EDWIN___CLIP" (integer TX, TY, V)
   ! Draw a vector (visible if V#0) to virtual position (TX,TY)
   ! but only that part of it (if any) which lies within the virtual window
   integer F, T, FX, FY
   constinteger LEFT = 1, RIGHT = 2, ABOVE = 4, BELOW = 8

   integer fn CODE (integer X, Y)
      ! Set one bit for each of the conditions that (X,Y) lies
      ! above, below, to the left, or to the right of window
      integer C
      C = 0
      C = LEFT if X<XL
      C = RIGHT if X>XR
      C = C + ABOVE if Y>YT
      C = C + BELOW if Y<YB
      result = c
   end

   FX=CX; FY=CY;          ! Let FROM be current position
   CX=TX; CY=TY;          ! Update current position to TO
   return if VIEWING<0
   VECTOR (FX,FY,TX,TY,V) and return if CLIPPING<0

   T=CODE (TX,TY)
   VIS = T; ! Remember whether inside window for CHARS
   cycle
      F = CODE (FX,FY)
      return if F&T#0;    ! Both endpoints outside window (same side)
      if F+T=0 start;     ! Both endpoints inside window
          VECTOR (FX,FY,TX,TY,V); ! So draw the line and it's over with
          return
      finish
      if F=0 start;       ! FROM is inside window: swop FROM and TO
          SWOP(F,T); SWOP(FX,TX); SWOP(FY,TY)
      finish
      ! Now FROM is outside and TO is either inside or outside
      ! So shift FROM along the line FROM/TO until it comes
      ! to lie on the window's edge.
      if F&LEFT#0 start
          FY=MUL DIV(TY-FY,XL-FX,TX-FX)+FY; FX=XL
      else
          if F&RIGHT#0 start
              FY=MUL DIV(TY-FY,XR-FX,TX-FX)+FY; FX=XR
          else
              if F&ABOVE#0 start
                  FX=MUL DIV(TX-FX,YT-FY,TY-FY)+FX; FY=YT
              else 
                  if F&BELOW#0 start
                      FX=MUL DIV(TX-FX,YB-FY,TY-FY)+FX; FY=YB
                  finish
              finish
          finish
      finish
   repeat
end

external routine PDF INSERT alias "EDWIN___PDF_INSERT" (integer A, X, Y)
   ! Insert instruction into display file
   switch IS (0:15)
   integer OOS

   return if STORING < 0
   OOS = OUTPUT STREAM
   SELECT OUTPUT (STORING)
   Write (A, 1)
   -> IS (A&15)
IS(0): IS(1): IS(2): IS(3): IS(4): IS(5):       { Two Values to be output
IS(6): IS(7): IS(8): IS(13):
       WRITE (X, 1);   WRITE (Y, 1)
       -> Done
IS(14): IS(15):                                 { Output X value
       WRITE (X, 1)
Done:
IS(9): IS(10): IS(11): IS(12):                  { No other values
   NEWLINE
   SELECT OUTPUT (OOS)
end

external routine SET ATTRIBUTE alias "EDWIN___SET_ATTRIBUTE" (integer WHAT, TO)
   return if ATTRIBUTES(WHAT) = TO
   ATTRIBUTES(WHAT) = TO
   if WHAT=Att Char Size start
      DEVICE CHAR SIZE = MUL DIV (TO, XS, XV)
      TO = DEVICE CHAR SIZE
   finish
   DRIVE DEVICE (Dev Attribute, WHAT, TO)
end

external routine GET ATTRIBUTE alias "EDWIN___GET_ATTRIBUTE" (integer CODE, integer name VAL)
   VAL = ATTRIBUTES (CODE)
end

routine CHAR OFFSET (integer name XO, YO)
   ! Update XO and YO with the relative movements caused by the character
   long real R
   integer SIZE, ROT

   ROT = ATTRIBUTES (Att Char Rot)
   SIZE = ATTRIBUTES (Att Char size)
   SIZE = -SIZE if Attributes (Att Char Mirror) & Mirror in Y Axis # 0
   if Rot = 0 start
       XO = SIZE
       YO = 0
   else
       R = Rot/DtoR
       XO = round (size * Cos(r))
       YO = round (size * Sin(r))
   finish
end

routine DO ASPECT
   integer MD, MV, N, ARF

   ARF = DEV DATA_ARF
   ARF = 100 if ARF = 0
   MD = round ((Float(100000)*Float(YS)) / (Float(ARF)*Float(XS)))
   MV = MUL DIV (1000, |YT-YB|, |XR-XL|)
   if MD#MV start
       if MD>MV start
           N = (MUL DIV(MD,|XR-XL|,1000)+YB - YT)//2
           YB = YB - N
           YT = YT + N
       else
           N = (MUL DIV(1000,|YT-YB|,MD)+XL - XR)//2
           XL = XL - N
           XR = XR + N
       finish
   finish
end


own integer MARKER SCALE = 1
const integer Anti Rounding = 1000

external routine SET MARKER SIZE alias "EDWIN_SET_MARK_SIZE" (integer S)
   S = 1 unless 0 < S < 256
   PDF INSERT ((Att Marker Size << 8 ! S) << 4 ! PDF Attribute, 0, 0)
   MARKER SCALE = S
end

routine spec INTERPRET (integer PC, SIZE, Rot, Mirror)

external routine MARKER alias "EDWIN___MARKER" (integer N)
   const integer array MK(0:10) = '.', 'O', '#', 'A', 'X', '*', '+', '>', '<', '^', 'V'
   integer scale
   !  This draws a marker at the current position.
   return unless 0<=N<=10

   DRIVE DEVICE (Dev Char, MK(N), 0) and return if DEV DATA_Dev no = VT100
   if Dev Data_Units per cm > 0 start
      SCALE = Trunc(Marker Scale * Anti Rounding * -
                   (DEV DATA_X Units per cm/40 + 0.5))
   else
      SCALE = Marker Scale * Anti Rounding
   finish
   INTERPRET (CHARPDF(2000-N*2), MUL DIV(SCALE, XV, XS), 0, 0)
end

routine INTERPRET (integer PC, SIZE, Rot, Mirror)
   ! Interpret instructions in display file starting
   ! at (relative) PC until an END instruction is found
   ! Codes are   0 LINEA   1 MOVEA   2 MARKERA
   !             3 LINER   4 MOVER   5 MARKERR
   !             6 SUBPIC  7 END     8 WINDOW
   !             9 CHAR   10 ATTRIBUTES   11 END
   integer WORD, CODE, X, Y, Z, P, SSAVE, LSAVE, CSIZE, ACTIVE, OX, OY
   long real SinR, CosR
   switch C (0:15)

   ACTIVE = FALSE
   if Rot # 0 start
      Sin R = Sin (Rot/DtoR)
      Cos R = Cos (Rot/DtoR)
   finish
   cycle
      WORD = CHARPDF(PC);   PC=PC+1
      CODE=WORD&15
      if CODE<=5 start;    !Draw, Move, Marker
          ACTIVE = TRUE
          X = CHARPDF (PC);   PC = PC + 1
          if WORD&16=0 start;   !Long form
              Y = CHARPDF (PC);   PC = PC + 1
          finishelsestart;             !Short form
              Y=X&255; X=X>>8&255
              X=X!!(¬255) if X&128#0
              Y=Y!!(¬255) if Y&128#0
          finish
          if CODE>=3 start;   !Relative
              !Change the scale
              X = X*SIZE
              Y = Y*SIZE
              X = -X if Mirror & Mirror in Y Axis # 0
              Y = -Y if Mirror & Mirror in X Axis # 0
              if Rot # 0 start
                 Z = Round (X*CosR - Y*SinR)
                 Y = Round (X*SinR + Y*CosR)
                 X = Z
              finish
              X = Round(X/Anti Rounding) + CX
              Y = Round(Y/Anti Rounding) + CY
              CODE=CODE-3;           !Map to absolute codes
          finish
    finish
    ->C(CODE)
C(0):    CLIP (X, Y, 1);   continue           { Line }
C(1):    CLIP (X, Y, 0);   continue           { Move }
C(2):    CLIP (X, Y, 0);   MARKER(WORD>>12&15)
  repeat
C(*): signal 14, 5
C(12):
end

!*******************************************************************
!*                                                                 *
!*               U S E R   R O U T I N E S                         *
!*                                                                 *
!*******************************************************************

external routine TERMINATE EDWIN alias "EDWIN_TERM"
   PDF INSERT   (PDF Terminate, 0, 0)
   DRIVE DEVICE (Dev Terminate, 0, 0)
   DEV DATA_DEV NO = -1
end

external routine LINE ABS alias "EDWIN_LINE_ABS" (integer X, Y)
   PDF INSERT (PDF Line Abs, X, Y)
   CLIP (X, Y, 1)
end

external routine MOVE ABS alias "EDWIN_MOVE_ABS" (integer X, Y)
   PDF INSERT (PDF Move Abs, X, Y)
   CLIP (X, Y, 0)
end

external routine MARKER ABS alias "EDWIN_MARK_ABS" (integer N, X, Y)
   PDF INSERT (N<<12!PDF Mark Abs, X, Y)
   CLIP (X, Y, 0)
   MARKER (N)
end

external routine LINE REL alias "EDWIN_LINE_REL" (integer DX, DY)
   PDF INSERT (PDF Line Rel, DX, DY)
   CLIP (DX+CX, DY+CY, 1)
end

external routine MOVE REL alias "EDWIN_MOVE_REL" (integer DX, DY)
   PDF INSERT (PDF Move Rel, DX, DY)
   CLIP (DX+CX, DY+CY, 0)
end

external routine MARKER REL alias "EDWIN_MARK_REL" (integer N, DX, DY)
   PDF INSERT (N<<12!PDF Mark Rel, DX, DY)
   CLIP (DX+CX, DY+CY, 0)
   MARKER (N)
end

external routine CHARACTER alias "EDWIN_CHAR" (integer SYM)
   const integer UNIT = 12
   integer FSAVE, SSAVE, LSAVE, SIZE, OX, OY, NX, NY
   long real R Size

   on 14 start
       signal 14, event_sub if event_sub # 14
       ATTRIBUTES (Att Char Quality) = 1 { software chars }
       -> resume
   finish

   PDF INSERT (SYM<<4!PDF character, 0, 0)

Resume: { After a hardware character error }
   if ATTRIBUTES(Att Char Quality) = 0 start
       CHAR OFFSET (NX, NY)
       CX = CX + NX
       CY = CY + NY
       DRIVE DEVICE (Dev Char, SYM, 0) if VIS=0
   else
       if ATTRIBUTES(Att Line Style)#0 start
           LSAVE = ATTRIBUTES(Att Line Style)
           DRIVE DEVICE(Dev Attribute, Att Line Style, 0)
       finish else LSAVE = -1
       if ATTRIBUTES(Att Shade Mode)#0 start
           FSAVE = ATTRIBUTES(Att Shade Mode)
           DRIVE DEVICE(Dev Attribute, Att Shade Mode, 0)
       finish else FSAVE = -1
       if ATTRIBUTES(Att Char Font)=0 start;   ! Normal EDWIN ones
           RSize = Float(Attributes(Att Char Size))*Float(Anti Rounding)/12
           if R Size > Max Int start
              Size = Max Int
           else
              Size = Round (R Size)
           finish
           if SIZE > 0 and 32<=SYM<=127 start
               OX = CX;   OY = CY
               if Device Char Size > 2 start
                  INTERPRET (CHARPDF(2000-(SYM-21)<<1), Size,
                         Attributes(Att Char Rot), Attributes(Att Char Mirror))
               finish
               CHAR OFFSET (NX, NY)
               CLIP (OX + NX, OY + NY, 0)
           finish
       else;  ! GIMMS characters
           SSAVE = STORING;   STORING = -1
           DRAW CHAR (SYM, Attributes(Att Char Font), Attributes(Att Char Size),
                      Attributes(Att Char Rot), Attributes(Att Char Mirror))
           STORING = SSAVE
       finish
       DRIVE DEVICE (Dev Attribute, Att Line Style, LSAVE) if LSAVE>=0
       DRIVE DEVICE (Dev Attribute, Att Shade Mode, FSAVE) if FSAVE>=0
   finish
end

external routine NEW FRAME alias "EDWIN_NEW_FRAME"
   PDF INSERT   (PDF Newframe, 0, 0)
   DRIVE DEVICE (Dev Newframe, 0, 0)
   CX = 0
   CY = 0
end

external routine UPDATE alias "EDWIN_UPDATE"
   DRIVE DEVICE (Dev update, 0, 0)
end

external routine CLIP ON alias "EDWIN_CLIP_ON"
   CLIPPING = 0
end

external routine CLIP OFF alias "EDWIN_CLIP_OFF"
   CLIPPING = DISABLED
end

external routine STORE ON alias "EDWIN_STORE_ON" (integer STREAM)
   STORING = STREAM
end

external routine STORE OFF alias "EDWIN_STORE_OFF"
   STORING = DISABLED
end

external routine VIEW ON alias "EDWIN_VIEW_ON" (integer STREAM)
   VIEWING = STREAM
end

external routine VIEW OFF alias "EDWIN_VIEW_OFF"
   VIEWING = DISABLED
end

external routine WINDOW alias "EDWIN_WINDOW" (integer A, B, C, D)
   signal 14, 12 if A>=B or C>=D
   XL = A;   OWXL = A;   XR = B;   OWXR = B
   YB = C;   OWYB = C;   YT = D;   OWYT = D
   PDF INSERT(PDF Window, A, B);   PDF INSERT(PDF Window, C, D)
   DO ASPECT if ATTRIBUTES(15)#0
   XV = XR-XL;   YV = YT-YB
   VIS = 0
   A = ATTRIBUTES(Att Char Size)
   return if A <= 0 { No size set up yet, Window having been called from Init }
   ATTRIBUTES (Att Char Size) = 0
   SET ATTRIBUTE (Att Char Size, A) { Fix device character size for new window }
end

external routine VIEWPORT alias "EDWIN_VIEWPORT" (integer XL, XR, YB, YT)
   integer S

   return if DEV DATA_DEV NO<=0

   ! Check that the bounds are valid, trying to make a sensible size if req.
   XL = 0 if XL<0
   YB = 0 if YB<0
   XR = DEV DATA_MVX if XR>DEV DATA_MVX
   YT = DEV DATA_MVY if YT>DEV DATA_MVY
   signal 14, 13 if XL>=XR or YB>=YT

   DRIVE DEVICE (Dev low wb,  XL, YB)  { Set lower viewport bounds
   DRIVE DEVICE (Dev high wb, XR, YT)  { Set upper viewport bounds
   XO=XL;  XS=XR-XL;  YO=YB;  YS=YT-YB

   S = STORING
   STORING = DISABLED
   WINDOW (OWXL, OWXR, OWYB, OWYT)
   STORING = S
end

external routine ASPECT RATIOING alias "EDWIN_ASPECT_RATIO" (integer MODE)
   const integer THIS = 15
   integer S
   MODE = 1 unless MODE = 0
   PDF INSERT ((THIS<<8 ! MODE) <<4 ! PDF Attribute, 0, 0)
   ATTRIBUTES (THIS) = MODE
   S = STORING
   STORING = DISABLED
   WINDOW (OWXL, OWXR, OWYB, OWYT)
   STORING = S
end

external routine INITIALISE FOR alias "EDWIN_INIT" (integer DEVICE TYPE)
   integer I
   DRIVE DEVICE (Dev initialise, DEVICE TYPE, 0)
   VIEW PORT (0, DEV DATA_DVX, 0, DEV DATA_DVY) if DEV DATA_DVX#0
   WINDOW (0, 1023, 0, 1023)
   ATTRIBUTES (I) = -1 for I = 0, 1, ATT MAXIMUM
   SET ATTRIBUTE (I, DEF ATTRIBUTES(I)) for I = 0, 1, ATT MAXIMUM
end

external routine INQUIRE POSITION alias "EDWIN_INQ_POSITION" (integer name X, Y)
   X = CX;   Y = CY
end

external routine INQUIRE WINDOW alias "EDWIN_INQ_WINDOW" (integer name A, B, C, D)
   A = XL;   B = XR;   C = YB;   D = YT
end

external routine INQUIRE VIEWPORT alias "EDWIN_INQ_VIEWPORT" (integer name A, B, C, D)
   A = XO;   B = XS+XO;   C = YO;   D = YS+YO
end

external routine REQUEST INPUT alias "EDWIN_REQUEST" (integer name STATE, X, Y)
   REQUEST DEVICE (STATE, X, Y)
   MAP TO VIRTUAL COORDS (X, Y)
end

external routine SAMPLE INPUT alias "EDWIN_SAMPLE" (integer name STATE, X, Y)
   SAMPLE DEVICE (STATE, X, Y)
   MAP TO VIRTUAL COORDS (X, Y)
end

external routine AREA INPUT alias "EDWIN_AREA" (integer name XL, YB, XR, YT)
   AREA DEVICE (XL, YB, XR, YT)
   MAP TO VIRTUAL COORDS (XL, YB)
   MAP TO VIRTUAL COORDS (XR, YT)
end

end of file { Unless you want it all together }

include "EDATTRIB"
include "EDTEXT"
include "EDUTILS"
include "EDERRORS"
record format POINT FM (integer X, Y)
include "EDWIN:SHAPES.INC"
include "EDSHAPES"
include "EDREVIEW"
include "EDCIFSUP"
include "EDDEFDEV"

end of file