! Routines for the EDWIN transformation stack.

%record %format POINTFM (%integer X, Y)

%include "I:maths.inc"
%include "txstack.inc"                   { as consistancy check }

%own %record (TRANS FM) %array T STACK (0:TRANSFORM STACK DEPTH)
%own %record (TRANS FM) %name CUR, OLD
%own %real %array UNITY (0:8) =  1, 0, 0,   0,  1, 0,   0, 0, 1
%own %real %array MX    (0:8) = -1, 0, 0,   0,  1, 0,   0, 0, 1
%own %real %array MY    (0:8) =  1, 0, 0,   0, -1, 0,   0, 0, 1

%own %integer STACK PTR = -1

%external %routine UNITY TRANSFORM %alias "ED_UNITY_TX" (%record (TRANSFM) %name T)
   T = record(addr(UNITY(0)))
%end

%external %routine TRANSLATE TRANSFORM %alias "ED_TRANSLATE_TX" (%real X, Y, %record (TRANSFM) %name T)
   T = record(addr(UNITY(0)))
   T_A(6) = X
   T_A(7) = Y
%end

%external %routine MIRROR X TRANSFORM %alias "ED_MIRROR_X_TX" (%record (TRANSFM) %name T)
   T = record(addr(MX(0)))
%end

%external %routine MIRROR Y TRANSFORM %alias "ED_MIRROR_Y_TX" (%record (TRANSFM) %name T)
   T = record(addr(MY(0)))
%end

%external %routine ROT DV TRANSFORM %alias "ED_ROT_DV_TX" (%real A, B, %record (TRANSFM) %name T)
   %long %real SQRT AA BB
   %real DIR AC, DIR BC
   T = record(addr(UNITY(0)))
   %return  %if A = 0 %and B = 0
   SQRT AA BB = SQRT (A*A + B*B)
   DIR AC = A/SQRT AA BB
   DIR BC = B/SQRT AA BB
   T_A(0) = DIR AC
   T_A(4) = DIR AC
   T_A(1) = DIR BC
   T_A(3) = -DIR BC
%end

%external %routine ROT ANG TRANSFORM %alias "ED_ROT_ANG_TX" (%real ANG, %record (TRANSFM) %name T)
   %real SA, CA
   T = record(addr(UNITY(0)))
   ANG = ANG / DtoR
   SA = SIN (ANG)
   CA = COS (ANG)
   T_A(0) = CA
   T_A(1) = SA
   T_A(3) = -SA
   T_A(4) = CA
%end

%external %routine SCALE TRANSFORM %alias "ED_SCALE_TX" (%real XS, YS, %record (TRANSFM) %name T)
   T = record(addr(UNITY(0)))
   T_A(0) = XS
   T_A(4) = YS
%end

%external %routine COMPOSE TRANSFORM %alias "ED_COMPOSE_TX" (%record (TRANSFM) %name TA, TB, TC)
   ! Performs the matrix multiplication C = A*B on the arrays in parameters.
   %real %array %name A, B, C (0:8)
   %integer I, J, K
   %real %name R
   A == TA_A;   B == TB_A;   C == TC_A
   %for I = 0,3,6 %cycle
        %for J = 0,1,2 %cycle
             R == C(I+J);   R = 0
             %for K = 0, 1, 2 %cycle
                  R = R + A(I+K) * B(K*3+J)
             %repeat
        %repeat
   %repeat
%end

%external %routine POP TRANSFORM %alias "ED_POP_TX"
   %signal 14, 9 %if STACK PTR < 0; ! Stack not initialised.
   STACK PTR = STACK PTR - 1
   %signal 14, 11 %if STACK PTR<0
   CUR  == OLD
   OLD == TSTACK(STACK PTR-1) %if STACK PTR>0
%end

%external %routine INIT TRANSFORM %alias "ED_INIT_TX"
   ! Initialise the transformation stack.
   STACK PTR = 0
   CUR == T STACK (0)
   CUR = record(addr(UNITY(0)))
%end

%external %routine SET TRANSFORM %alias "ED_SET_TX" (%record (TRANSFM) %name T)
   INIT TRANSFORM %if STACK PTR < 0
   OLD == CUR
   STACK PTR = STACK PTR + 1
   %signal 14, 10 %if STACK PTR > TRANSFORM STACK DEPTH
   CUR == T STACK(STACK PTR)
   CUR = T
%end

%external %routine PUSH TRANSFORM %alias "ED_PUSH_TX" (%record (TRANSFM) %name T)
   INIT TRANSFORM %if STACK PTR < 0
   OLD == CUR
   STACK PTR = STACK PTR + 1
   %signal 14, 10 %if STACK PTR > TRANSFORM STACK DEPTH
   CUR  == T STACK(STACK PTR)
   COMPOSE TRANSFORM (T, OLD, CUR); ! CUR = T * OLD  gives new transformation to be done.
%end

%external %routine POINT TRANSFORM %alias "ED_POINT_TX" (%record (POINTFM) %name P, NP)
   ! Transforms a point P to NP under the current transformation.
   NP_X = INT(P_X*CUR_A(0) + P_Y*CUR_A(3) + CUR_A(6))
   NP_Y = INT(P_X*CUR_A(1) + P_Y*CUR_A(4) + CUR_A(7))
%end

%external %routine VECTOR TRANSFORM %alias "ED_VECTOR_TX" (%record (POINTFM) %name V, NV)
   ! Transforms a vector V to NV under the current transformation.
   NV_X = INT(V_X*CUR_A(0) + V_Y*CUR_A(3))
   NV_Y = INT(V_X*CUR_A(1) + V_Y*CUR_A(4))
%end

%external %routine BB TRANSFORM %alias "ED_BB_TX" (%record (POINTFM) %name OPL, OPU, NPL, NPU)
   ! Transforms a bounding box to a new paraxial bounding box
   %integer SWAP
   %record (POINTFM) UL, LR

   %integer %function MAX (%integer A, B)
      %result = A %if A > B
      %result = B
   %end

   %integer %function MIN (%integer A, B)
      %result = A %if A < B
      %result = B
   %end

   POINT TRANSFORM (OPL, NPL)
   POINT TRANSFORM (OPU, NPU)
   SWAP = OPL_Y
   OPL_Y = OPU_Y
   OPU_Y = SWAP
   POINT TRANSFORM(OPL, UL)
   POINT TRANSFORM(OPU, LR)
   SWAP = OPL_Y
   OPL_Y = OPU_Y
   OPU_Y = SWAP
   NPU_X = MAX ( MAX(NPU_X,NPL_X) , MAX(UL_X,LR_X) ) ;! Get max X from 4 corners
   NPL_X = MIN ( MIN(NPU_X,NPL_X) , MIN(UL_X,LR_X) ) ;! Get min X from 4 corners
   NPU_Y = MAX ( MAX(NPU_Y,NPL_Y) , MAX(UL_Y,LR_Y) ) ;! Get max Y from 4 corners
   NPL_Y = MIN ( MIN(NPU_Y,NPL_Y) , MIN(UL_Y,LR_Y) ) ;! Get min Y from 4 corners
%end

%external %routine GET TRANSFORM %alias "ED_GET_TX" (%record (TRANSFM) %name TX)
  TX = CUR
%end

%external %routine PRINT TRANSFORM %alias "ED_PRINT_TX" (%record (TRANSFM) %name T)
   ! PRINT TRANS is only required if debugging.
   %integer I
   %for I = 0, 1, 8 %cycle
        PRINT (T_A(I), 3, 1)
        NEWLINE %if I=2 %or I=5 %or I=8
   %repeat
%end

%end %of %file
