[comp.lang.pascal] Source code listing for XGRAPH and XCRT

wct@po.CWRU.Edu (William C. Thompson) (04/25/91)

I got over 20 request in under 12 hours for source code for
these two new units, so I have decided to just post them to
the board instead.  It would save myself inordinate amounts
of time and energy.  Please report any bugs and send any 
thanks/offerings to wct@po.cwru.edu

unit xgraph;

{ Written by William C. Thompson (wct@po.cwru.edu) - 1991 }

{ This unit was written for programs with heavy graphics usage.
  There are a number of procedures to make graphics more bearable.
  There are some procedures that do different drawings.
  There are some procedures that can/recall a screen image. }

interface

uses graph,math;

procedure setfillcolor(col:word);
procedure setfillpattern(pat: word);
procedure settextfont(font:word);
procedure settextsize(size:word);
procedure settextdir(dir:word);
procedure settextall(font,dir,size,hor,ver:word);
procedure ngon(cx,cy,sides: word; r,ang: real);
procedure fline(x1,y1,x2,y2:word; warp,pixres:real);
procedure fbranch(fn:string ; warp,pixres:real);
procedure frip(fn: string; warp,pixres: real);
procedure saveimage(x1,y1,x2,y2:word; fn:string; var size:word);
procedure recallimage(x1,y1:word; fn: string; size:word; wput:byte);

implementation

type
  buffer = array[1..64000] of byte;

procedure setfillcolor(col:word);
var
  s: fillsettingstype;
begin
  getfillsettings(s);
  setfillstyle(s.pattern,col)
end;

procedure setfillpattern(pat: word);
var
  s: fillsettingstype;
begin
  getfillsettings(s);
  setfillstyle(pat,s.color)
end;

procedure settextfont(font:word);
var
  s: textsettingstype;
begin
  gettextsettings(s);
  settextstyle(font, s.direction, s.charsize)
end;

procedure settextsize(size:word);
var
  s: textsettingstype;
begin
  gettextsettings(s);
  settextstyle(s.font, s.direction, size)
end;

procedure settextdir(dir:word);
var
  s: textsettingstype;
begin
  gettextsettings(s);
  settextstyle(s.font, dir, s.charsize)
end;

procedure settextall(font,dir,size,hor,ver:word);
begin
  settextstyle(font,dir,size);
  settextjustify(hor,ver)
end;

procedure ngon(cx,cy,sides: word; r,ang: real);
var
  i: word;
begin
  for i:=0 to sides-1 do
    line(round(cx+r*cos(i/sides*2*pi+ang-pi/2)),
         round(cy+r*sin(i/sides*2*pi+ang-pi/2)),
         round(cx+r*cos((i+1)/sides*2*pi+ang-pi/2)),
         round(cy+r*sin((i+1)/sides*2*pi+ang-pi/2)));
end;

procedure fline(x1,y1,x2,y2:word; warp,pixres:real);
{ Generates a fractal line from (x1,y1) bent by warp % such that no
  two points are more than pixres pixels apart.  A higher warp means
  the line can deviate more.  Caution: a warp above 1.0 is not good }
var
  d,ang:real;
  x3,y3:word;        { point of elbow }
begin
  d:=distance(x1,y1,x2,y2);
  if d<=pixres then line(x1,y1,x2,y2)
  else begin
    ang:=random(65535)*9.5875262E-5;       { generate [0,2 pi) }
    x3:=round((x1+x2)/2+d/2*warp*sin(ang));
    y3:=round((y1+y2)/2+d/2*warp*cos(ang));
    fline(x1,y1,x3,y3,warp,pixres);
    fline(x3,y3,x2,y2,warp,pixres)
    end
end;

procedure fbranch(fn:string ; warp,pixres:real);
{ reads a fractal branch file from disk and draws it with
  parameters warp and pixres, as described in fline.  There
  is a maximum of 250 nodes.  Define a branch as follows:

  number of nodes                         e.g.  5
  list of each node's coordinates               100 100
                                                ...
  list of connections from node to node         1 2
                                                ... }
var
  f: text;
  i: word;
  a,b: word;             { node numbers }
  pts: word;             { number of nodes }
  x, y: array[1..250] of word;  { node coordinates }
begin
  assign(f,fn);
  reset(f);
  { read in points }
  readln(f,pts);
  for i:=1 to pts do readln(f,x[i],y[i]);
  while not eof(f) do begin
    readln(f,a,b);
    if [a,b]*[1..pts]=[a,b] then fline(x[a],y[a],x[b],y[b],warp,pixres)
    end;
  close(f);
end;

procedure frip(fn: string; warp,pixres: real);
{ Reads and draws a fractal rip (looks like a river)
  A rip file is defined as follows:

  List of coordinates to connect    e.g.    100 100
                                            150 120
                                            160 180
                                            ...

  This can be used to draw lakes, borders, etc. }
var
  x1,y1,x2,y2: word;
  f: text;
begin
  assign(f,fn);
  reset(f);
  { read first point }
  readln(f,x1,y1);
  while not eof(f) do begin
    readln(f,x2,y2);
    fline(x1,y1,x2,y2,warp,pixres);
    x1:=x2;
    y1:=y2
    end;
  close(f)
end;

procedure saveimage(x1,y1,x2,y2:word; fn:string; var size:word);
{ This procedure captures an image from the screen and writes
  it to the specified file and returns the size.  There is no error
  checking as to how much memory is available.  The size of an image
  is the number of pixels divided by two plus 6 (VGA mode) }
var
  f: file;
  p: ^buffer;
  n: word;
begin
  size:=imagesize(x1,y1,x2,y2);
  getmem(p,size);                 { allocate space }
  getimage(x1,y1,x2,y2,p^);       { capture the image from screen }
  assign(f,fn);
  rewrite(f,1);                   { objects are 1 byte large }
  blockwrite(f,p^,size,n);        { write image to disk }
  freemem(p,size);                { return memory }
  close(f);
end;

procedure recallimage(x1,y1:word; fn: string; size:word; wput:byte);
{ This procedure reads in an image from the specified file
  and puts the image on the screen }
var
  f: file;
  p: ^buffer;
  n: word;
begin
  getmem(p,size);                { allocate space }
  assign(f,fn);                  { prepare file }
  reset(f,1);                    { object are 1 byte large }
  blockread(f,p^,size,n);        { read in image }
  close(f);
  putimage(x1,y1,p^,wput);       { put image on screen }
  freemem(p,size);               { return memory }
end;

end.


unit keydef;

{ This unit contains all the constants and transformations for
  the xcrt unit function ReadAllKeys }

interface

const
  { These are the constants for readallkeys }
  CtrlF1:char=#138; CtrlF2:char=#139; CtrlF3:char=#140; CtrlF4:char=#141;
  CtrlF5:char=#142; CtrlF6:char=#143; CtrlF7:char=#144; CtrlF8:char=#145;
  CtrlF9:char=#146; CtrlF10:char=#147;
  ShftF1:char=#148; ShftF2:char=#149; ShftF3:char=#150; ShftF4:char=#151;
  ShftF5:char=#152; ShftF6:char=#153; ShftF7:char=#154; ShftF8:char=#155;
  ShftF9:char=#156; ShftF10:char=#157;
  AltF1:char=#158; AltF2:char=#159; AltF3:char=#160; AltF4:char=#161;
  AltF5:char=#162; AltF6:char=#163; AltF7:char=#164; AltF8:char=#165;
  AltF9:char=#166; AltF10:char=#167;
  AltA:char=#168; AltB:char=#169; AltC:char=#170; AltD:char=#171;
  AltE:char=#172; AltF:char=#173; AltG:char=#174; AltH:char=#175;
  AltI:char=#176; AltJ:char=#177; AltK:char=#178; AltL:char=#179;
  AltM:char=#180; AltN:char=#181; AltO:char=#182; AltP:char=#183;
  AltQ:char=#184; AltR:char=#185; AltS:char=#186; AltT:char=#187;
  AltU:char=#188; AltV:char=#189; AltW:char=#190; AltX:char=#191;
  AltY:char=#192; AltZ:char=#193;
  Ins:char=#194; Home:char=#195; PgUp:char=#196; Del:char=#197;
  EndKey:char=#198; PgDn:char=#199; UpArrow:char=#200; LeftArrow:char=#201;
  RightArrow:char=#202; DownArrow:char=#203;
  CtrlLeftArrow:char=#204; CtrlRightArrow:char=#205;
  F1:char=#206; F2:char=#207; F3:char=#208; F4:char=#209; F5:char=#210;
  F6:char=#211; F7:char=#212; F8:char=#213; F9:char=#214; F10:char=#215;
  Alt1:char=#216; Alt2:char=#217; Alt3:char=#218; Alt4:char=#219;
  Alt5:char=#220; Alt6:char=#221; Alt7:char=#222; Alt8:char=#223;
  Alt9:char=#224; Alt0:char=#225;
  CtrlHome:char=#226; CtrlEnd:char=#227; CtrlPgUp:char=#228;
  CtrlPgDn:char=#229;

function transformedkey(c:char):char;

implementation

function transformedkey(c:char):char;
begin
 case ord(c) of
   59..68:transformedkey:=chr(ord(c)+ord(F1)-59);       { F1-F10 }
   94..103:transformedkey:=chr(ord(c)-94+ord(CtrlF1));  { CtrlF1-CtrlF10 }
   84..93:transformedkey:=chr(ord(c)-84+ord(ShftF1));   { ShftF1-ShftF10 }
   104..113:transformedkey:=chr(ord(c)-104+ord(AltF1)); { AltF1-AltF10 }
   30:transformedkey:=AltA; 48:transformedkey:=AltB; 46:transformedkey:=AltC;
   32:transformedkey:=AltD; 18:transformedkey:=AltE; 33:transformedkey:=AltF;
   44:transformedkey:=AltG; 35:transformedkey:=AltH; 23:transformedkey:=AltI;
   36:transformedkey:=AltJ; 37:transformedkey:=AltK; 38:transformedkey:=AltL;
   50:transformedkey:=AltM; 49:transformedkey:=AltN; 24:transformedkey:=AltO;
   25:transformedkey:=AltP; 16:transformedkey:=AltQ; 19:transformedkey:=AltR;
   31:transformedkey:=AltS; 20:transformedkey:=AltT; 22:transformedkey:=AltU;
   47:transformedkey:=AltV; 17:transformedkey:=AltW; 45:transformedkey:=AltX;
   21:transformedkey:=AltY; 44:transformedkey:=AltZ;
   120..129:transformedkey:=chr(ord(c)-120+ord(Alt1));  { Alt1-Alt0 }
   72:transformedkey:=UpArrow;
   75:transformedkey:=LeftArrow;
   77:transformedkey:=RightArrow;
   80:transformedkey:=DownArrow;
   115:transformedkey:=CtrlLeftArrow;
   116:transformedkey:=CtrlRightArrow;
   82:transformedkey:=Ins;
   71:transformedkey:=Home;
   73:transformedkey:=PgUp;
   83:transformedkey:=Del;
   79:transformedkey:=EndKey;
   81:transformedkey:=PgDn;
   119:transformedkey:=CtrlHome;
   117:transformedkey:=CtrlEnd;
   132:transformedkey:=CtrlPgUp;
   118:transformedkey:=CtrlPgDn;
   end
end;

end.



unit xcrt;

{ Written by William C. Thompson (wct@po.cwru.edu) - 1991
  Special thanks to Mike Lacy for contributions

  As yet, this unit is only designed to handle screens with
  80 columns.  Including checking for 40 columns would slow
  down the procedures which are intended to be very fast.
  A program using 40 columns could easily borrow the ideas
  used in this unit.  I have confirmed that they do work for
  43/50 rows.  It doesn't work for 40 columns. }

interface

uses crt,dos,keydef;

type
  screenpoint=record            { occupies 3 bytes of space }
    c:char;
    colattr: word;
    end;
  screenpt=^screen;
  screen=array[0..3999] of screenpoint;
  { This is a maximum size for a screen - 80 columns * 50 rows = 4000.
    The maximum space required would then be 12000 bytes. }
  block=record
    rows,cols: word;
    sp: screenpt
    end;

procedure beep(hz,dur: word);
function getch(x,y: byte): char;
procedure putch(x,y: byte; c: char);
procedure writexy(x,y: byte; s: string);
procedure colorxy(x,y,forecol,backcol,blink: byte);
procedure textbox(nwx,nwy,sex,sey: word);
procedure textxbox(nwx,nwy,sex,sey: word);
procedure textline(startat,endat,c:word; attr:byte);
function readallkeys:char;
function yesorno:char;
function getoneof(s:string):char;
procedure cursoron;
procedure cursoroff;
procedure savewindow(x1,y1,x2,y2: word; var w: block);
procedure killwindow(var w:block);
procedure recallwindow(x1,y1:word; var w: block);

implementation

const
  base:word=$B800;        { This may change for another machine }

procedure beep(hz,dur: word);
begin
  sound(hz);
  delay(dur);
  nosound
end;

function getch(x,y: byte):char;
{ returns character at absolute position x,y through memory
  The error checking has been removed to speed up function }
begin
  getch:=char(mem[base:(160*y+2*x-162)]);    { 2*80*(y-1)+2*(x-1) }
end;

procedure putch(x,y: byte; c: char);
{ QUICKLY writes c to absolute position x,y through memory
  This is at least 10 times faster than a gotoxy(x,y), write(c)
  Another bonus is that the cursor doesn't move.
  The error checking has been removed  }
begin
  mem[base:(160*y+2*x-162)]:=ord(c);
  mem[base:(160*y+2*x-161)]:=textattr;
end;

procedure writexy(x,y: byte; s: string);
{ Writes string at x,y and returns to old position.  The coordinates
  are relative to the current window. }
var
  ox,oy: byte;
begin
  ox:=wherex;
  oy:=wherey;
  gotoxy(x,y);
  write(s);
  gotoxy(ox,oy)
end;

procedure colorxy(x,y,forecol,backcol,blink: byte);
{ Directly change the color attributes of char at absolute screen x,y
  To turn blink on, set it to 128 }
var
  offset: word;
begin
  if (x>80) or (y>25) then exit;
  mem[base:(160*y+2*x-161)]:=forecol+16*backcol+blink;
end;

procedure textbox(nwx,nwy,sex,sey: word);
{ draws a thin text defined by the two points }
var
  i: integer;
begin
  { handle special cases first, nwx=sex or nwy=sey }
  if nwx=sex then   { straight line down }
    for i:=nwy to sey do putch(nwx,i,#179)
  else if nwy=sey then  { straight line across }
    for i:=nwx to sex do putch(i,nwy,#196)
  else if (nwx<sex) and (nwy<sey) then begin
    { draw corners }
    putch(nwx,nwy,#218);
    putch(nwx,sey,#192);
    putch(sex,sey,#217);
    putch(sex,nwy,#191);
    { draw lines }
    for i:=nwy+1 to sey-1 do putch(nwx,i,#179);
    for i:=nwy+1 to sey-1 do putch(sex,i,#179);
    for i:=nwx+1 to sex-1 do begin
      putch(i,nwy,#196);
      putch(i,sey,#196);
      end
    end
end;

procedure textxbox(nwx,nwy,sex,sey: word);
{ draws a thick text defined by the two points }
var
  i: integer;
begin
  { handle special cases first, nwx=sex or nwy=sey }
  if nwx=sex then   { straight line down }
    for i:=nwy to sey do putch(nwx,i,#186)
  else if nwy=sey then  { straight line across }
    for i:=nwx to sex do putch(i,nwy,#205)
  else if (nwx<sex) and (nwy<sey) then begin
    { draw corners }
    putch(nwx,nwy,#201);
    putch(nwx,sey,#200);
    putch(sex,sey,#188);
    putch(sex,nwy,#187);
    { draw lines }
    for i:=nwy+1 to sey-1 do putch(nwx,i,#186);
    for i:=nwy+1 to sey-1 do putch(sex,i,#186);
    for i:=nwx+1 to sex-1 do begin
      putch(i,nwy,#205);
      putch(i,sey,#205);
      end;
    end
end;

procedure textline(startat,endat,c:word; attr:byte);
{ draws a line -
  attr=0  =>  thin horizontal       attr=1  =>  thin vertical
  attr=2  =>  thick horizontal      attr=3  =>  thick vertical

  The first two parameters are the starting and ending values
  of the range of the line, vertical or horizontal.  The third
  is the constant value.  i.e. horiz => (x1,x2,y), vert => (y1,y2,x) }
var
  i: integer;
begin
  if attr mod 2=0 then begin
    gotoxy(startat,c);
    if attr div 2=0 then for i:=startat to endat do putch(i,c,#196)
    else for i:=startat to endat do putch(i,c,#205)
    end
  else
    if attr div 2=0 then for i:=startat to endat do putch(c,i,#179)
    else for i:=startat to endat do putch(c,i,#186)
end;

function readallkeys:char;
{ This function correctly reads in a keypress and returns the
  correct value for "other" keys.  See the KEYDEF unit for what
  each special key returns.  Note: the function doesn't return
  actual character for special keys (F1-F10,etc.) - it is only
  a character to represent the special key that was pressed. }
var
  ch: char;
  ch2: char;
begin
  ch:=readkey;
  if ch=#0 then readallkeys:=transformedkey(readkey)
  else readallkeys:=ch
end;

function yesorno:char;
{ waits for the user to press 'y','Y','n','N' }
var
  ch: char;
begin
  repeat
    ch:=upcase(readallkeys)
  until ch in ['Y','N'];
  yesorno:=ch
end;

function getoneof(s:string):char;
{ waits for the user to input a character contained in s }
var
  ch: char;
begin
  repeat
    ch:=readallkeys
  until pos(ch,s)>0;
  getoneof:=ch
end;

procedure cursoron;
var
  regs: registers;
begin
  regs.ah:=1;
  regs.ch:=11;
  regs.cl:=13;
  intr($10, regs);
end;

procedure cursoroff;
var
  regs: registers;
begin
  regs.ch:=regs.ch or $20;
  regs.ah:=1;
  intr($10, regs);
end;

procedure savewindow(x1,y1,x2,y2: word; var w: block);
{ This procedure saves a screen block.  It is not intended to
  open up a window, but can be used to store what is underneath
  a window.  (absolute coordinates) }
var
  i,j: word;
  size: word;
begin
  with w do begin
    rows:=0;
    cols:=0;
    if (x2<x1) and (y2<y1) then exit;    { invalid window }
    rows:=x2-x1+1;
    cols:=y2-y1+1;
    size:=rows*cols*3;    { bytes required to store screen }
    getmem(sp,size);      { allocate sufficient space }
    for i:=0 to rows-1 do
      for j:=0 to cols-1 do with sp^[j*rows+i] do begin
        c:=char(mem[base:(160*(j+y1)+2*(i+x1)-162)]);
        colattr:=mem[base:(160*(j+y1)+2*(i+x1)-161)];
        end
    end
end;

procedure killwindow(var w:block);
{ Free space taken up by screen block (absolute coordinates) }
begin
  with w do freemem(sp,rows*cols*3)
end;

procedure recallwindow(x1,y1:word; var w:block);
{ redraw window at (x1,y1) (absolute coordinates) }
var
  i,j: word;
begin
  with w do begin
    for i:=0 to rows-1 do
      for j:=0 to cols-1 do with sp^[j*rows+i] do begin
        mem[base:(160*(j+y1)+2*(i+x1)-162)]:=ord(c);
        mem[base:(160*(j+y1)+2*(i+x1)-161)]:=colattr   { color it }
        end
    end
end;

end.


There you go.  If I make any serious changes, I will post.  Once
again, please report any bugs or changes you feel are necessary/
beneficial.

-- 
Ticking away, the moments that make up a dull day.   | William C. Thompson
You fritter and waste the hours in an offhand way.   | Michelson 620D (wct)
Kicking around on a piece of ground in your hometown,| a.k.a. Master of Time,
waiting for someone or something to show you the way.| Space, and Dimension