#include <stdlib.h>
#include <bios.h> /* for biosprint */
#include "grafprt.h"
#include "arrays.h"

#define MAXPRINTROWS 100
#define MAXCOLS 960
#define MAXROWS (MAXPRINTROWS * 16)

/*--------------------------------------------------------------------*/
/*  module grafprt.c in turbo c version 2.0, large code model.        */
/*                                                                    */
/*  a set of graphics routines for high-resolution printer graphics   */
/*  on Epson FX, LX and other IBM Graphics Printer compatibles.       */
/*                                                                    */
/*  sample usage:                                                     */
/*                                                                    */
/*  initgrafprt();                                                    */
/*  portrait();                                                       */
/*  frame();                                                          */
/*  defreg(0.0,1.0,0.5,2.0,200,860,700,1500);                         */
/*  framxy();                                                         */
/*  prtlin(0.1,0.3,0.5,1.5,1);                                        */
/*  prntgr(0);                                                        */
/*                                                                    */
/*  released to the public domain by Gregory K. Landheim on January   */
/*  1, 1989, (c) all rights reserved.  anybody is authorized to use   */
/*  this code for any purpose whatsoever on the condition that they   */
/*  realize I assume absolutely no liability and give no warrantee    */
/*  for its use or performance.  it is distributed "as is", and if    */
/*  you use it you assume full liability for its use.                 */
/*                                                                    */
/*--------------------------------------------------------------------*/
/*                                                                    */
/*  initgrafprt()                                                     */
/*             allocates arrays for bit map from the heap             */
/*             and initializes certain global variables.              */
/*  dismem()   de-allocates arrays for bit map                        */
/*  colorset() sets global Color for use by line drawing routines     */
/*  pset()     plots a point in the bit map                           */
/*  plank()    blanks a point in the bit map                          */
/*  prntgr()   sends the graphics page (bit map) to the printer       */
/*  iprtln()   draws a line in device integer coordinates             */
/*  frame()    draws a boundary on the maximum plotting region        */
/*  framxy()   draws a boundary around the real world region          */
/*  transf()   transforms real world (x,y) to integer world  (ix,iy)  */
/*  itransf()  transforms int world (ix,iy) to plot device (ix,iy)    */
/*  defreg()   defines correspondence between integer and real worlds */
/*  prtlin()   draws a line specified in real world coordinates       */
/*                                                                    */
/*  a note about worlds:                                              */
/*  the real world is right-handed and rectangular and is expressed   */
/*  in floating point.                                                */
/*  the integer world is the right-handed and rectangular integer     */
/*  representation of the maximum pixel plotting domain of the        */
/*  plot device.                                                      */
/*  the plot device is left-handed and rectangular.                   */
/*                                                                    */
/*  Printer page, Portrait mode:                                      */
/*                                                                    */
/*          (0,1959)=[0,0] ------------------  [959,0]=(959,1599)     */
/*                         |                |                         */
/*  [x,y] = printer        |                |                         */
/*          coordinates    |                |                         */
/*                         |                |                         */
/*  (x,y) = integer world  |                |                         */
/*          coordinates    |                |                         */
/*                         |                |                         */
/*                         |                |                         */
/*                         |                |                         */
/*                         |                |                         */
/*          (0,0)=[0,1959] ------------------ [959,1599]=(959,0)      */
/*                                                                    */
/*                                                                    */
/*  Printer page, Landscape mode:                                     */
/*                                                                    */
/*           [959,0] --------------------------------- [959,1599]     */
/*           (0,959) |                               | (1599,959)     */
/*                   |                               |                */
/*                   |                               |                */
/*                   |                               |                */
/*                   |                               |                */
/*                   |                               |                */
/*                   |                               |                */
/*           [0,0]   --------------------------------- [0,1599]       */
/*           (0,0)                                     (1599,0)       */
/*                                                                    */
/*  defreg() is used to define a real world window within the         */
/*  integer world.  the integer world coordinates used depend on      */
/*  whether you are in portrait or landscape mode.                    */
/*                                                                    */
/*--------------------------------------------------------------------*/

int Arx,Ary;                       /* printer aspect ratio parameters */
int Maxwidth;                      /* pixel width of plot device      */
int Maxheight;                     /* pixel height of plot device     */
int Portrait = 1;                  /* flag, indicates whether to plot */
                                   /* in portrait or landscape mode   */

int Color = 1;                     /* color used for drawing lines    */

unsigned char **Evenrow;           /* buffer for printer pixel map    */
unsigned char **Oddrow;            /* buffer for printer pixel map    */

float X_min, X_max, Y_min, Y_max;        /* real world minima, maxima */
int  IX_min, IX_max, IY_min, IY_max;  /* integer world minima, maxima */
                     /* corresponding to real world minima and maxima */

float Dw,Dh;                       /*     real world delta-x, delta-y */
int IDw,IDh;                       /*  integer world delta-x, delta-y */
                  /* corresponding to real world delta-x  and delta-y */

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  void portrait()                                                   */
/*                                                                    */
/*  toggles global variable Portrait to indicate portrait mode        */
/*  plotting.                                                         */
/*                                                                    */
/*--------------------------------------------------------------------*/

void portrait()

{
   Portrait = 1;

}  /* void portrait() */

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  void landscape()                                                  */
/*                                                                    */
/*  toggles global variable Portrait to indicate landscape mode       */
/*  plotting.                                                         */
/*                                                                    */
/*--------------------------------------------------------------------*/

void landscape()

{
   Portrait = 0;

} /* void landscape() */

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  int initgrafprt()                                                 */
/*                                                                    */
/*  Sets aside space in the heap for the printer bit map. The pixel   */
/*  data will be stored in two sets of MAXCOL global arrays, one set  */
/*  for the odd-numbered rows of pixels, and one for the even-numbered*/
/*  rows of pixels.                                                   */
/*  Since calloc() is called instead of malloc(), the arrays are      */
/*  initialized to zero.                                              */
/*  If the memory is successfully allocated, initgrafprt returns a    */
/*  value of 1, else 0.                                               */
/*                                                                    */
/*  There must be about 188 kb of memory available for the bit map in */
/*  order to call this routine successfully.                          */
/*                                                                    */
/*--------------------------------------------------------------------*/

int initgrafprt()

{
/* prototype for alloc_2d_array() is in arrays.h                      */

   Evenrow = (unsigned char **)alloc_2d_array(MAXCOLS,MAXPRINTROWS,
                               sizeof(char *),sizeof(char));
   if (!Evenrow) return(0);

   Oddrow = (unsigned char **)alloc_2d_array(MAXCOLS,MAXPRINTROWS,
                               sizeof(char *),sizeof(char));
   if (!Evenrow) return(0);

   Arx = 4;
   Ary = 5;
   Maxwidth = MAXCOLS - 1;
   Maxheight = MAXROWS - 1;

   return(1);

}  /* int initgrafprt() */

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  void dismem()                                                     */
/*                                                                    */
/*  Disposes of space in the heap previously allocated for the printer*/
/*  bit map allocated in function initgrafprt().  The pixel data      */
/*  was stored in two global arrays, one for  the odd-numbered rows   */
/*  of pixels, and one for the even-numbered rows of pixels.          */
/*                                                                    */
/*  In ordinary use, this routine should not be explicitly called     */
/*  except where it is called in prntgr(). If initgrafprt() is called */
/*  and prntgr() is not called, be sure to call dismem() to dispose   */
/*  of the bit map memory from the heap.                              */
/*                                                                    */
/*--------------------------------------------------------------------*/

void dismem()

{
   free_2d_array((void **) Evenrow,MAXCOLS);
   free_2d_array((void **) Oddrow,MAXCOLS);

}  /* void dismem() */

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  void far colorset(int color)                                      */
/*                                                                    */
/*  sets global variable Color as flag for line drawing routines      */
/*  if color = 1, pset() is used, else pblank()                       */
/*                                                                    */
/*--------------------------------------------------------------------*/

void far colorset(int color)

{
  Color = color;

} /* void colorset() */

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  void pset(int x,int y)                                            */
/*                                                                    */
/*  Writes the dot at position (x,y) into memory arrays (bit map).    */
/*                                                                    */
/*  x must be less than or equal to Maxwidth.  y must be less than or */
/*  equal to Maxheight. Both x and y must be greater than or equal to */
/*  0.  initgrafprt() must be called before pset() is called for the  */
/*  first time.                                                       */
/*                                                                    */
/*--------------------------------------------------------------------*/

void pset(int x,int y)

{
   static unsigned char o[8] = {128,64,32,16,8,4,2,1};

   ((y % 2) == 0) ? (Evenrow[x][y >> 4] |= o[y % 16 >> 1]) :
                    (Oddrow[x][y >> 4] |= o[y % 16 >> 1]);

}  /* void pset() */

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  void pblank(int x,int y)                                          */
/*                                                                    */
/*  Blanks the dot at position (x,y) in memory arrays (bit map).      */
/*                                                                    */
/*  x must be less than or equal to Maxwidth. y must be less than or  */
/*  equal to Maxheight. Both x and y must be greater than or equal to */
/*  0.  initgrafprt() must be called before pblank() is called for    */
/*  the first time.                                                   */
/*                                                                    */
/*--------------------------------------------------------------------*/

void pblank(int x,int y)

{
   static unsigned char a[8] = {127,191,223,239,247,251,253,254};

   ((y % 2) == 0) ? (Evenrow[x][y >> 4] &= a[y % 16 >> 1]) :
                    (Oddrow[x][y >> 4] &= a[y % 16 >> 1]);

}  /* void pblank() */

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  void prntgr(int port)                                             */
/*                                                                    */
/*  Lists the contents of the bit map to an Epson FX-80, LX-90, ALPS  */
/*  P2000, and other IBM Graphics Printer compatibles using double    */
/*  density graphics mode.                                            */
/*                                                                    */
/*  port = printer port to be used, e.g. LPT1 = 0.                    */
/*                                                                    */
/*  The vertical high resolution is obtained by using the ESC 3 or    */
/*  'Select n/216-Inch Line Spacing' command.                         */
/*                                                                    */
/*  REFERENCES:  'ALPS P2000/P2100 Printer User's Manual,'            */
/*              Copyright (C) 1986 by ALPS Electric Co., Ltd.         */
/*                                                                    */
/*              'Printer Interface Cartridge for the IBM PC and       */
/*              and Compatibles Operation Manual,' (C) 1985 by Epson  */
/*              Corporation, Nagano, Japan.                           */
/*                                                                    */
/*              "High-Resolution Printer Graphics," by Mark Bridger   */
/*              and Mark Goresky, BYTE, November 1985, pp 219-232.    */
/*                                                                    */
/*  This routine will work only on an Epson LX or FX type printer, as */
/*  well as the ALPS P2000 printer, and other IBM Graphics Printer    */
/*  compatibles.  It should not be called unless a call has been made */
/*  to initgrafprt().                                                 */
/*                                                                    */
/*  biosprint() is used instead of fputc() to disable control-Z and   */
/*  other annoying characters that can screw up the printer stream.   */
/*                                                                    */
/*--------------------------------------------------------------------*/

void prntgr(int port)

{
   int n1,n2;
   int i,j;

/* n1 and n2 together give the number of graphics columns:            */

   n2 = MAXCOLS / 256;
   n1 = MAXCOLS % 256;

/* loop through all the even-row odd-row lines:                       */

   for (j=0; j<MAXPRINTROWS; j++)
     {
/*     initialize low-speed double density graphics for even line     */

       biosprint(0,27,port);
       biosprint(0,76,port);
       biosprint(0,n1,port);
       biosprint(0,n2,port);

       for (i = 0; i<MAXCOLS; i++)
         biosprint(0,(int)Evenrow[i][j],port);

       biosprint(0,13,port);                       /* carriage return */
       biosprint(0,27,port);                       /* set spacing to  */
       biosprint(0,51,port);                       /* 1/216 inch      */
       biosprint(0, 1,port);
       biosprint(0,10,port);                              /* linefeed */

/*     initialize low-speed double density graphics mode for odd line */

       biosprint(0,27,port);
       biosprint(0,76,port);
       biosprint(0,n1,port);
       biosprint(0,n2,port);

       for (i=0; i<MAXCOLS; i++)
         biosprint(0,(int)Oddrow[i][j],port);

       biosprint(0,13,port);                       /* carriage return */
       biosprint(0,27,port);                          /* line spacing */
       biosprint(0,51,port);                          /* at 22/216 in */
       biosprint(0,22,port);
       biosprint(0,10,port);                              /* linefeed */

     } /* j loop */

   biosprint(0,27,port);                          /* set line spacing */
   biosprint(0,50,port);                          /* to 1/6 inch      */

   biosprint(0,12,port);                                 /* form feed */

/* dispose of the allocated memory from the heap:                     */

   dismem();

}  /* void prntgr() */


/*--------------------------------------------------------------------*/
/*                                                                    */
/*  void far iprtln(int x1,int y1,int x2,int y2)                      */
/*                                                                    */
/*  Draws a line from integer device coordinates (x1,y1) to (x2,y2).  */
/*                                                                    */
/*  function colorset() must be called before calling iprtln()        */
/*                                                                    */
/*  translated to C by Greg Landheim from a Pascal implementation of  */
/*  Bresenham's line algorithm by Prof Richard Rasala of Northeastern */
/*  University Computer Science Dept.  Used with Rasala's permission. */
/*                                                                    */
/*--------------------------------------------------------------------*/

void far iprtln(int x1,int y1,int x2,int y2)

{
   int  z,a,b,dx,dy,d,deltap,deltaq;
   register int x,y;

   dx = abs(x2 - x1);
   dy = abs(y2 - y1);

   x = x1;
   y = y1;
   if (dy <= dx)
     {
       z = x2;
       a = (x1 <= x2) ? 1 : -1;
       b = (y1 <= y2) ? 1 : -1;

       deltap = dy << 1;
       d      = deltap - dx;
       deltaq = d - dx;
       (Color == 1) ? pset(x,y) : pblank(x,y);

       while (x != z)
         {
           x += a;
           (d < 0) ? (d += deltap) : (y += b,d += deltaq);
           (Color == 1) ? pset(x,y) : pblank(x,y);
         }
     }
   else
     {
       z = y2;
       a = (y1 <= y2) ? 1 : -1;
       b = (x1 <= x2) ? 1 : -1;

       deltap = dx << 1;
       d      = deltap - dy;
       deltaq = d - dy;
       (Color == 1) ? pset(x,y) : pblank(x,y);

       while (y != z)
         {
           y += a;
           (d < 0) ? (d += deltap) : (x += b,d += deltaq);
           (Color == 1) ? pset(x,y) : pblank(x,y);
         }
     }
}  /* void iprtln() */

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  void frame()                                                      */
/*                                                                    */
/*  Draws a box around the maximum plotting region. The maximum plot  */
/*  region is delimited by the global variables Maxwidth, and         */
/*  Maxheight.  These define the maximum available pixel domain of    */
/*  the plot device.                                                  */
/*                                                                    */
/*  initgrafprt() must be called before calling frame()               */
/*  colorset() must be called before calling frame()                  */
/*                                                                    */
/*--------------------------------------------------------------------*/

void frame()

{
   iprtln(0,0,0,Maxheight);
   iprtln(0,Maxheight,Maxwidth,Maxheight);
   iprtln(Maxwidth,Maxheight,Maxwidth,0);
   iprtln(Maxwidth,0,0,0);

 } /* void frame() */

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  void framxy()                                                     */
/*                                                                    */
/*  Draws a box around the real world plot region defined by a call   */
/*  to defreg()                                                       */
/*                                                                    */
/*  Both initgrafprt() and defreg() must be called before framxy()    */
/*  can be called.                                                    */
/*  colorset() must be called before calling framxy().                */
/*                                                                    */
/*--------------------------------------------------------------------*/

void framxy()

{
   prtlin(X_min,Y_min,X_min,Y_max);
   prtlin(X_min,Y_max,X_max,Y_max);
   prtlin(X_max,Y_max,X_max,Y_min);
   prtlin(X_max,Y_min,X_min,Y_min);

}  /* void framxy() */

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  void transf(float x,float y,int *ix,int *iy)                      */
/*                                                                    */
/*  Transforms real world (x,y) coordinates to integer world          */
/*  coordinates ix and iy.  the correspondence between integer and    */
/*  real worlds is defined by a call to defreg().                     */
/*                                                                    */
/*  x and y must lie within the valid region of the plot device after */
/*  transformation.  defreg() must be called before calling transf()  */
/*  for the first time.                                               */
/*                                                                    */
/*--------------------------------------------------------------------*/

void transf(float x,float y,int *ix,int *iy)

{
   *ix = IX_min + (int) (IDw * (x - X_min) / Dw);
   *iy = IY_min + (int) (IDh * (y - Y_min) / Dh);

}  /* void transf() */

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  void itransf(int *ix,int *iy)                                     */
/*                                                                    */
/*  Transforms integer world coordinates into plot device integer     */
/*  coordinates in either portrait or landscape modes.                */
/*                                                                    */
/*--------------------------------------------------------------------*/

void itransf(int *ix,int *iy)

{
   int temp;

   (Portrait) ? (*iy = Maxheight - *iy) :
                (temp = *ix, *ix = *iy, *iy = temp);

}  /* void itransf() */

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  void defreg(float x1,float x2,float y1,float y2,                  */
/*              int ix1,int ix2,int iy1,int iy2)                      */
/*                                                                    */
/*  Sets up a correspondence between real and integer worlds.         */
/*  Assigns values to the global constants which define the           */
/*  (equivalent) integer and real world plotting regions to be used.  */
/*                                                                    */
/*  integer world values must lie within the device valid domain.     */
/*                                                                    */
/*--------------------------------------------------------------------*/

void defreg(float x1,float x2,float y1,float y2,
            int ix1,int ix2,int iy1,int iy2)

{
   X_min = x1;                   /* minimum x-value of real world     */
   X_max = x2;                   /* maximum x-value of real world     */
   Y_min = y1;                   /* minimum y-value of real world     */
   Y_max = y2;                   /* maximum y-value of real world     */
  IX_min = ix1;                  /* minimum x-value of integer world  */
  IX_max = ix2;                  /* maximum x-value of integer world  */
  IY_min = iy1;                  /* minimum y-value of integer world  */
  IY_max = iy2;                  /* maximum y-value of integer world  */

   Dw = X_max - X_min;
   Dh = Y_max - Y_min;
  IDw = IX_max - IX_min;
  IDh = IY_max - IY_min;

}  /* void defreg() */

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  void prtlin(float x1,float y1,float x2,float y2)                  */
/*                                                                    */
/*  draws a line in the dot matrix printer memory map, using real     */
/*  world coordinates from (x1,y1) to (x2,y2).                        */
/*                                                                    */
/*  defreg() must be called before calling prtlin() for the first time*/
/*  colorset() must be called before calling prtlin()                 */
/*                                                                    */
/*--------------------------------------------------------------------*/

void prtlin(float x1,float y1,float x2,float y2)

{
   int ix1,iy1,ix2,iy2;

/* Change real world coordinates to integer world coordinates:        */

   transf( x1,y1, &ix1,&iy1);
   transf( x2,y2, &ix2,&iy2);

/* Change integer world coordinates to device coordinates:            */

   itransf(&ix1,&iy1);
   itransf(&ix2,&iy2);

/* Draw line in device coordinates:                                   */

   iprtln(ix1,iy1,ix2,iy2);

}  /* void prtlin() */

/*----------------------end of module grafprt-------------------------*/


