! EDWIN 5.1  Sept 1984

!@16_1160 %real%fn float(%integer i)
%include ":l:src:util.imp"
%include ":l:b:inc:maths.imp"
%include "edwin:consts.inc"
%include "edwin:icodes.inc"
%include "edwin:specs.inc"
!%external %integer %fn %spec MULDIV %alias "EDWIN___MUL_DIV" (%integer A, B, C)

%include "edwin:CHARSPEC"

! Global Data for the EDWIN library -

%external %record (DEVICE DATA FM) DEV DATA %alias "EDWIN___DEVICE_DATA"

%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

%include "edwin:EDCONFIG"

! control
%own %integer VIS = 0;      ! Current Char visibility
%const %integer %array RA (0:3) = 0, 5, 3, 6
%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,
                                               { Att 12       }  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 (4, FX, FY) %if OTX#FX %or OTY#FY
       DRIVE DEVICE (5, TX, TY)
   %finish %else DRIVE DEVICE (4, TX, TY)
   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 = OUT STREAM
   SELECT OUTPUT (STORING)
   -> IS (A&15)
IS(0): IS(1): IS(2): IS(3): IS(4): IS(5):       { Two Values which may be merged
   %if -128<=X<=127 %and -128<=Y<=127 %start
       WRITE (A ! 16, 1)
       WRITE ((X&255)<<8! Y&255, 1)
       -> Done
   %finish
IS(6): IS(7): IS(8): IS(13):                    { Output both other values
       WRITE (A, 1);   WRITE (X, 1);   WRITE (Y, 1)
       -> Done
IS(14): IS(15):                                 { Output X value
       WRITE (A, 1);   WRITE (X, 1)
       -> Done
IS(9): IS(10): IS(11): IS(12):                  { No other values
       WRITE (A, 1)
Done:
   NEWLINE
   SELECT OUTPUT (OOS)
%end

%external %routine SET ATTRIBUTE %alias "EDWIN___SET_ATTRIBUTE" (%integer WHAT, TO)
   %return %if ATTRIBUTES(WHAT) = TO
   ATTRIBUTES(WHAT) = TO
   TO = MUL DIV (TO, XS, XV) %if WHAT=2 { Character size }
   DRIVE DEVICE (7, WHAT, TO)
%end

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

%integer %fn CHAR OFFSET (%integer DIR)
   ! Dir is X or Y and the result is the current character offset in dir.
   %const %integer %array RX (0:3) = 1, 0, -1, 0
   %const %integer %array RY (0:3) = 0, 1, 0, -1
   %integer SIZE, ROT

   ROT = (ATTRIBUTES (3)//90)&3
   SIZE = ATTRIBUTES (2)
   %result = SIZE * RX(ROT) %if DIR = 'X'
   %result = SIZE * RY(ROT)
%end

%routine DO ASPECT
   %integer MD, MV, N, ARF

   ARF = DEV DATA_ARF
   ARF = 100 %if ARF = 0
   MD = Int ((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


%constinteger ONE=32;  !  Internal representation of unity
%own %integer MARKER SCALE = 1

%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)

%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 (6, MK(N), 0) %and %return %if DEV DATA_MVX<250 { VDU }
   SCALE = ONE
   SCALE = ONE * 10 %if DEV DATA_MVX>4095; !  For calcomps & HP plotters.
   SCALE = SCALE * MARKER SCALE
   INTERPRET (CHARPDF(2000-N*2), MUL DIV(SCALE, XV, XS), 0)
%end

%routine INTERPRET (%integer PC, SIZE, ORIENT)
   ! 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
   %switch C (0:15)

   ACTIVE = FALSE
   %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
          %finish%elsestart;           !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
              %if ORIENT&4#0 %start; !Coordinate swop
                  Z=X; X=Y; Y=Z
              %finish
              X=-X %if ORIENT&1#0;   !Y-axis reflection
              Y=-Y %if ORIENT&2#0;   !X-axis reflection
              !Change relative to absolute coords
              X = MUL DIV (X,SIZE,ONE) + CX
              Y = MUL DIV (Y,SIZE,ONE) + 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"
   DRIVE DEVICE (1, 0, 0); ! Tell the device driver to Terminate
   PDF INSERT (12, 0, 0);   ! Close the PDF if it was in use.
   VIEWING = 0
   DEV DATA_DEV NO = -1
%end

%external %routine LINE ABS %alias "EDWIN_LINE_ABS" (%integer X, Y)
   PDF INSERT(0,X,Y);    CLIP(X,Y,1)
%end

%external %routine MOVE ABS %alias "EDWIN_MOVE_ABS" (%integer X, Y)
   PDF INSERT(1,X,Y);    CLIP(X,Y,0)
%end

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

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

%external %routine MOVE REL %alias "EDWIN_MOVE_REL" (%integer DX, DY)
   PDF INSERT(4,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!5,DX,DY);    CLIP(DX+CX,DY+CY,0);    MARKER(N)
%end

%external %routine CHARACTER %alias "EDWIN_CHAR" (%integer SYM)
   %const %integer UNIT = 12
   %integer SSAVE, LSAVE, SIZE, OX, OY

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

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

Resume: { After a hardware character error }
   %if ATTRIBUTES(4)&1 = 0 %start
       DRIVE DEVICE (6, SYM, 0) %if VIS=0
       CX = CX + CHAR OFFSET ('X');   CY = CY + CHAR OFFSET ('Y')
   %else
       %if ATTRIBUTES(1)#0 %start
           LSAVE = ATTRIBUTES(1)
           DRIVE DEVICE(7, 1, 0)
       %finish %else LSAVE = -1
       %if ATTRIBUTES(5)=0 %start; ! Normal EDWIN ones
           SIZE = MUL DIV (ATTRIBUTES(2), ONE, UNIT)
           %if SIZE > 8 %and 32<=SYM<=127 %start
               OX = CX;   OY = CY
               INTERPRET(CHARPDF(2000-(SYM-21)<<1), SIZE, RA(ATTRIBUTES(3)//90&3))
               CLIP (OX + CHAR OFFSET ('X'), OY + CHAR OFFSET ('Y'), 0)
           %finish
       %else; ! GIMMS characters
           SSAVE = STORING;   STORING = -1
           DRAW CHAR (SYM, ATTRIBUTES(5), ATTRIBUTES(2), ATTRIBUTES(3))
           STORING = SSAVE
       %finish
       DRIVE DEVICE (7, 1, LSAVE) %if LSAVE>=0
   %finish
%end

%external %routine NEW FRAME %alias "EDWIN_NEW_FRAME"
   DRIVE DEVICE (3, 0, 0)
   PDF INSERT (11, 0, 0)
   CX = 0;   CY = 0
%end

%external %routine UPDATE %alias "EDWIN_UPDATE"
   DRIVE DEVICE (2, 0, 0)   { All the work is done by the device driver }
%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(8, A, B);   PDF INSERT(8, C, D)
   DO ASPECT %if ATTRIBUTES(15)#0
   XV = XR-XL;   YV = YT-YB
   VIS = 0
   A = ATTRIBUTES(2)
   %return %if A <= 0 { No size set up yet, Window having been called from Init }
   ATTRIBUTES (2) = 0
   SET ATTRIBUTE (2, A) { Fix device character size for new window }
%end

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

   %signal 14, 13 %if XL>=XR %or YB>=YT
   ! Choose the largest value if the upper bound is too large
   %return %if DEV DATA_DEV NO<=0
   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
   DRIVE DEVICE (8, XL, YB); ! Set lower window bounds
   DRIVE DEVICE (9, XR, YT); ! Set upper window bounds (If  driver req.)
   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 ! 10, 0, 0)
   SET ATTRIBUTE (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 (0, DEVICE TYPE, 0);    ! Initialise device driver
   I = DEV DATA_DEV NO
   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 "edwin:EDATTRIB"
%include "edwin:EDTEXT"
%include "edwin:EDUTILS"
%include "edwin:EDERRORS"
%record %format POINT FM (%integer X, Y)
%include "edwin:SHAPES.INC"
%include "edwin:EDSHAPES"
%include "edwin:EDREVIEW"
%include "edwin:EDCIFSUP"
%include "edwin:EDDEFDEV"

%end %of %file
