

procedure arbitraryscale(var inputmatrix  : matrixtype ;
                             scalevector  : vectortype;
                                    point : pointtype );
(************************************************************
**  arbitrary scale:                                       **
**    scale about the given point by using translatinon    **
**    and scale procedure. concatenate the result to the   **
**    inputmatrix.                                         **
**                                                         **
**  local variables:                                       **
**    i : counter                                          **
**    negativepoint: negate of the input point             **
**                                                         **
*************************************************************)

{ add a scaling to the inputmatrix about the given point}
   var
   i            : integer;
   negativepoint : pointtype;
   begin { arbitrarscale }
   for i := 1 to userdimension do
      negativepoint[i] := -1*point[i] ;
   translate(inputmatrix,negativepoint);
   scale(inputmatrix,scalevector);
   translate(inputmatrix,point);
   end; { arbitraryscale }


procedure arbitraryrotate(var inputmatrix : matrixtype ;
                              point       : pointtype  ;
                              angle       : integer    );
(************************************************************
**  arbitraryrotate:                                       **
**     apply a rotation about the given point by the given **
**     angle( in degrees ). and concatenate the result     **
**     with the input matrix. procedure is optimized       **
**                                                         **
**  local variables:                                       **
**    rotation,tempmatrix : temporary matrices             **
**    radian              : value of angle in radians      **
**                                                         **
*************************************************************)

   var
      rotationmatrix,
      tempmatrix  : matrixtype;
      radian      : real;
   begin  { arbitraryrotate }
   radian := angle*pi/180;
   rotationmatrix[1,1] := cos(radian);
   rotationmatrix[1,2] := sin(radian);
   rotationmatrix[1,3] := 0 ;
   rotationmatrix[2,1] := -1*sin(radian) ;
   rotationmatrix[2,2] := cos(radian);
   rotationmatrix[2,3] := 0 ;
   rotationmatrix[3,1] := point[1]*(1-cos(radian))+point[2]*sin(radian) ;
   rotationmatrix[3,2] := point[2]*(1-cos(radian))-point[1]*sin(radian) ;
   rotationmatrix[3,3] := 1;
   concatenate(inputmatrix,rotationmatrix,tempmatrix);
   inputmatrix := tempmatrix;
   end;

(************************************************************
**           viewing transformtion routines                **
*************************************************************)


procedure init_clip_rectangle(var viewarea : viewareatype);
(************************************************************
**                                                         **
**  init_clip_rectangle:                                   **
**    reset the viewarea with the new values of the view   **
**    -port                                                **
**                                                         **
**  local variables:                                       **
**    i : counter                                          **
**                                                         **
*************************************************************)

   var
     i  : integer;

   begin  { init_clip_rectangle }
   with viewarea[1] do
      begin
      a := 1;
      b := 0;
      c := viewminx;
      orgdir := true;
      end;
   with viewarea[2] do
      begin
      a := 1;
      b := 0;
      c := viewmaxx;
      orgdir := false;
      end;
   with viewarea[3] do
      begin
      a := 0;
      b := 1;
      c := viewminy;
      orgdir := true;
      end;
   with viewarea[4] do
      begin
      a := 0;
      b := 1;
      c := viewmaxy;
      orgdir := false;
      end;
   end; { init_clip_rectangle }

procedure updatewindow(var windowmatrix : matrixtype );
(************************************************************
**                                                         **
** updates the viewing transformation matrix according to  **
** the values in global variables :                        **
**     xwindsize,ywindsize : window values,                **
** viewminx,viewminy,viewmaxx,                             **
**                    viewmaxy : viewport coordinates      **
**                                                         **
**     using the formula of                                **
**          xv = sx(xw - xwmin)+ xvmin                     **
**          yv = sy(yw - ywmin)+ yvmin                     **
**                                                         **
*************************************************************)

   begin
   windowmatrix[1,1] := (viewmaxx - viewminx) / (xwindsize );
   windowmatrix[1,2] := 0;
   windowmatrix[1,3] := 0;
   windowmatrix[2,1] := 0;
   windowmatrix[2,2] := (viewmaxy - viewminy) / (ywindsize );
   windowmatrix[2,3] := 0;
   windowmatrix[3,1] := viewminx - xwindpos * windowmatrix[1,1];
   windowmatrix[3,2] := viewminy - ywindpos * windowmatrix[2,2];
   windowmatrix[3,3] := 1;
   end; { updatewindow }

procedure resetview ;
(************************************************************
**   resetview:                                            **
**     reinitialize the window and viewport so the         **
**     object is visible.                                  **
**                                                         **
*************************************************************)

   begin
   viewminx := 0;
   viewminy := 0;
   viewmaxx := xscreensize;
   viewmaxy := yscreensize;
   xwindsize := 8;
   ywindsize := 8;
   xwindpos := -4;
   ywindpos := -4;
   init_clip_rectangle( myviewarea  );
   updatewindow(windowmatrix);
   end; { resetview }


(************************************************************
**                user menu routines                       **
*************************************************************)



procedure gettranslate(var  transmatrix : matrixtype);
(************************************************************
**                                                         **
**  gettranslate:                                          **
**     get a translation vector from the user and do       **
**     the appropriate translation                         **
**                                                         **
**  local variables:                                       **
**     tempvector : user transformtion vector              **
**                                                         **
*************************************************************)

   var
      transvector : vectortype;
   begin
   writeln;
   writeln( '**  give me the translation values ** ');
   writeln;
   readvector(transvector);
   translate(transmatrix , transvector );
   print(transmatrix);
   end; { gettranslate }

procedure getscale( var transmatrix : matrixtype);
(************************************************************
**                                                         **
**  getscale:                                              **
**      get a scaling vector and a point about which to    **
**      scale . and do the scaling                         **
**                                                         **
**  local variables:                                       **
**      point : point about which to scale                 **
**      scalevect : user scaling vector                    **
**                                                         **
*************************************************************)

   var
      point : pointtype;
      scalevect : vectortype;
   begin
   writeln;
   writeln('****** scale about a point ******');
   writeln('** first give me the point about which to scale **  ');
   writeln;
   readvector(point);
   writeln;
   writeln('** now give me the scaling vector ** ');
   writeln;
   readvector(scalevect);
   arbitraryscale(transmatrix , scalevect , point) ;
   print(transmatrix);
   end; { getscale }

procedure getrotate( var transmatrix : matrixtype);
(************************************************************
**   getrotate:                                            **
**     get a point and angle of rotation from the user     **
**     and go do the actual rotation. add it to the        **
**     transformatiion matrix                              **
**                                                         **
**  local variables:                                       **
**     point : user rotation point                         **
**     angle : angle of rotation                           **
**                                                         **
*************************************************************)
   var
      point : pointtype;
      angle : integer;
   begin
   writeln;
   writeln('*****   rotate about a point    *******');
   writeln('**  first give me the rotation value **');
   write('** in degrees counterclockwise? ');
   readln( angle);
   writeln;
   writeln('** now give me the point to rotate about **');
   readvector(point);
   arbitraryrotate(transmatrix ,  point , angle);
   print(transmatrix);
   end; { getrotate }

procedure changeviewport;
(************************************************************
**  changeviewport:                                        **
**     get the new values of the viewport from the user    **
**     and reset the wiewing matrix and viewarea to        **
**     reflect the change.                                 **
**                                                         **
**  local variables:                                       **
**     temp : temporary normalize form of viewport location**
**                                                         **
*************************************************************)

   var
    temp : real;
   begin
   writeln;
   writeln('********      change viewport    **********');
   writeln;
   writeln('** enter the coordinates of the viewport **');
   writeln('**    in normalized form (real 0..1 )    **');
   repeat
      write('** minimum x-axis? ');
      readln(temp);
      viewminx := trunc( temp * xscreensize );
      write('** maximum x-axis? ');
      readln(temp);
      viewmaxx := trunc(temp * xscreensize );
      write('** minimum y-axis? ');
      readln(temp);
      viewminy := trunc( temp * yscreensize );
      write('** maximum y-axis? ');
      readln(temp);
      viewmaxy := trunc( temp * yscreensize );
   until ((viewminx < viewmaxx) and (viewminy < viewmaxy));
   updatewindow(windowmatrix);
   init_clip_rectangle(myviewarea );
   print(windowmatrix);
   end;

procedure changewindow;
(************************************************************
**  changewindow:                                          **
**      get the new window size from the user and update   **
**      the wiewing matrix                                 **
**                                                         **
*************************************************************)

   begin
   writeln;
   writeln('********  change window size  ********');
   writeln;
   writeln('** enter the size of the window     ** ');
   writeln('** in integer form ,can not be zero **');
   repeat
      write('** size in x direction? ');
      readln(xwindsize);
      until xwindsize <> 0;
   repeat
      write('** size in y direction? ');
      readln(ywindsize);
      until ywindsize <> 0;
   init_clip_rectangle(myviewarea );
   updatewindow( windowmatrix);
   print(windowmatrix);
   end; { changewindow }

procedure movewindow;
(************************************************************
**  movewindow:                                            **
**    get the new location of the window and update the    **
**    viewing matrix                                       **
**                                                         **
*************************************************************)

   begin
   writeln;
   writeln('*********    move the window    **********');
   writeln;
   writeln('** enter the new location of the window **');
   writeln('** this is the location of the lower    ** ');
   writeln('** lefthand corner  of the window       ** ');
   writeln;
   write('** x coordinate ? ');
   readln(xwindpos);
   write('** y coordinate ? ');
   readln(ywindpos);
   updatewindow(windowmatrix);
   print(windowmatrix);
   end; { movewindow }



procedure drawline( segment : segmenttype;
                    matrix  : matrixtype   );
(************************************************************
**  drawline:                                              **
**     draw the line segment after applying the given      **
**     matrix to it. and clipping it to the viewport.      **
**     used by the draw window procedure                   **
**                                                         **
**  local variables:                                       **
**     outside : whether the line is totally outside or not**
**                                                         **
*************************************************************)

   var
     outside : boolean;
   begin
   applymatrix(segment,matrix);
   clip_line(segment,segment,myviewarea,outside);
   if not outside then
      begin
      line(trunc(segment[1,1]),trunc(segment[1,2]),
           trunc(segment[2,1]),trunc(segment[2,2]));
      end;
   end; { drawline }

procedure drawsymbol(symbol : command) ;
(************************************************************
**  drawsymbol:                                            **
**    draw the symbol which is a list of commands.         **
**    each command could be a line or polygon              **
**                                                         **
**  local variables:                                       **
**    tempmatrix : result of concatenation of trans and    **
**                 view matrices                           **
**    tempcommand : local pointer to the symbol commands   **
**     polyptr    : pointers to the polygon nodes          **
**     tempsegment: temporary line segment                 **
**                                                         **
*************************************************************)
   var
      tempmatrix : matrixtype;
      tempcommand: command;
      polyptr1,
      polyptr2   : polygontyp;
      tempsegment: segmenttype;
   begin

   hires;
   hirescolor(white);

   line(viewminx,viewminy,viewmaxx,viewminy);
   line(viewminx,viewmaxy,viewmaxx,viewmaxy);
   line(viewminx,viewminy,viewminx,viewmaxy);
   line(viewmaxx,viewminy,viewmaxx,viewmaxy);

   concatenate(transmatrix,windowmatrix,tempmatrix);
   tempcommand := symbol;
   while ( tempcommand <> nil ) do
      begin
      with tempcommand^ do
         begin
         case kind of
            lineseg : begin
                      drawline(segment , tempmatrix );
                      end;
            poly    : begin
                      polyptr1 := polygon;
                      polyptr2 := polygon^.next;
                      repeat
                         tempsegment[1] := polyptr1^.point;
                         tempsegment[2] := polyptr2^.point;
                         drawline(tempsegment , tempmatrix);
                         polyptr1 := polyptr2;
                         polyptr2 := polyptr2^.next;
                      until (polyptr1 = polygon );
                      end;
            end; { case }
         tempcommand := tempcommand^.next;
         end; { with }
      end; { while }

   gotoxy(24,1);
   writeln('press a key to continue');
   while not keypressed do;
   textmode(bw80);
   textcolor(white);
   end; { draw }




procedure menu;
(************************************************************
**   menu:                                                 **
**     give the user a menu to work with.                  **
**     has toggle print and expert mode options            **
**                                                         **
*************************************************************)

   var
     i : integer;
     expert : boolean;
     done   : boolean;
     c      : char;
   begin
   expert := false;
   done   := false;
   repeat
   if not expert then
      begin
      writeln('********  user menu options ********* ');
      writeln('** 0. quit this program ');
      writeln('** 1. translate the model');
      writeln('** 2. scale the model about a point ');
      writeln('** 3. rotate the model about a point ');
      writeln('** 4. reset the transformation matrix');
      writeln('** 5. reset the viewing and viewport ');
      writeln('** 6. change the viewport');
      writeln('** 7. change the window size');
      writeln('** 8. change window location');
      writeln('** 9. clear the screen ');
      writeln('**10. draw the model ');
      writeln('**11. set expert mode ');
      writeln('**12. toggle print mode ');
      end
   else
   begin
   writeln(' 0. quit      1. trans      2. scal      3. rotat   4. rst-trans');
   writeln(' 5. rst-view  6. chg-view   7. wnd-size  8. wnd-loc 9. clr');
   writeln('10. draw     11.novice     12. togl-prnt  ');
   end;
   repeat
     write('** your choice (0 to 12)? ');
     readln(i);
     until ((i>=0) and ( i<=12));
   case i of
      0 : begin
          write('are you sure (y/n) ? ');
          readln(c);
          if c in ['y','Y'] then
             done := true;
          end;
      1 : gettranslate(transmatrix) ;
      2 : getscale(transmatrix)     ;
      3 : getrotate(transmatrix)    ;
      4 : setidentity(transmatrix);
      5 : resetview;
      6 : changeviewport;
      7 : changewindow;
      8 : movewindow;
      9 : {clearscreen} ;
      10: drawsymbol(mysymbol) ;
      11: expert := not expert ;
      12: printmode := not printmode ;
      end; { case }
   until done ;
   end; { menu }



(************************************************************
**                  main program                           **
*************************************************************)

begin  { main }
xscreensize := 639;
yscreensize := 199;
printmode := false;
initialize;
resetview;
define_model(mysymbol);
setidentity(transmatrix);
menu;
end.   { main }
9;
printmode := false;
initializ