


(*************************************************************
**                                                          **
**                 ICS 4390 Project                         **
**                      part 2                              **
**                   written by:                            **
**                 Fereydon Shenassa                        **
**                 Fall Quarter 1984                        **
**                                                          **
**  this program is a monitor which allows the use and test **
**  of a 2d graphics package on a predefine object.         **
**  the object is defined as a triangle with vertices at    **
**  (-1,-1),(1,-1),(0,5) in world coordinates with a line   **
**  in the middle (0,-1)-(0,5).                             **
**                                                          **
**  the operations defined are:                             **
**    1)rotation,translation,scaling                        **
**    2)window and viewport operations                      **
**                                                          **
**  the program makes use of 3 procedures in the lida       **
**  package:                                                **
**    1) openwk : initialize the workstation                **
**    2) clearscreen                                        **
**    3) line(x1,y1,x2,y2)                                  **
**                                                          **
**  the program is device independent.                      **
*************************************************************)










program monitor(input,output);
(*************************************************************
**  constants:                                              **
**      userdimension- the number of dimensions the user    **
**                     works with. 2 for this program.      **
**      dimension    - userdimension + 1.                   **
**      numhplanes   - number of hyperplanes in the viewport**
**                     used in clipping                     **
**                                                          **
*************************************************************)

const
   userdimension = 2;
   dimension     = 3;
   num_hplanes   = 4;


(*************************************************************
**  types:                                                  **
**   elementtype : type of each entry in vectors and matrix **
**   columntype  : one column of elements                   **
**   matrixtype  : a matrix with 1 dimension higher than    **
**                 the user dimension                       **
**   pointype,vectortype : same as columntype, renamed for  **
**                         clarity                          **
**   viewareatype: array of halfspaces for clipping         **
**   polygontyp  : polygon represented as circular          **
**                 linked list                              **
**   command     : a linked list representation of commands **
**                 which could be lines or polygons         **
**                                                          **
*************************************************************)


type
  (*  types needed by the user to access package utilities *)

  devicechoice = (hp9845,iklores,ikhires, ps300,tek4115,tek4107);
  inpnames     = (lightpen, digitizer, tablet);
  inpclass     = (locator, pick, choice, valuator, strings, stroke);
  outnames     = (plotter);
  polygontype  = (hollow, solid);
  pointcoords = record
                x, y : integer
                end;
  symbolid = array[1..6] of char;
  textstring  = array[1..80] of char;
  ptpairarray = array[1..50] of pointcoords;
  polytype    = array[0..49, 1..3] of integer;  (* for solid polygons *)
  orientationrange = -180..180;   (* angle of the text *)
  anglerange  = -89..89;          (* angle of each character in the text *)


  colorrec   =    record
                hpred, hpgreen, hpblue : real
                end;

  (* types needed for the clipping operation *)

  realptcoords = record
                  x, y : real;
                  end;
  
  bezierarray = array[1..4] of realptcoords;

  lines = record
          a, b, c : integer;  (* equation ax + by = c is used *)
          orgdir : boolean;   (* true implies that the origin is outside *)
          end;

  (* types needed by the package to process symbols  *)
  commandptr   = ^commandentry;
  commandentry = record
                next : commandptr;
                case tag: char of
                 'a' : (polnum : integer;
                        polptarr : ptpairarray);

                 'b' : (linnum : integer;
                        linptarr : ptpairarray);

                 'c' : ();

                 'd' : (xs, ys, xd, yd : integer);     (* line *)

                 'e' : (setindex : integer; sred, sgreen, sblue : real);

                 'g' : (xt, yt, lgth : integer;
                        strings : textstring);

                 'h' : (bezierpts : bezierarray);

                 'i' : (isymname : symbolid;
                        i11, i12, i21, i22, i31, i32 : real);

                 'j' : (orientation : orientationrange);

                 'k' : (csize: integer; htwdthratio: real; tilt: anglerange);

                 'l' : (lstyle, lindex : integer );

                 'p' : (pfill : polygontype; pindex : integer);

                end;

  symbolptr = ^symbolrec;
  symbolrec = record
                name : array[1..6] of char;
                start : commandptr;
                next : symbolptr
               end;

  segtransform = array [1..3,1..2] of real;

  visibility = (visible,invisible);

  highlighting = (normal,highlighted);

  detectability = (detectable,undetectable);

   elementtype   = real;
   columntype    = array[1..dimension] of elementtype;
   matrixtype    = array[1..dimension] of columntype;
   pointtype     = columntype;
   vectortype    = columntype;
   segmenttype   = array[1..2] of pointtype;

   viewareatype  = array[1..num_hplanes] of lines;
   polygontyp    = ^polygonelement;
   polygonelement= record
                   point: pointtype;
                   next : polygontyp;
                   end; { polygon element }


   kindtype     = ( lineseg,poly);
   command      = ^commandnode;
   commandnode  = record
                  next : command;
                  case kind  : kindtype of
                     lineseg : ( segment : segmenttype);
                     poly    : ( polygon : polygontyp );
                  end; { record }

(*************************************************************
**  variables:                                              **
**    mysymbol - predefined symbol used for testing the     **
**               routines. a triangle with a line in middle **
**    myviewarea-array of halfspaces defining the viewport  **
**    transmatrix-global transformation matrix              **
**    vindowmatrix-global viewing transformtion matrix      **
**    x,ywindpos-location of left hand corner of window     **
**    printmode - toggle for print routines on /off         **
**                                                          **
**    x,yscreensize-resolution of device in x and y direct  **
**    viewminx,maxx-location of viewport in physical coord  **
**    x,yscreensize-size of window in x and y directions    **
**                                                          **
*************************************************************)


var
  (* global variables needed by the package  *)
  rs : char;    (* control character to indicate graphics command for HP Emul *)
  station : devicechoice;
  fill : boolean;
  hpout : file of char;
  psratio : real;
  hpratio : real;      (* actually a constant of 4.55 *)
  setfill : boolean;
  esc,us : char; (*special chars for command initiation and termination
                   on tek4115 and tek4107 *)
  polyfillcolor : integer;   (* index location of fill color *)
  lowres : boolean;
  warnswitch : boolean;

  hptable : array[0..7] of colorrec;
  hplinestyletab : array[0..9] of integer;
(* variables needed to handle the window to viewport mapping *)
  mapmode : boolean;
  maphold : boolean;
  maxscreensize : integer;
  xscreensize, yscreensize : integer;
  xwindsize, ywindsize : integer;
  viewminx, viewmaxx : integer;
  viewminy, viewmaxy : integer;
  m11, m12, m21, m22, m31, m32 : real;   (* for the mapping transform *)
  charsize : integer;
  aspect : real;

  viewarea : array[1..4] of lines;       (* for the clipping operation *)
  intersectcoords : pointcoords;

  z11, z12, z21, z22, z31, z32 : real;
  recursecount : integer;

(* variables needed to handle the symbol mechanism *)
  namecount : integer;   (* global count of number of names used for PS 300 *)
  psname    : symbolid;
  defmode : boolean;  (* boolean for definition mode command *)
  symstart : symbolptr;
  thiscommand : commandptr;
  nextcommand : commandptr;
  lastcommand : commandptr;

   mysymbol      : command;
   myviewarea    : viewareatype;
   transmatrix ,
   windowmatrix  : matrixtype;
   xwindpos,
   ywindpos      : integer;
   printmode     : boolean;




(************************************************************
**             initialization procedures                   **
*************************************************************)

procedure initialize ;
(*************************************************************
**   initialize :                                           **
**      open the work station as a tektronix 4107           **
**      and clear the screen.                               **
**      it sets up the xscreensize and yscreensize          **
**                                                          **
*************************************************************)

   begin { initialize}
{   open_wk(tek4107);
   clear_screen;}
   end; {initialize}

procedure line(x1,y1,x2,y2 : integer );
   begin
   draw(x1,yscreensize-y1,x2,yscreensize-y2,white);
   end; (* line *)


procedure setidentity(  var matrix : matrixtype);
(*************************************************************
**  setidentity:                                            **
**    reset the given matrix to the identity matrix         **
**    with 1's in the diagonal and 0's elsewhere            **
**                                                          **
**  local variables:                                        **
**    i,j : counters                                        **
**                                                          **
*************************************************************)
   var
     i,j : integer;

   begin { setidentity }
   for i:= 1 to dimension do
      begin
      for j := 1 to dimension do
         matrix[i,j] := 0;
      matrix[i,i] := 1;
      end;
   end; { setidentity }



procedure define_model(var mysymbol     : command );
(*************************************************************
**  define_model:                                           **
**    define the triangle used in the drawing routines      **
**    using a polygon and a line.                           **
**                                                          **
**  local variables:                                        **
**    element,element2 : pointers to polygon nodes          **
**    command2         : pointer to the line node           **
**                                                          **
*************************************************************)

   var
     element : polygontyp;
     element2: polygontyp;
     command2: command;
   begin
   new(mysymbol);
   mysymbol^.next := nil;
   mysymbol^.kind := poly;
   with mysymbol^  do
      begin
      new(polygon);
      new(element);
      with polygon^ do
         begin { with polygon }
         point[1] := -3;
         point[2] := -3;
         point[3] := 1;
         next := element;
         end; { with polygon }
      element^.point[1]:= 3;
      element^.point[2]:= -3 ;
      element^.point[3] := 1;
      new(element2);
      element^.next := element2;
      element2^.next := polygon;
      element2^.point[1] := 0;
      element2^.point[2] := 3;
      element2^.point[3] := 1;
      end; { with }
   new(command2);
   command2^.next := nil;
   command2^.kind := lineseg;
   command2^.segment[1,1] := 0;
   command2^.segment[1,2] := -3;
   command2^.segment[2,1] := 0;
   command2^.segment[2,2] := 3;
   mysymbol^.next := command2;
   end;   {define_symbol }

(************************************************************
**                 read and print routines                 **
*************************************************************)

procedure print(matrix : matrixtype);
(*************************************************************
**   print:                                                 **
**     utility to print a square matrix of size dimension   **
**     checks the printmode toggle first. if its false      **
**     it doesn't print anything                            **
**                                                          **
**   local variables:                                       **
**     i,j : counters                                       **
**                                                          **
*************************************************************)

   var
      i , j : integer;
   begin
   if printmode then
   begin
   writeln;
   write(' ':2);
   for i:= 1 to dimension do
      write('*******');
   writeln('*');
   for i := 1 to dimension do
      begin
      write('*':3);
      for j := 1 to dimension do
         write(matrix[i,j]:6:2);
      writeln('*':3);
      end; { i }
   write(' ':2);
   for i:= 1 to dimension do
      write('*******');
   writeln('*');
   writeln;
   end;
   end; { print }

procedure  readvector(var  vector : vectortype );
(*************************************************************
**  readvector:                                             **
**    read from the input elements of a vector of size      **
**    userdimension.                                        **
**                                                          **
*************************************************************)

   var
     i : integer;

   begin  { readvector }
   for i := 1 to userdimension do
      begin
      write(i:1,'''th  element ? ');
      readln(vector[i]);
      vector[dimension] := 1;
      end;
   end; { readvector }


(************************************************************
**                 clipping algorithm                      **
*************************************************************)


procedure clip_line(    line     : segmenttype ;
                    var result   : segmenttype;
                        viewarea : viewareatype;
                    var outside  : boolean );
(*************************************************************
**  clip_line :                                             **
**     clip the given line segment to the viewarea given.   **
**     and return the result. set outside to true if the    **
**     line is completely outside the viewarea.             **
**                                                          **
**  local variables:                                        **
**     i    : counter                                       **
**     done : flag to tell end of clipping                  **
**     outcode: array of boolean used to keep the           **
**              location of each point with respect to      **
**              the viewarea array.                         **
**                                                          **
**   local procedures:                                      **
**     computelocation:return true if point is outside      **
**                      the given halfspace                 **
**     computeintersection: compute the intersection of a   **
**                     point and a halfspace                **
**                                                          **
*************************************************************)

   var
      i        : integer;
      done     : boolean;
      outcode  : array[1..num_hplanes,1..3] of boolean;

   function compute_location(point : pointtype ; line : lines): boolean;
   (************************************************************
   ** compute_location:                                       **
   **    compute the location of the given point with         **
   **    respect to the given line. return true if the        **
   **    point is outside. false otherwise.                   **
   **                                                         **
   ** local variables:                                        **
   **    result : temporary storage of the result of puting   **
   **             the given point in the equation of the line **
   **                                                         **
   *************************************************************)

      var
        result : real;

      begin  { compute_location }
      with line do
        begin
        result := a * point[1] + b * point[2] ;
         compute_location := not(  ( ( result < c ) and (not orgdir ) )
                                 or( ( result > c ) and (    orgdir ) )
                                 or(   result = c )
                                   );
        end;
      end; { compute_location }

   procedure compute_intersection(var segment      : segmenttype ;
                                      line         : lines ;
                                      outsidepoint : integer );
   (************************************************************
   **  compute_intersection:                                  **
   **     compute the intersection of the segment with the    **
   **     given line. replace the result in the outside       **
   **     endpoint. use the equation                          **
   **        y = y1 + slope *(x-x1)                           **
   **        x = x1 + 1/slope * (y-y1)                        **
   **                                                         **
   **  local variables:                                       **
   **    tempx,tempy : temporary intersection points          **
   **                                                         **
   *************************************************************)

      var
         tempx , tempy   : real;

      begin  { compute_intersection }
      with line do
      if (line.a = 0 ) then
         begin
         if (segment[2,2] - segment[1,2]) <> 0 then
         begin
         tempx := segment[1,1] + (segment[2,1] - segment[1,1])
                               * (line.c - segment[1,2]      )
                              / ( segment[2,2] - segment[1,2]);
         tempy := line.c;
         end
         else
           begin
         tempx := segment[1,1];
         tempy := line.c;
           end;
         end
      else
         begin
         if (segment[2,1] - segment[1,1]) <> 0 then
         begin
         tempy := segment[1,2] + (segment[2,2] - segment[1,2])
                               * (line.c - segment[1,1]      )
                             / ( segment[2,1] - segment[1,1]);
         tempx := line.c;
         end
         else
            begin
         tempy := segment[1,2] ;
         tempx := line.c;
            end
         end;
      segment[outsidepoint,1] := trunc(tempx);
      segment[outsidepoint,2] := trunc(tempy);

     end; { compute_intersection }

   (************************************************************
   **            body of clip line starts here                **
   *************************************************************)

   begin { body of clip_line }
   done    := false;
   i       := 1;
   outside := false;
   result  := line;
   while (not done ) and ( i <= num_hplanes) do
      begin
      outcode[i,1] := compute_location(line[1],viewarea[i]);
      outcode[i,2] := compute_location(line[2],viewarea[i]);

      if outcode[i,1] and outcode[i,2] then
          begin
          outside := true;
          done    := true;
          end
      else
          outcode[i,3] := (not outcode[i,1]) and (not outcode[i,2]);
          { if both points are inside, skip that hplane, later }
      i := i + 1;
      end; { while }

   if ( not done ) then
      begin
      i := 1;
      while (i <=num_hplanes ) and (not done)  do
         begin
         if (not outcode[i,3]) then
            begin
            outcode[i,1] := compute_location(result[1],viewarea[i]);
            outcode[i,2] := compute_location(result[2],viewarea[i]);

            if (outcode[i,1] and outcode[i,2] ) then
               begin
               done    := true;
               outside := true;
               end
            else
               begin
               if outcode[i,1] or outcode[i,2] then
                  if ( outcode[i,1] ) then
                     compute_intersection(result,viewarea[i],1)
                  else
                     compute_intersection(result,viewarea[i],2);
               end;
            end;
         i := i + 1;
         end; { while not done }
      end; { if not done }
   end; { clip_line }

(************************************************************
**             matrix operation routines                   **
*************************************************************)



procedure  concatenate(leftmatrix,rightmatrix : matrixtype;
                             var   resultmatrix: matrixtype);
(************************************************************
**   concatenate:                                          **
**     multiply the left and right matrices and put the    **
**     result in resultmatrix.                             **
**                                                         **
**   local variables:                                      **
**     i,j,k : counters                                    **
**     temp  : temporary storage area for sum of a column  **
**                                                         **
*************************************************************)


  var
     i,j,k : integer;
     temp  : elementtype;

  begin { concatenate }
  for i := 1 to dimension do
     begin
     for j := 1 to dimension do
        begin
        temp := 0;
        for k  := 1 to dimension do
           temp := temp + leftmatrix[i,k] * rightmatrix[k,j] ;
        resultmatrix[i,j] := temp ;
        end;
     end;
  end; { concatenate }

procedure applymatrix(var segment : segmenttype;
                          matrix  : matrixtype );
(************************************************************
**  applymatrix:                                           **
**    multiply the segment vector by the matrix and return **
**    the result in the segment.                           **
**                                                         **
**  local variables:                                       **
**    i : counter                                          **
**    tempseg: temporary result of multiplication.         **
**                                                         **
*************************************************************)

   var
      i         : integer;
      tempseg   : segmenttype;

   begin { applymatrix }
   for i := 1 to 2 do
      begin
      tempseg[i,1] := segment[i,1] * matrix[1,1]+
                      segment[i,2] * matrix[2,1]+
                                   + matrix[3,1];
      tempseg[i,2] := segment[i,1] * matrix[1,2]+
                      segment[i,2] * matrix[2,2]+
                                   + matrix[3,2];
      tempseg[i,3] := 1;
      end; { for }
   segment := tempseg;
   end; {applymatrix }



(************************************************************
**             transformation routines                     **
*************************************************************)


procedure translate(var inputmatrix :matrixtype;
                         transvector : vectortype );
(************************************************************
**  translate:                                             **
**     add a translation by a translation vector to the    **
**     inputmatrix.                                        **
**                                                         **
**  local variables:                                       **
**     i : counters                                        **
**                                                         **
*************************************************************)

   var
     i  : integer;
   begin  { translate}
   for i := 1 to userdimension do
      inputmatrix[dimension,i] := inputmatrix[dimension,i]
                                 +transvector[i];
   end; { translate }


procedure scale(var inputmatrix  : matrixtype ;
                    scalevector  : vectortype );
(************************************************************
**  scale:                                                 **
**    concatenate a scaling matrix of value scalevector    **
**    to the input matrix.  the procedure is optimized     **
**                                                         **
**  local variables:                                       **
**    i,j : counters                                       **
**                                                         **
*************************************************************)

   var
   i , j : integer;
   begin  { scale }
   for i := 1 to userdimension do
      for j := 1 to dimension do
         inputmatrix[j,i]  := inputmatrix[j,i] * scalevector[i] ;
   end; { scale }
{$i graph2.pas }
 := 1 to dimension do
         inputmatrix[j,i]  := inputmatrix[j,i] * scalevector