home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 June
/
SIMTEL_0692.cdr
/
msdos
/
microcrn
/
issue_45.arc
/
PR-GRAPH.ARC
/
PR-GRAPH.C
< prev
Wrap
Text File
|
1988-03-20
|
25KB
|
690 lines
/* PR-GRAPH.C - library of printer graphics functions
This supports an article in Micro Cornucopia Magazine Issue #45.
As always, the code is up for grabs. In the immortal words of Laine Stump,
"Permission to do whatever the hell you want with it." I do ask that if
you use this code to become rich beyond your wildest dreams, please send
me a six-pack of the best local brew in your neck of the woods.
You'll notice the structure (of type plot) is different from the one
printed in the mag. I originally wrote only the East version of the
graphing routines. When I started the others I realized that it made more
sense to define an axis in terms of its origin and axis lengths. Much
easier to rotate a given axis to different orientations with it defined
this way.
I also fiddled with the axis labeling in p_draw_scale with less than
wonderful results. If you choose axis lengths (x_length and y_length)
judiciously there's no problem. Play with different axis lengths for a
given plot and you'll see what I mean.
Needs DUMP.C (from Issue #44) for printer setup, etc. The function
setup_gr_line () in DUMP.C should be changed to accept (char p_mode,
int width) as parameters. I originally wrote it with 60 dpi mode hard
coded in.
Requires the font file PR_CHARS.DAT generated by CHARGEN.C.
Oh yeah, I used Turbo C v1.5 compact model.
Larry Fogg - Halloween, 1988
*/
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <math.h> /* only included for sin () example */
#include "dump.c" /* printer setup and Herc screen dump from Micro C #44 */
#define boolean char
#define yes 1
#define no 0
#define N 'N' /* "directions" on printer paper */
#define E 'E' /* N is the direction of normal text output */
#define W 'W'
#define S 'S'
#define herc_base 0xb000 /* Herc parameters */
#define v_max_row 347
#define v_max_col 719
#define ch_size 11 /* dot highth of a printer char */
typedef struct /* used for graph plotting routines */
{
char orientation; /* direction of X axis of graph */
int x_min; /* actual axis origin coordinates (x-east, y-north) */
int y_min;
int x_length; /* actual length of axis */
int y_length;
char x_label [50]; /* axis labels */
char y_label [50];
int xdata_min; /* data bounds */
int xdata_max;
int ydata_min;
int ydata_max;
int x_step; /* increments for labeling axis */
int y_step;
int points [2][100]; /* data in (x, y) pairs */
} plot;
char *p_buf; /* pointer to printer memory */
char p_chars [256][11]; /* holds 11 byte definition for each character */
int p_max_line; /* printer memory boundaries */
int p_max_col;
unsigned p_buf_size;
char p_mode; /* printer mode */
boolean p_init (char p_m, int max_line, int max_col)
{
p_buf_size = (max_col + 1) * (((max_line+1)/8) + 1); /* alloc print memory */
p_buf = malloc (p_buf_size);
if (p_buf != NULL) /* malloc () returns null if it fails */
{
p_max_line = max_line; /* set global vars */
p_max_col = max_col;
p_mode = p_m;
return (yes);
}
else
return (no); /* not enough memory for printer memory */
} /* p_init */
void p_load_chars () /* load the character font from file */
{ /* this file created by CHARGEN.C */
FILE *f;
f = fopen ("PR_CHARS.DAT", "rb");
fread (p_chars, 0xb00, 1, f);
} /* p_load_chars */
void p_clear_buf () /* clears printer memory */
{
unsigned i;
for (i=0; i<p_buf_size; i++)
*(p_buf+i) = 0;
} /* p_clear_buf */
void p_draw_point (int col, int line, boolean fill)
{
int i; /* offset within memory for byte containing the point */
char mask; /* locates point within the byte */
if ((col>=0) && (line>=0) && (col<=p_max_col) && (line<=p_max_line)) /* OK */
{
mask = 1 << (7 - (line % 8));
i = (line/8)*(p_max_col+1) + col;
if (fill) /* draw the point */
*(p_buf+i)= *(p_buf+i) | mask;
else /* erase the point */
*(p_buf+i) = *(p_buf+i) & ~mask;
}
else; /* do nothing - coordinates out of bounds */
} /* p_draw_point */
void p_big_point (int col, int line, boolean fill)
{ /* draws point and its nine neighbors */
int i, j;
for (i=col-1; i<=col+1; i++)
for (j=line-1; j<=line+1; j++)
p_draw_point (i, j, fill);
} /* p_big_point */
void p_draw_line (int col1, int line1, int col2, int line2, boolean on)
{ /* uses Bresenham algorithm to draw a line */
int dX, dY; /* vector components */
int row, col,
final, /* final row or column number */
G, /* used to test for new row or column */
inc1, /* G increment when row or column doesn't change */
inc2; /* G increment when row or column changes */
boolean pos_slope;
dX = line2 - line1; /* find vector components */
dY = col2 - col1;
pos_slope = (dX > 0); /* is slope positive? */
if (dY < 0)
pos_slope = !pos_slope;
if (abs (dX) > abs (dY)) /* shallow line case */
{
if (dX > 0) /* determine start point and last column */
{
col = line1;
row = col1;
final = line2;
}
else
{
col = line2;
row = col2;
final = line1;
}
inc1 = 2 * abs (dY); /* determine increments and initial G */
G = inc1 - abs (dX);
inc2 = 2 * (abs (dY) - abs (dX));
if (pos_slope)
while (col <= final) /* step through columns chacking for new row */
{
p_draw_point (row, col, on);
col++;
if (G >= 0) /* it's time to change rows */
{
row++; /* positive slope so increment through the rows */
G += inc2;
}
else /* stay at the same row */
G += inc1;
}
else
while (col <= final) /* step through columns checking for new row */
{
p_draw_point (row, col, on);
col++;
if (G > 0) /* it's time to change rows */
{
row--; /* negative slope so decrement through the rows */
G += inc2;
}
else /* stay at the same row */
G += inc1;
}
} /* if |dX| > |dY| */
else /* steep line case */
{
if (dY > 0) /* determine start point and last row */
{
col = line1;
row = col1;
final = col2;
}
else
{
col = line2;
row = col2;
final = col1;
}
inc1 = 2 * abs (dX); /* determine increments and initial G */
G = inc1 - abs (dY);
inc2 = 2 * (abs (dX) - abs (dY));
if (pos_slope)
while (row <= final) /* step through rows checking for new column */
{
p_draw_point (row, col, on);
row++;
if (G >= 0) /* it's time to change columns */
{
col++; /* positive slope so increment through the columns */
G += inc2;
}
else /* stay at the same column */
G += inc1;
}
else
while (row <= final) /* step throuth rows checking for new column */
{
p_draw_point (row, col, on);
row++;
if (G > 0) /* it's time to change columns */
{
col--; /* negative slope so decrement through the columns */
G += inc2;
}
else /* stay at the same column */
G += inc1;
}
}
} /* p_draw_line */
void p_put_c (unsigned char ch, int col, int line, char direction)
{
int r, c;
switch (direction)
{
case E: for (r=0; r<11; r++)
for (c=0; c<8; c++)
p_draw_point (col+r, line+c, p_chars [ch][r] & (1 << (7-c)));
break;
case W: for (r=0; r<11; r++)
for (c=0; c<8; c++)
p_draw_point (col-r, line-c, p_chars [ch][r] & (1 << (7-c)));
break;
case N: for (r=0; r<11; r++)
for (c=0; c<8; c++)
p_draw_point (col+c, line-r, p_chars [ch][r] & (1 << (7-c)));
break;
case S: for (r=0; r<11; r++)
for (c=0; c<8; c++)
p_draw_point (col-c, line+r, p_chars [ch][r] & (1 << (7-c)));
break;
} /* switch */
} /* p_put_c */
void p_put_s (unsigned char str_out [80], int col, int line, char direction)
{
int i;
i = 0;
switch (direction)
{
case E: while (str_out [i] != '\0')
{
p_put_c (str_out [i], col, line + 9*i, E);
i++;
}
break;
case W: while (str_out [i] != '\0')
{
p_put_c (str_out [i], col, line - 9*i, W);
i++;
}
break;
case N: while (str_out [i] != '\0')
{
p_put_c (str_out [i], col + 9*i, line, N);
i++;
}
break;
case S: while (str_out [i] != '\0')
{
p_put_c (str_out [i], col - 9*i, line, S);
i++;
}
break;
} /* switch */
} /* p_put_s */
void p_print_buf () /* print out contents of printer memory */
{
int col, line, total_lines;
total_lines = p_max_line/8 + 1; /* # passes for the printer head */
prn_setup ();
for (line=0; line<total_lines; line++)
{
setup_gr_line (p_mode, p_max_col+1);
for (col=0; col<=p_max_col; col++) /* send graphics line */
prn_out (*(p_buf + (unsigned)(line*(p_max_col+1) + col)));
prn_out (10); /* print it */
}
} /* p_print_buf */
void p_draw_border (int col1, int line1, int col2, int line2)
{
p_draw_line (col1, line1, col2, line1, yes);
p_draw_line (col2, line1, col2, line2, yes);
p_draw_line (col2, line2, col1, line2, yes);
p_draw_line (col1, line2, col1, line1, yes);
} /* p_draw_border */
/* This function reads Hercules graphics page 0 and stuffs it in the printer
memory. Position the video graphics within the printer page using the two
offset parameters. Note that a line_ofs of 1 is really 8 printer lines. I
didn't want to fool with "uneven" line offsets. Be sure there's room for
the screen - the function doesn't check. */
void p_get_screen (int col_ofs, int line_ofs)
{
unsigned buf_index; /* offset from base of printer memory */
int byte_ofs, /* offset from base of video memory */
vid_col, vid_row, blanks;
blanks = p_max_col - v_max_row; /* skip this much between video columns */
buf_index = line_ofs*(p_max_col + 1) + col_ofs; /*video starts filling here*/
for (vid_col=0; vid_col<v_max_col; vid_col+=8, buf_index+=blanks)
for (vid_row=v_max_row; vid_row>=0; vid_row--, buf_index++)
{
byte_ofs = 0x2000*(vid_row % 4) + 90*(vid_row/4) + (vid_col/8);
*(p_buf + buf_index) = peekb (herc_base, byte_ofs); /*video to printer*/
}
} /* p_get_screen */
void p_draw_axis (plot *plt) /* draw axis and print text labels */
{
switch (plt->orientation)
{
case E: p_draw_line (plt->y_min, plt->x_min,
plt->y_min, plt->x_min + plt->x_length, yes);
p_draw_line (plt->y_min, plt->x_min,
plt->y_min + plt->y_length, plt->x_min, yes);
p_put_s (plt->x_label, plt->y_min-3*ch_size, plt->x_min+15, E);
p_put_s (plt->y_label, plt->y_min+15, plt->x_min-2*ch_size, N);
break;
case W: p_draw_line (plt->y_min, plt->x_min,
plt->y_min, plt->x_min - plt->x_length, yes);
p_draw_line (plt->y_min, plt->x_min,
plt->y_min - plt->y_length, plt->x_min, yes);
p_put_s (plt->x_label, plt->y_min+3*ch_size, plt->x_min-15, W);
p_put_s (plt->y_label, plt->y_min-15, plt->x_min+2*ch_size, S);
break;
case N: p_draw_line (plt->y_min, plt->x_min,
plt->y_min, plt->x_min - plt->x_length, yes);
p_draw_line (plt->y_min, plt->x_min,
plt->y_min + plt->y_length, plt->x_min, yes);
p_put_s (plt->x_label, plt->y_min+15, plt->x_min+3*ch_size, N);
p_put_s (plt->y_label, plt->y_min-2*ch_size, plt->x_min-15, W);
break;
case S: p_draw_line (plt->y_min, plt->x_min,
plt->y_min, plt->x_min + plt->x_length, yes);
p_draw_line (plt->y_min, plt->x_min,
plt->y_min - plt->y_length, plt->x_min, yes);
p_put_s (plt->x_label, plt->y_min-15, plt->x_min-3*ch_size, S);
p_put_s (plt->y_label, plt->y_min+2*ch_size, plt->x_min+15, E);
break;
} /* switch */
} /* p_draw_axis */
void p_draw_scale (plot *plt)
{
int x, y, /* current drawing location (x-east, y-north) */
x_label, y_label; /* incremental axis labels */
float xdata_span, ydata_span, /* data range (float forces real division) */
dx, dy; /* axis increments */
char temp [6]; /* x_label and y_label in string form */
xdata_span = plt->xdata_max - plt->xdata_min; /* find data range */
ydata_span = plt->ydata_max - plt->ydata_min;
dx = (plt->x_step/xdata_span) * plt->x_length + 0.5; /*find axis increments*/
dy = (plt->y_step/ydata_span) * plt->y_length + 0.5;
switch (plt->orientation)
{
case E: x = plt->x_min; /* initial values */
x_label = plt->xdata_min;
while (x <= plt->x_min + plt->x_length)
{
p_draw_point (plt->y_min - 1, x, yes); /* draw "tick" on axis */
p_draw_point (plt->y_min - 2, x, yes);
itoa (x_label, temp, 10); /* convert tick label to string */
p_put_s (temp, plt->y_min - 16, x - 4, E); /* label the tick */
x_label += plt->x_step; /* set up for next tick */
x += dx;
}
y = plt->y_min; /* same for y-axis */
y_label = plt->ydata_min;
while (y <= plt->y_min + plt->y_length)
{
p_draw_point (y, plt->x_min - 1, yes);
p_draw_point (y, plt->x_min - 2, yes);
itoa (y_label, temp, 10);
p_put_s (temp, y - 4, plt->x_min - 5, N);
y_label += plt->y_step;
y += dy;
}
break;
case W: x = plt->x_min; /* initial values */
x_label = plt->xdata_min;
while (x >= plt->x_min - plt->x_length)
{
p_draw_point (plt->y_min + 1, x, yes); /* draw "tick" on axis */
p_draw_point (plt->y_min + 2, x, yes);
itoa (x_label, temp, 10); /* convert tick label to string */
p_put_s (temp, plt->y_min + 16, x + 4, W); /* label the tick */
x_label += plt->x_step; /* set up for next tick */
x -= dx;
}
y = plt->y_min; /* same for y-axis */
y_label = plt->ydata_min;
while (y >= plt->y_min - plt->y_length)
{
p_draw_point (y, plt->x_min + 1, yes);
p_draw_point (y, plt->x_min + 2, yes);
itoa (y_label, temp, 10);
p_put_s (temp, y + 4, plt->x_min + 5, S);
y_label += plt->y_step;
y -= dy;
}
break;
case N: y = plt->y_min; /* initial values */
x_label = plt->xdata_min;
while (y <= plt->y_min + plt->x_length)
{
p_draw_point (y, plt->x_min + 1, yes); /* draw "tick" on axis */
p_draw_point (y, plt->x_min + 2, yes);
itoa (x_label, temp, 10); /* convert tick label to string */
p_put_s (temp, y - 4, plt->x_min + 16, N); /* label the tick */
x_label += plt->x_step; /* set up for next tick */
y += dx;
}
x = plt->x_min; /* same for y-axis */
y_label = plt->ydata_min;
while (x >= plt->x_min - plt->y_length)
{
p_draw_point (plt->y_min - 1, x, yes);
p_draw_point (plt->y_min - 2, x, yes);
itoa (y_label, temp, 10);
p_put_s (temp, plt->y_min - 5, x + 4, W);
y_label += plt->y_step;
x -= dy;
}
break;
case S: y = plt->y_min; /* initial values */
x_label = plt->xdata_min;
while (y >= plt->y_min - plt->x_length)
{
p_draw_point (y, plt->x_min - 1, yes); /* draw "tick" on axis */
p_draw_point (y, plt->x_min - 2, yes);
itoa (x_label, temp, 10); /* convert tick label to string */
p_put_s (temp, y + 4, plt->x_min - 16, S); /* label the tick */
x_label += plt->x_step; /* set up for next tick */
y -= dx;
}
x = plt->x_min; /* same for y-axis */
y_label = plt->ydata_min;
while (x <= plt->x_min + plt->y_length)
{
p_draw_point (plt->y_min + 1, x, yes);
p_draw_point (plt->y_min + 2, x, yes);
itoa (y_label, temp, 10);
p_put_s (temp, plt->y_min + 5, x - 4, E);
y_label += plt->y_step;
x += dy;
}
break;
} /* switch */
} /* p_draw_scale */
void p_draw_data (plot *plt)
{
int i;
float xdata_span, ydata_span; /* data range (float forces real division) */
xdata_span = plt->xdata_max - plt->xdata_min; /* find data range */
ydata_span = plt->ydata_max - plt->ydata_min;
switch (plt->orientation)
{
case E: for (i=1; i<=plt->points [0][0]; i++) /* 0,0 has # of points */
p_big_point (plt->y_min +
(plt->points [1][i]/ydata_span)*plt->y_length + 0.5,
plt->x_min +
(plt->points [0][i]/xdata_span)*plt->x_length + 0.5,
yes);
break;
case W: for (i=1; i<=plt->points [0][0]; i++) /* 0,0 has # of points */
p_big_point (plt->y_min -
(plt->points [1][i]/ydata_span)*plt->y_length + 0.5,
plt->x_min -
(plt->points [0][i]/xdata_span)*plt->x_length + 0.5,
yes);
break;
case N: for (i=1; i<=plt->points [0][0]; i++) /* 0,0 has # of points */
p_big_point (plt->y_min +
(plt->points [1][i]/ydata_span)*plt->y_length + 0.5,
plt->x_min -
(plt->points [0][i]/xdata_span)*plt->x_length + 0.5,
yes);
break;
case S: for (i=1; i<=plt->points [0][0]; i++) /* 0,0 has # of points */
p_big_point (plt->y_min -
(plt->points [1][i]/ydata_span)*plt->y_length + 0.5,
plt->x_min +
(plt->points [0][i]/xdata_span)*plt->x_length + 0.5,
yes);
break;
} /* switch */
} /* p_draw_data */
void p_draw_plot (plot *plt)
{
p_draw_axis (plt);
p_draw_scale (plt);
p_draw_data (plt);
} /* p_draw_plt */
void graph_test () /* example of graph use */
{ /* use 705 x 470 in p_init call in main */
int i;
plot *p;
p = (plot *) malloc (sizeof (plot)); /* get memory for structure */
p->orientation = E; /* load structure values */
p->x_min = 35; p->x_length = p_max_line - p->x_min - 25;
p->y_min = 35; p->y_length = p_max_col - p->y_min - 25;
strcpy (p->x_label, "Sample PR-GRAPH Output - (X)");
strcpy (p->y_label, "150+100*sin(X/4)");
p->xdata_min = 0; p->xdata_max = 100;
p->ydata_min = 0; p->ydata_max = 300;
p->x_step = 20;
p->y_step = 100;
p->points [0][0] = 99; /* element 0,0 holds # of data points */
for (i=1; i<=99; i++) /* load data array */
{
p->points [0][i] = i; /* x value */
p->points [1][i] = 150 + 100*sin(i/4.0); /* y value */
}
p_draw_plot (p); /* do the graph */
p_draw_border (0, 0, p_max_col, p_max_line);
p_print_buf (); /* print it */
} /* graph_test */
void graph_test4 () /* example of four graph orientations */
{ /* use 450 x 450 in p_init call in main */
int i;
plot *p;
p = (plot *) malloc (sizeof (plot)); /* get memory for structure */
p->orientation = E; /* load structure values for E plot */
p->x_min = 275; p->x_length = 150;
p->y_min = 275; p->y_length = 150;
strcpy (p->x_label, "EAST X-axis");
strcpy (p->y_label, "EAST Y-axis");
p->xdata_min = 0; p->xdata_max = 10;
p->ydata_min = 0; p->ydata_max = 100;
p->x_step = 2;
p->y_step = 25;
p->points [0][0] = 10; /* element 0,0 holds # of data points */
for (i=1; i<=10; i++) /* load data array */
{
p->points [0][i] = i; /* x value */
p->points [1][i] = 10*i; /* y value */
}
p_draw_plot (p); /* do the graph */
p->orientation = W; /* load new W values */
p->x_min = 175;
p->y_min = 175;
strcpy (p->x_label, "WEST X-axis");
strcpy (p->y_label, "WEST Y-axis");
p_draw_plot (p); /* do the graph */
p->orientation = N; /* load new N values */
p->x_min = 175;
p->y_min = 275;
strcpy (p->x_label, "NORTH X-axis");
strcpy (p->y_label, "NORTH Y-axis");
p_draw_plot (p); /* do the graph */
p->orientation = S; /* load new S values */
p->x_min = 275;
p->y_min = 175;
strcpy (p->x_label, "SOUTH X-axis");
strcpy (p->y_label, "SOUTH Y-axis");
p_draw_plot (p); /* do the graph */
p_draw_border (0, 0, p_max_col, p_max_line);
p_print_buf (); /* print it */
} /* graph_test4 */
main ()
{
if (p_init (75, 450, 450))
{
p_clear_buf ();
p_load_chars ();
/* graph_test ();*/
graph_test4 ();
}
else
printf ("\n\n Not enough memory ...\n\n");
free (p_buf); /* retreive printer memory */
}