home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-385-Vol-1of3.iso
/
d
/
dots151.zip
/
GRAPHSRC.ZIP
/
GRAPH.C
< prev
next >
Wrap
C/C++ Source or Header
|
1990-07-05
|
39KB
|
1,267 lines
/*
#define MOUSE
graph - graph a function of a single variable
history...
5 Jul 90 Ver 3.21: Script files may have comments.
4 Jul 90 Ver 3.20: Added support for device driver configuration
files.
16 Jun 90 Ver 3.19: Accepting two script file reference syntaxes.
If a log scale switch causes a labeled point to be
discarded, the label is moved to the last retained
point.
14 Jun 90 Ver 3.18: Fetching options from environment variable
GRAPH.
19 Apr 90 Ver 3.17: displaying up to 16380 data points (or
as limited by user memory).
6 Mar 90 Ver 3.16: allowing axes separated from graph area.
27 Feb 90 Ver 3.15: no longer attempts to restore video mode
if graphics mode was never set. Fixed occasional
divide-by-zero bug for screens with subsquare
pixels (pixel width > pixel height, like the VGA).
19 Feb 90 Ver 3.14: No more text mode error messages if a memory
allocation fails while in graphics mode. Coherent
error message if script file cannot be opened.
Default script file name is constructed from data file
name. Menu starts at top right.
16 Sep 89 Ver 3.13: Switching to log scales no longer temporarily
drops a linestyle. "abscissa" menu option can now
discard y values.
15 Sep 89 Ver 3.12: Adding styles interactively automatically
enables breaking, keystrokes entered during a redraw
are not echoed through DOS when they are discarded.
13 Aug 89 Ver 3.11: Automatically adjusting width to be consistent
with right move, etc.
1 Jul 89 Ver 3.10: graphics mode menus, mouse handler.
16 May 89 Cleaned up nonportable constructions.
4 May 89 Ver 3.09: user can adjust grid width & number of tic
marks interactively. Markers don't have corner glitches.
25 Apr 89 Ver 3.08: not adjusting # tic marks for label size
if no numeric labels are requested.
24 Apr 89 using intensity if color isn't available.
8 Apr 89 Ver 3.07: name of input script file becomes default for
output script file
6 Apr 89 Ver 3.06: optionally writes data file names to script file
5 Apr 89 Ver 3.05: writes -c and -n switches to output script file
The part of argv[] referring to data files is preserved.
22 Mar 89 Ver 3.04: requests correct number of tic marks in
script file.
27 Feb 89 Ver 3.03: quits gracefully if there are no data points
23 Nov 88 Ver 3.02: Minor corrections for line style and
axis limit menus.
23 Sep 88 Ver 3.01: Interactive update of label, abscissa
start & step. Linestyles can be deleted or
inserted. X and y menus accept only one input.
For "plotting devices", bottom right hand numerical
label is centered on the tic mark.
5 Aug 88 Ver 3.00: Allowing interactive update of switches
via menubars.
1 Aug 88 Ver 2.49: Allowing 100 styles rather than 30.
Style of FF suppresses display entirely.
4 May 88 Ver 2.48: Allows axis labels as high as 10000 before
switching to scientific notation.
23 Mar 88 Ver 2.47: Warns about points dropped after memory is
full.
4 Mar 88 Ver 2.46: Can display bottom and left axes only.
10 Feb 88 Ver 2.45: Restarts automatic abscissas with each
new curve.
23 Jan 88 Ver 2.44: Reads parameters from script file.
3 Dec 87 Ver 2.43: Fixed linestyle so symbols work again.
25 Nov 87 Ver 2.42: The linestyle is interpreted as a hex
number, so the color digit can range from 0 (white)
through f. Plot area is shrunk if necessary so
wide grid stays within specified width and height.
29 Oct 87 Ver 2.41: Marker color controlled only by the
second digit of the style number.
25 Aug 87 Ver 2.40: Large number of labels permitted, via
dynamic reallocation (shrinking) of data arrays.
Saving repeat counts.
20 Aug 87 Ver 2.39: Tic marks can be outside graph area. # small
tic marks is adjustable.
17 Aug 87 Ver 2.38: width of grid lines can be specified.
9 Aug 87 Ver 2.37: Points can be labeled even if tabs rather than
spaces are used for separators. Null byte in input file
signifies break in plot (end of curve).
6 Aug 87 Ver 2.36: fixed off-by-one error in label positioning:
graph is once again broken correctly if data points
come from separate files
29 Jul 87 Ver 2.35: line styles may have repeat counts.
e option gives equal vertical and horizontal scales.
numeric requires presence of at least one numeral.
21 Jul 87 Ver 2.34: #labels and #tics requested are adjusted
according to the aspect ratio of the display used.
28 Jun 87 Ver 2.33: Any number of labels
31 May 87 Ver 2.32: Corrected vertical positioning of labels.
26 May 87 Ver 2.31: specifying more than one linestyle
automatically turns "breaking" on, styles from
multiple -m options accumulate.
21 May 87 Ver 2.30: plotting magnitude or phase.
7 May 87 Ver 2.21: Reading into doubles, checking for overflow
before converting to floats. This allows reading very
small data values.
6 Apr 87 Ver 2.20: Options may be intermixed with files.
Multiple files accepted.
5 Apr 87 Log axes labeled with superscripts.
11 Sep 86 Last specified line style is the default.
5 Sep 86 Ver 2.10: Storing points as floats rather than longs,
accepting 5000 points.
28 Jun 86 Ver 2.01: Accepting 3000 points rather than 1000.
4 Jun 86 If input comes from stdin, closing and reopening it to
allow for keyboard input.
3 May 86 Calculating approx. label width before reducing # labels.
14 Apr 86 Ver 2.0: Reducing # labels for narrow plots.
13 Apr 86 Ver 1.9: Specifying minimum and maximum works for
log scales.
Allowing 9 characters on left for labels.
12 Apr 86 Ver 1.8: Scaling axis labels by factors of 1000.
Allowing 8 characters on left for labels.
Placing text label above graph.
Label starting with '"' can contain spaces.
Scientific notation allowed in switches.
6 Apr 86 Tic marks on sides and top are same length, even for
narrow or squat plot windows. Ignoring blank lines.
9 Aug 85 Using box marker rather than plus, setting viewport based
actual character width.
14 Nov 85 Enlarged array sizes and stopped reading when max label
count exceeded.
14 Nov 85 Writing text label after drawing axes.
17 Nov 85 Drawing axes before figure.
23 Nov 85 Ignoring data lines beginning with ';'.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <dos.h>
#include "g.h"
#include "g3.h"
#include "graph.h"
#define RIGHT1 "Copyright (c) 1985 - 1990 James R. Van Zandt (jrv@mbunix.mitre.org)\n"
#define RIGHT2 "Resale forbidden, copying encouraged, comments welcome."
#define VERSION "3.21"
#define amax1(a,b) (((a)>(b))?(a):(b))
#ifdef __TURBOC__
#define key_avail() kbhit()
#include <string.h>
#include <ctype.h>
#else
#define key_avail() _os(0xb,0)
#define strchr(a,b) index(a,b)
#endif
#define BIGFLOAT 6.e38 /* no floats larger than this */
#define ENTRIES 5000
#define MAXSTYLES 100
#define BUFSIZE 200
char buf[BUFSIZE];
/* local functions */
void to_text( void );
static image( );
static range_check( double x );
static read_data( int *argcp, char ***argvp );
static initialize( );
static display_data( );
static int handle_break( );
float *x, *y;
double xmin, xmax, xdel = 0., ymin, ymax, ydel = 0.;
double
height_used, /* fraction of vertical space used */
width_used, /* fraction of horizontal space used */
right_move, /* fraction of space to move right before plotting */
up_move, /* fraction of space to move up before plotting */
requested_height_used=1., /* initial values for above */
requested_width_used=1.,
requested_right_move=0.,
requested_up_move=0.,
abscissa, /* current value for automatic abscissas */
abscissa_start=0., /* default starting value for automatic abscissas */
abscissa_step=1.; /* default step for automatic abscissas */
extern int debugging;
int markers=0;
int equal=0; /* nonzero if vertical and horizontal scales
must be equal */
int text_labeling=0;
int numeric_labeling=1;
int out_of_memory=0; /* set nonzero when malloc fails */
int grid_width=1; /* width of grid lines */
int grid_style=1; /* 0 for no grid, 1 for frame with tics,
2 for full grid, 3 for left and lower
sides only, negative for tic marks on
outside. */
int breaking=0; /* nonzero if breaking (disconnecting) graph
after each label in input */
int default_labeling=0; /* nonzero if default point label was given */
int transposing=0; /* nonzero if transposing x & y */
int logx=0; /* nonzero if x axis is to be logrithmic */
int logy=0; /* nonzero if y axis is to be logrithmic */
int standard_input=0; /* nonzero if data is coming from standard input */
int x_arguments=0; /* number specified: xmin, xmax, xdel */
int y_arguments=0; /* number specified: ymin, ymax, ydel */
int automatic_abscissas=0; /* nonzero if abscissas are to be generated */
int magnitude=0; /* nonzero if displaying magnitude of complex # */
unsigned max_points=ENTRIES; /* # x or y points */
int phase=0; /* nonzero if displaying phase of complex # */
int dividing_by_pi=0; /* nonzero if dividing phase by pi */
int abscissa_arguments=0; /* number specified: abscissa spacing & start */
int last=0; /* number of entries in x and y */
int rlx; /* number of x axis labels */
int rly; /* number of y axis labels */
int rtx; /* number of x axis tics */
int rty; /* number of y axis tics */
int requested_rlx=6; /* initial values for above */
int requested_rly=6;
int requested_rtx=30;
int requested_rty=30;
int numstyles=0; /* # linestyles specified by user
(index into next two arrays) */
int style[MAXSTYLES]; /* array of requested line styles */
int repeat[MAXSTYLES]; /* array of repeat counts for line styles */
int labels=0; /* number of user-supplied labels */
int graphics_mode = 0; /* zero until the switch to graphics mode */
FILE *ifile, *fopen();
char *default_script_file = "";
char null_label[] = "";
char *default_label = "";
char *text_label = "";
char *program_name = NULL;
/* allocate block from heap, shrinking x & y if necessary */
void *gmem(n) int n;
{ char *tmp;
void *malloc(), *realloc();
int size;
tmp = malloc(n);
if(tmp != NULL) return tmp;
size = 30+n/sizeof(double);
if(last < max_points-size-10)
{max_points -= size; /* free up almost 2*(30*8+n) = 480+2*n bytes */
/* if memory management is working correctly, these should all succeed */
x = (float *)realloc(x, max_points*sizeof(float));
y = (float *)realloc(y, max_points*sizeof(float));
tmp = malloc(n);
if(x != NULL && y != NULL && tmp != NULL) return tmp;
}
out_of_memory = 1;
return NULL;
}
/* allocate a block, otherwise stop program */
void *getmem(n) int n;
{ char *tmp;
tmp = gmem(n);
if(tmp == NULL)
{to_text();
fprintf(stderr, "\007out of memory");
exit(1);
}
return tmp;
}
typedef struct _blk
{struct _blk *nxt;
int num;
char txt[1];
} BLOCK;
BLOCK *top_label, *last_label;
BLOCK *new_label(n, s) int n; char *s;
{ BLOCK *tmp;
tmp = (BLOCK *)gmem(sizeof(BLOCK)+strlen(s));
if(tmp)
{tmp->nxt = (BLOCK *)0;
tmp->num = n;
strcpy(tmp->txt, s);
}
return tmp;
}
void to_text( void )
{ if(graphics_mode) finish_graphics();
graphics_mode = 0;
}
static int handle_break()
{ /* control comes here if user types ^C */
/* scr_term(); */
terminate_view_surface(1);
exit(0);
}
#ifdef __DESMET__
uncook(handle) unsigned handle;
{ /* select raw input mode for keyboard input */
unsigned extern _rax, _rbx, _rcx, _rdx;
_rax = 0x4400; /* I/O control read */
_rbx = handle;
_doint(0x21);
_rdx &= 0x00ff;
_rdx |= 0x0020;
_rax = 0x4401; /* I/O control write */
_rbx = handle;
_doint(0x21);
}
forcedup(unused, existing) unsigned unused, existing;
{
unsigned extern _rax, _rbx, _rcx, _rdx, _carryf;
_rax = 0x4600;
_rbx = existing;
_rcx = unused;
_doint(0x21);
return _carryf; /* return nonzero on error */
}
#endif
main(argc,argv) int argc; char **argv;
{ int c;
double cur_xmin, cur_xmax, cur_ymin, cur_ymax;
printf("GRAPH version %s, interface version %s for %s \n",
VERSION,interface_version,machine);
initialize(); /* allocate storage for data points */
init_env(); /* get initial options from environment variable GRAPH */
read_data(&argc,&argv);
if(last == -1) {fprintf(stderr, "no data values"); exit(1);}
/* init_graphics(); /* this step switches to graphics mode */
initialize_core(0,0,2); /* only 2D functions will be required */
initialize_view_surface(1);
graphics_mode = 1;
init_menus();
ctrlbrk(handle_break); /* register the control-break handler */
atexit(to_text); /* register an exit function */
while(1)
{cur_xmin = xmin;
cur_xmax = xmax;
cur_ymin = ymin;
cur_ymax = ymax;
display_data();
xmin = cur_xmin;
xmax = cur_xmax;
ymin = cur_ymin;
ymax = cur_ymax;
if(plotting_device) break;
if(standard_input) c = getc(ifile);
else
{
while(key_avail()) {scr_ci();} /* clear input queue */
#ifdef MOUSE
c = GetEvent();
#else
c = scr_ci();
#endif
}
if(c != '/') break;
adjust_parameters(&argc, &argv);
}
terminate_view_surface(1); exit(0);
}
display_data()
{ double lower, upper, delt, span, broadened, aspect, aspect2, adj, del;
int nlab, ntic, i, j, lw, max;
rlx = requested_rlx;
rly = requested_rly;
rtx = requested_rtx;
rty = requested_rty;
height_used = requested_height_used;
width_used = requested_width_used;
right_move = requested_right_move;
up_move = requested_up_move;
/*printf("xmin=%f, xmax=%f, ymin=%f, ymax=%f\n", xmin, xmax, ymin, ymax);*/
ndc_space_2(best_width,best_height); /* use all the display surface */
if(text_labeling) height_used -= 1.5/(pixels_high/char_height);
if(numeric_labeling) /* allow space for numeric labels */
{adj = 9./(pixels_wide/char_width);
right_move += adj;
width_used -= adj;
adj = 1.7/(pixels_high/char_height);
up_move += adj;
height_used -= adj;
}
adj = 0.;
del = .02*sqrt(width_used*width_used + height_used*height_used);
if(grid_style<0) /* allow space for tic marks outside graph */
adj = 1.2*del;
if(abs(grid_style) > 3) /* allow space for offset axes */
adj += 1.5*del;
right_move += adj; up_move += adj;
adj *= 2.;
width_used -= adj; height_used -= adj;
if(grid_width>1) /* allow space for wide grid lines */
{if(numeric_labeling)
{width_used -= (grid_width+2)/(2.*pixels_wide);
height_used -= (grid_width+2)/(2.*pixels_high);
}
else
{adj = (grid_width+2)/(double)(pixels_wide);
width_used -= adj; right_move += adj/2.;
adj = (grid_width+2)/(double)(pixels_high);
height_used -= adj; up_move += adj/2.;
}
}
if(equal) /* adjust display width or height for approx. equal scales */
{aspect = (xmax-xmin)/(width_used*best_width)
/((ymax-ymin)/(height_used*best_height));
if(aspect<1.) /* graph would be too short */
width_used *= aspect;
else /* graph would be too tall */
height_used /= aspect;
}
/* calculate square root of width to height ratio */
broadened = sqrt(width_used/height_used*best_width/best_height);
/* adjust # tic marks and labels for graph aspect ratio */
rlx *= broadened;
rtx *= broadened;
rly /= broadened;
rty /= broadened;
/* adjust # tic marks and labels to eliminate label overlap */
if(numeric_labeling)
{
if(logx) lw = 3; /* calculate lw = maximum label width */
else
{lw = 4;
if(xmin<0.) lw++; /* sign */
if(xmax-xmin<.01) lw += 3; /* negative exponent */
else if (xmax-xmin>1000.) lw += 2; /* positive exponent */
}
max = width_used*pixels_wide/char_width;
if(max <= lw*rlx) rlx = max/lw;
max = height_used*pixels_high/char_height;
if(max <= rly) rly = max;
}
height_used *= best_height; up_move *= best_height;
width_used *= best_width; right_move *= best_width;
viewport2(right_move, right_move+width_used, up_move, up_move+height_used);
clip_window(1);
/* printf("requesting x=%f to %f, y=%f to %f \n",xmin,xmax,ymin,ymax);
getchar();
*/
scale(xmin, xmax, rlx, rtx, logx, ymin, ymax, rly, rty, logy);
inquire_window(&xmin, &xmax, &ymin, &ymax);
if(equal) /* adjust width and/or height to compensate for
changes in window made by scale */
{aspect2 = (xmax-xmin)/width_used
/((ymax-ymin)/height_used);
if(aspect*aspect2<aspect ^ aspect*aspect2>aspect2)
{ /* both changes in same directions - shrink by aspect2 */
if(aspect2<1.) /* graph would be too short */
width_used *= aspect2;
else /* graph would be too tall */
height_used /= aspect2;
}
else if(fabs(log(aspect2))<fabs(log(aspect)))
{ /* 2nd adjustment is smaller - enlarge graph by aspect2 */
if(aspect2<1.) height_used /= aspect2;
else width_used *= aspect2;
}
else
{ /* first, enlarge graph by 1/aspect */
if(aspect<1.) width_used /= aspect;
else height_used *= aspect;
/* now, shrink by aspect*aspect2 */
aspect2 *= aspect;
if(aspect2<1.) width_used *= aspect2;
else height_used /= aspect2;
}
viewport2(right_move,right_move+width_used,up_move,up_move+height_used);
}
/* printf("plotting x=%f to %f, y=%f to %f \n",xmin,xmax,ymin,ymax);
getchar();
*/
create_temporary_segment();
set_color_or_intensity(0);
/*
move_abs_2(.9*xmin+.1*xmax,1.02*ymax-.02*ymin);
text("temporary segment created\n");
*/
axis(numeric_labeling,grid_style, grid_width, width_used, height_used);
/*
move_abs_2(.9*xmin+.1*xmax,1.02*ymax-.02*ymin);
text("axes displayed \n");
*/
image(); /* generate the figure */
if(text_labeling)
{clip_window(0);
move_abs_2(.9*xmin+.1*xmax,1.02*ymax-.02*ymin);
text(text_label);
clip_window(1);
}
close_temporary_segment();
}
/*
Parse arguments. Switches and their arguments are removed from the
list of parameters. Other parameters (which should be file names)
are left undisturbed. Note the function arguments are pointers to
the normal argc and argv, so this routine may alter them. In
particular, argv may be altered to point to a different string of
arguments (as a result of a -f switch)
If there's an error...
In text mode (i.e. it's interpreting command line arguments) or
if it can't allocate memory, prints an error message and stops the
program.
In graphics mode (i.e. it's been called by script_file in
gmouse), returns nonzero and leaves an error message in buf.
script_file can then print the error message and continue.
*/
parse_args(argcp, argvp) int *argcp; char ***argvp;
{
int i,
argc, /* # parameters passed over (file names) */
nac,
ac; /* # parameters remaining to be scanned */
char **argv, /* pointers to parameters passed over */
**nav,
**av, /* pointers to parameters remaining to be scanned */
*sname, /* name of script file */
*s; /* points to a token */
FILE *sfile; /* script file */
#define strdup(s) strcpy(getmem(1 + strlen(s)), s)
ac = *argcp; av = *argvp; argc = 0;
while(ac>0)
{if(**av == '@' || streq(*av, "-f"))
{if(**av == '-') /* older syntax: -f foo */
{if(ac < 2) gripe_arg(*av);
sname = av[1];
av++; ac--;
}
else sname = *av + 1; /* newer syntax: @foo */
nav = av + 1; /* save pointer to remaining parameters... */
nac = ac - 1; /* ...and their count */
sfile = fopen(sname, "r");
if(!sfile)
{sprintf(buf,"cannot open script file %s", sname);
if(graphics_mode) return 1; /* signal the error */
fprintf(stderr, buf); /* ...or notify user & quit */
exit(1);
}
/* save file name for script_file() */
default_script_file = strdup(sname);
/*
{int i;
FILE *dfile;
dfile = fopen("debug", "a");
fprintf(dfile, "%d+%d orig parameters: ", argc, ac);
for (i = 0; i < argc; i++) fprintf(dfile, "%s ", (*argvp)[i]);
for (i = 0; i < nac; i++) fprintf(dfile, "%s ", nav[i]);
fprintf(dfile, "\n");
fclose(dfile);
}
*/
ac = 0;
strtok("", " "); /* force initial read from file */
while(token(sfile) != NULL) ac++; /* count tokens in script file */
av = (char **)getmem((argc+ac+nac)*sizeof(char *));
for (i = 0; i<argc; i++) av[i] = (*argvp)[i]; /* copy file names */
/*
{int i;
FILE *dfile;
dfile = fopen("debug", "a");
fprintf(dfile, " passed file names: ");
for (i = 0; i < argc; i++) fprintf(dfile, "%s ", av[i]);
fprintf(dfile, "\n");
fprintf(dfile, " script file: %s, with %d parameters\n", sname, ac);
fclose(dfile);
}
*/
*argvp = av;
av += argc; ac = 0;
rewind(sfile);
strtok("", " "); /* force initial read from file */
while((s = token(sfile)) != NULL) /* copy token from file */
{av[ac] = getmem(1+strlen(s));
strcpy(av[ac++], s);
}
fclose(sfile);
while(nac--) av[ac++] = *nav++; /* copy remaining tokens */
/*
{int i;
FILE *dfile;
dfile = fopen("debug", "a");
fprintf(dfile, "parameters afterwards: ");
for (i = 0; i < ac + argc; i++) fprintf(dfile, "%s ", (*argvp)[i]);
fprintf(dfile, "\n-----------------------\n");
fclose(dfile);
}
*/
}
else if(**av == '&') /* device driver configuration file */
{config_file = (*av)+1;
ac--; av++;
}
else if(**av == '-') /* normal switch */
{i = get_parameter(ac, av);
ac -= i; av += i;
}
else {(*argvp)[argc++] = *av++; ac--;} /* data file */
}
*argcp = argc;
return 0; /* no error */
}
initialize()
{
#ifdef __DESMET__
double xx;
sscanf("1.9","%lf",&xx); /* workaround for bug in strtod() */
#else
long data_bytes, farcoreleft();
data_bytes = (farcoreleft() - 10000)/2; /* allow for labels, etc. */
if(data_bytes > 65536L - 16) /* each array must be <64K */
data_bytes = 65536L - 16; /* (allow for 16 byte overhead) */
max_points = data_bytes/sizeof(float);
#endif
x = (float *)malloc(max_points*sizeof(float));
y = (float *)malloc(max_points*sizeof(float));
if(x == NULL || y == NULL) {puts("out of memory"); exit(1);}
top_label = new_label(-1, "");
}
#define TR(x) /* printf x; scr_ci() /**/
read_data(argcp, argvp) int *argcp; char ***argvp;
{ int argno, i, j, ac, expected, argc;
double xx, yy, zz, d;
float *pd;
double actual_xmin, actual_xmax, actual_ymin, actual_ymax;
char *s, *t, **av, **argv;
argc = *argcp;
argv = *argvp;
program_name = argv[0]; /* save program name (if any) */
argc--; argv++; /* skip program name */
if(argc >= 1 && **argv == '?') help();
parse_args(&argc, &argv);
if(automatic_abscissas && abscissa_arguments == 0 && x_arguments)
{abscissa_start = xmin;
}
abscissa = abscissa_start;
last = 0;
actual_xmin = actual_ymin = 1.e25;
actual_xmax = actual_ymax = -1.e25;
yy = zz = 0.;
expected = 2;
if(automatic_abscissas) expected--;
if(magnitude||phase) expected++;
argno = 0;
while(1)
{ /* read a data file */
if(argno < argc)
{ifile = fopen(argv[argno], "r");
if(ifile == 0)
{printf("file %s not found\n", argv[argno]);
exit(1);
}
else
{if(*default_script_file==0)
{ /* construct default script file name */
s = getmem(3+strlen(argv[argno]));
if(s)
{default_script_file = s;
strcpy(default_script_file, argv[argno]);
if(s = strchr(default_script_file, '.')) *s=0;
strcat(default_script_file, ".f");
}
}
}
abscissa = abscissa_start;
}
else {ifile = stdin; standard_input++;}
argno++;
while(last<max_points)
{if(fgets(buf, BUFSIZE, ifile) == 0)
{if(standard_input)
{fclose(ifile); ifile = fopen("/dev/con", "r");
#ifdef __DESMET__
uncook(ifile);
forcedup(stdin, ifile);
#endif
}
break;
}
if(buf[0] == ';') continue; /* ignore comments */
if(buf[0] == 0)
{ /* assume a null byte was read, signifying end of line */
buf[0] = ' ';
if(labels++) last_label = last_label->nxt = new_label(last-1, "");
else top_label = last_label = new_label(last-1, "");
breaking = 1;
}
t = buf+strlen(buf)-1; /* zap the CRs (should be none) and LFs */
while(t>buf && (*t == '\015' || *t == '\n')) *t-- = 0;
t = buf; while(*t && isspace(*t)) t++;
if(*t == 0) continue; /* ignore blank lines */
if(automatic_abscissas)
{xx = abscissa;
abscissa += abscissa_step;
if(magnitude||phase)
{sscanf(buf, "%lf %lf", &yy, &zz);
if(magnitude) yy = sqrt(yy*yy + zz*zz);
else if(yy != 0. || zz != 0.) yy = atan2(zz,yy);
}
else sscanf(buf, "%lf", &yy);
}
else
{if(magnitude||phase)
{sscanf(buf, "%lf %lf %lf", &xx, &yy, &zz);
if(magnitude) yy = sqrt(yy*yy + zz*zz);
else if(yy != 0. || zz != 0.) yy = atan2(zz,yy);
}
else sscanf(buf, "%lf %lf", &xx, &yy);
}
if(dividing_by_pi) yy /= 3.1415926535;
if(logx)
if(xx <= 0.) goto GET_LABEL;
else xx = log10(xx);
if(logy)
if(yy <= 0.) goto GET_LABEL;
else yy = log10(yy);
if(xx<actual_xmin) actual_xmin = xx;
if(xx>actual_xmax) actual_xmax = xx;
if(yy<actual_ymin) actual_ymin = yy;
if(yy>actual_ymax) actual_ymax = yy;
range_check(xx); range_check(yy);
x[last] = xx; y[last] = yy; /* convert doubles to floats here */
last++;
GET_LABEL:
s = buf;
for (j = expected; j--; )
{while(*s == ' '||*s == '\t')s++; /* skip a number */
while(*s && (*s != ' ') && (*s != '\t'))s++;
}
while(*s == ' '||*s == '\t')s++;
if(strlen(s))
{if(*s == '\"')
{t = ++s;
while(*t && (*t != '\"')) t++;
}
else
{t = s;
while(*t && (*t != ' '))t++;
}
*t = 0;
if(labels)
{if(last_label->num != last-1)
{last_label = last_label->nxt = new_label(last - 1, s);
labels++;
}
}
else
if(last>0)
{top_label = last_label = new_label(last - 1, s);
labels++;
}
if(breaking) abscissa = abscissa_start;
}
if(out_of_memory) goto NO_MORE_INPUT;
} /* finished with one data file */
if(!labels || last_label->num != last-1)
{ /* add label to last point from each data file */
if(labels++) last_label = last_label->nxt = new_label(last-1, "");
else top_label = last_label = new_label(last-1, "");
}
if(argno >= argc) break;
breaking = 1;
} /* finished with all data files */
last--;
NO_MORE_INPUT:
if(out_of_memory || last+1 >= max_points)
{printf("out of memory - only the 1st %d points displayed", last+1);
printf("\n(press any key to continue)"); getchar();
}
switch(x_arguments)
{case 0: xmin = actual_xmin;
case 1: xmax = actual_xmax;
case 2: ;
case 3: ;
default: ;
}
switch(y_arguments)
{case 0: ymin = actual_ymin;
case 1: ymax = actual_ymax;
case 2: ;
case 3: ;
default: ;
}
if(transposing)
{i = logx; logx = logy; logy = i;
i = x_arguments; x_arguments = y_arguments; y_arguments = i;
pd = x; x = y; y = pd;
d = xmin; xmin = ymin; ymin = d;
d = xmax; xmax = ymax; ymax = d;
d = xdel; xdel = ydel; ydel = d;
}
/*
if(debugging)
{BLOCK *tmp;
printf("%d labels found...\n", labels);
for (tmp = top_label; tmp; tmp = tmp->nxt)
printf("line %d: \"%s\"\n", tmp->num, tmp->txt);
printf("automatic_abscissas = %d\n",automatic_abscissas);
printf("breaking = %d\n",breaking);
printf("debugging = %d\n",debugging);
printf("numeric_labeling = %d\n",numeric_labeling);
printf("logx = %d\n",logx);
printf("logy = %d\n",logy);
printf("magnitude = %d\n",magnitude);
printf("phase = %d\n",phase);
getchar();
}
*/
*argcp = argc;
*argvp = argv;
}
#undef TR
/* return a pointer to the next token from the indicated file
NOTE: uses global array buf */
char *token(fp) FILE *fp;
{ int c;
char *s;
static char separate[] = " \t\n";
s = strtok(NULL, separate);
while(s == NULL)
{s = fgets(buf, BUFSIZE, fp);
if(s == NULL) return NULL;
s = strchr(buf, ';');
if(s != NULL) *s = 0; /* zap the comment */
s = strtok(buf, separate);
}
return s;
}
init_env() /* get initial options from environment variable GRAPH */
{
int ac, error;
char *string, *s, **av;
string = getenv("GRAPH");
if(string == NULL) return;
for (s = string, ac = 0; stoken(&s, buf); ac++)
; /* count tokens in environment string */
av = (char **)getmem(ac*sizeof(char *)); /* allocate appropriate array */
for (s = string, ac = 0; stoken(&s, buf); ac++)
{av[ac] = getmem(1+strlen(buf));
strcpy(av[ac], buf); /* copy token from string */
}
parse_args(&ac, &av); /* on error, it will never return */
}
stoken(sin, sout) char **sin, *sout;
{ int c;
do {*sout = c = *(*sin)++;
if(c == 0) return 0;
} while(isspace(c));
while((*++sout = c = **sin) && !isspace(c)) (*sin)++;
*sout = 0;
return 1;
}
range_check(x) double x;
{ if(fabs(x)>BIGFLOAT)
{printf("input number too large: %f\n", x);
exit(1);
}
}
image()
{ int i, j, j_style = 1, k, st, group, segment;
BLOCK *current_label;
current_label = top_label;
for(group = i = 0; i <= last; group++)
{if(group<numstyles) {j_style = style[group]; segment = repeat[group];}
else segment = 1;
while(segment--)
{j = j_style;
if(j>0)
{if(st = j%16) st--;
set_linestyle(st); j /= 16;
set_color_or_intensity(j); j /= 16;
set_linewidth(j);
move_abs_2(x[i], y[i]);
markers = 0;
}
else
{j = -j;
st = j%16;
set_marker_symbol(st ? st : 6);
j /= 16;
k = j%16;
set_color_or_intensity(j ? k : 0);
markers = 1;
}
if(breaking && j_style == 0xff) /* skip a curve segment */
i = current_label->num+1;
else
{while(i <= last)
{if(markers)marker_abs_2(x[i], y[i]);
else line_abs_2(x[i], y[i]);
if(i == current_label->num)
{text(current_label->txt);
if(breaking) {i++; break;}
else
{current_label = current_label->nxt;
move_abs_2(x[i], y[i]);
}
}
else if(default_labeling)
{text(default_label);
move_abs_2(x[i], y[i]);
}
i++;
}
}
current_label = current_label->nxt;
}
}
set_linewidth(1);
set_linestyle(0);
set_color_or_intensity(0);
}
set_color_or_intensity(j) int j;
{ j &= 15;
if(prim_attr.color_count > 2)
{set_color(j%prim_attr.color_count);
}
else if(prim_attr.intensity_count > 2)
{set_intensity(1. - j/(double)prim_attr.intensity_count);
}
}
/* get_parameter - process one command line option
(return # parameters used) */
get_parameter(argc, argv) int argc; char **argv;
{ int i, left, st;
char *s;
if(streq(*argv, "-a"))
{i = axis_arg(argc, argv, &abscissa_step, &abscissa_start,
&abscissa_start);
abscissa_arguments = i-1;
automatic_abscissas = 1;
return i;
}
else if(streq(*argv, "-b")) {breaking = 1; return 1;}
else if(streq(*argv, "-c"))
{if(argc>1) {default_labeling = 1; default_label = argv[1]; return 2;}
else gripe_arg(argv[0]);
}
else if(streq(*argv, "-d")) {debugging = 1; return 1;}
else if(streq(*argv, "-g"))
{i = 1;
grid_style = 0;
if((argc>i) && numeric(argv[i])) grid_style = atoi(argv[i++]);
if((argc>i) && numeric(argv[i]))
requested_rtx = requested_rty = requested_rlx*atoi(argv[i++]);
if((argc>i) && numeric(argv[i])) grid_width = atoi(argv[i++]);
return i;
}
else if(streq(*argv, "-h"))
{if(argc>1 && numeric(argv[1]))
{requested_height_used = atof(argv[1]); return 2;
}
else gripe_arg(argv[0]);
}
else if(streq(*argv, "-l"))
{if(argc>1)
{argc--;
argv++; /* point to next argument (start of text string) */
text_labeling = 1;
if(**argv == '\"')
{i = 1; left = 80;
(*argv)++; /* skip quote */
text_label = getmem(left--);
*text_label = 0;
while(argc-->0)
{strncat(text_label, *argv, left); left -= strlen(*argv);
argv++;i++;
if(s = strchr(text_label, '\"')) {*s = 0; break;}
strncat(text_label, " ", left--);
}
return i;
}
else {text_label = *argv; return 2;}
}
else gripe_arg(argv[0]);
}
else if(streq(*argv, "-r"))
{if(argc>1 && numeric(argv[1]))
{requested_right_move = atof(argv[1]); return 2;
}
else gripe_arg(argv[0]);
}
else if(streq(*argv, "-t")) {transposing = 1; return 1;}
else if(streq(*argv, "-u"))
{if(argc>1 && numeric(argv[1]))
{requested_up_move = atof(argv[1]); return 2;
}
else gripe_arg(argv[0]);
}
else if(streq(*argv, "-w"))
{if(argc>1 && numeric(argv[1]))
{requested_width_used = atof(argv[1]); return 2;
}
else gripe_arg(argv[0]);
}
else if(streq(*argv, "-e")) {equal = 1; return 1;}
else if(streq(*argv, "-n")) {numeric_labeling = 0; return 1;}
else if(streq(*argv, "-m"))
{i = 1;
while((--argc >0) && hexanumeric(argv[1]) && numstyles<MAXSTYLES)
{if(s = strchr(*++argv,'*'))
{repeat[numstyles] = atoi(*argv);
s++;
}
else {repeat[numstyles] = 1; s = *argv;}
style[numstyles++] = atox(s);
i++;
}
if(numstyles>1 || repeat[0]>1) breaking = 1;
return i;
}
else if(streq(*argv, "-x"))
{i = axis_arg(argc, argv, &xmin, &xmax, &xdel);
x_arguments = i-1;
return i;
}
else if(streq(*argv, "-xl"))
{i = axis_arg(argc, argv, &xmin, &xmax, &xdel);
x_arguments = i-1;
/* printf("before taking log: xmin = %f, xmax = %f, xdel = %f \n",
xmin,xmax,xdel);
*/
if(i>1) take_log(&xmin);
if(i>2) take_log(&xmax);
if(i>3) take_log(&xdel);
/* printf("after taking log: xmin = %f, xmax = %f, xdel = %f \n",
xmin, xmax, xdel);
*/
logx = 1;
return i;
}
else if(streq(*argv, "-yap")||streq(*argv, "-ypp"))
{dividing_by_pi = 1; goto PHASEY;
}
else if(streq(*argv, "-ym")) {magnitude = 1; phase = 0; goto YLIMITS;}
else if(streq(*argv, "-ya")||streq(*argv, "-yp"))
{
PHASEY: phase = 1; magnitude = 0; goto YLIMITS;
}
else if(streq(*argv, "-y"))
{
YLIMITS:
i = axis_arg(argc, argv, &ymin, &ymax, &ydel);
y_arguments = i-1;
return i;
}
else if(streq(*argv, "-ylm")) {magnitude = 1; phase = 0; goto LOGY; }
else if(streq(*argv, "-yl"))
{
LOGY:
i = axis_arg(argc, argv, &ymin, &ymax, &ydel);
y_arguments = i-1;
logy = 1;
if(i>1) take_log(&ymin);
if(i>2) take_log(&ymax);
if(i>3) take_log(&ydel);
return i;
}
else gripe(argv);
}
axis_arg(argc,argv,qmin,qmax,qdel)
int argc; char **argv; double *qmin,*qmax,*qdel;
{ int i = 1;
if((argc>i) && numeric(argv[i])) sscanf(argv[i++],"%lf",qmin);
if((argc>i) && numeric(argv[i])) sscanf(argv[i++],"%lf",qmax);
if((argc>i) && numeric(argv[i])) sscanf(argv[i++],"%lf",qdel);
return i;
}
int streq(a,b) char *a,*b;
{ while(*a)
{if(*a != *b)return 0;
a++; b++;
}
return (*b == 0);
}
gripe_arg(s) char *s;
{ to_text();
printf("argument missing for switch %s",s);
help();
}
gripe(argv) char **argv;
{ to_text();
printf("%s isn\'t a legal argument\n", *argv);
help();
}
char *message[]=
{"sample usage: graph data_file [options]\n",
" or graph <data_file [options]\n",
"\n",
"Data file has pairs (x and y values) or triples (x, yreal, yimag) of\n",
"floating point numbers. Each line may optionally end with a label.\n",
"\n",
"options:\n",
" -a [step [start]] automatic abscissas \n",
" -b break graph after each label (see -m)\n",
" -c <text> default label for points \n",
" -e make vertical & horizontal scales equal \n",
" -f <file> or @<file> get more parameters from <file>\n",
" -g omit grid\n",
" -g <style> <tics> <width> \n",
" <style>: 0=none, 1=frame, 2=full, 3=bottom & left \n",
" only, 4=separated, negative=tics outside\n",
" <tics>: approximate # tic marks per numeric label, \n",
" <width>: grid line width (default 1) \n",
" -l \"<text>\" label to appear above graph \n",
" --- press any key to continue ---",
0,
"\015 -m n1 n2 n3... n positive for line styles: \n",
" n=WCS, where hex digits W=width, C=color, S=dash style,\n",
" nonpositive for markers, ff for nothing \n",
" -n omit numbers on axes \n",
" -t transpose x & y \n",
" -w <num> fraction of screen width to use\n",
" -h <num> fraction of screen height to use\n",
" -r <num> fraction of screen to move right before plotting\n",
" -u <num> fraction of screen to move up before plotting\n",
"",
" -x[l] [min [max]] l signals log axis, \n",
" x axis extends from min to max \n",
" -y[l][m] [min [max]]\n",
" -ya[p] [min [max]]\n",
" -yp[p] [min [max]]\n",
" l signals log axis, m, a, or p signals real & imag parts supplied.\n",
" m to plot magnitude, a or p to plot argument (phase), ap or pp\n",
" to plot phase/pi. y axis extends from min to max \n",
"\n",
/* in printer/plotter versions, the following two lines are zapped */
"When graph is finished: type / to adjust parameters or any other key to quit.\n",
"\n",
RIGHT1,
RIGHT2,
0
};
help()
{ char **s;
int i;
if(plotting_device)
{i = sizeof(message)/sizeof(message[0]) - 5;
strcpy(message[i], "");
strcpy(message[i + 1], "");
}
if(graphics_mode) finish_graphics();
for (s = message; *s; s++) printf(*s);
scr_ci();
while (*++s) printf(*s);
exit(1);
}
numeric(s) char *s;
{ char c;
int numeral_found = 0;
while(c = *s++)
{if(c <= '9' && c >= '0') {numeral_found = 1; continue;}
else if(strchr("eE*+-.", c)) continue;
return 0;
}
return numeral_found;
}
hexanumeric(s) char *s;
{ char c;
int numeral_found = 0;
if(numeric(s)) return 1;
while(c = *s++)
{c = toupper(c);
if((c <= '9' && c >= '0') || (c <= 'F' && c >= 'A'))
{numeral_found = 1; continue;
}
else if(strchr("*-", c)) continue;
return 0;
}
return numeral_found;
}
take_log(q) double *q;
{ if(*q>0.) *q = log10(*q);
else {printf("nonpositive argument %f for log axis", *q); exit(1);}
}
atox(s) char *s;
{ int n, c, sign;
while(*s == ' '||*s == '\t') s++;
if(*s == '-')
{sign = 1;
s++;
}
else sign = 0;
n = 0;
while(1)
{c = *s++;
if(c >= '0' && c <= '9') c -= '0';
else
{if(c >= 'a') c -= 0x20; /* convert to upper case */
if(c >= 'A' && c <= 'F') c = c-'A'+10;
else return (sign ? -n : n);
}
n = (n<<4)+c;
}
}