home *** CD-ROM | disk | FTP | other *** search
- /*
- #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;
- }
- }
-
-