From decwrl!uunet!allbery Sun Mar 25 18:43:08 PST 1990 Article 1446 of comp.sources.misc: Path: decwrl!uunet!allbery From: thaw@ucbvax.Berkeley.EDU@pixar.UUCP (Tom Williams) Newsgroups: comp.sources.misc Subject: v11i071: Gnuplot 2.0 - 6 of 14 Message-ID: <82365@uunet.UU.NET> Date: 26 Mar 90 00:11:54 GMT Sender: allbery@uunet.UU.NET Organization: Pixar -- Marin County, California Lines: 2653 Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) Posting-number: Volume 11, Issue 71 Submitted-by: thaw@ucbvax.Berkeley.EDU@pixar.UUCP (Tom Williams) Archive-name: gnuplot2/part06 This is gnuplot.sh06 --- CUT HERE --- #! /bin/sh echo x - help.c sed 's/^X//' >help.c <<'*-*-END-of-help.c-*-*' X#include X Xextern int errno; X Xextern int strcmp(); Xextern int strlen(); Xextern char *strcpy(); Xextern char *strncpy(); Xextern char *strcat(); Xextern char *strncat(); Xextern char *getenv(); Xextern FILE *fopen(); Xextern char *malloc(); X Xextern int instring(); X X#define SAME 0 /* for strcmp() */ X X#include "help.h" /* values passed back */ X X/* help -- help subsystem that understands defined keywords X** X** Looks for the desired keyword in the help file at runtime, so you X** can give extra help or supply local customizations by merely editing X** the help file. X** X** The original (single-file) idea and algorithm is by John D. Johnson, X** Hewlett-Packard Company. Thanx and a tip of the Hatlo hat! X** X** Much extension by David Kotz for use in gnutex, and then in gnuplot. X** Added output paging support, both unix and builtin. Rewrote completely X** to read helpfile into memory, avoiding reread of help file. 12/89. X** X** The help file looks like this (the question marks are really in column 1): X** X** ?topic X** This line is printed when the user wants help on "topic". X** ?keyword X** ?Keyword X** ?KEYWORD X** These lines will be printed on the screen if the user wanted X** help on "keyword", "Keyword", or "KEYWORD". No casefolding is X** done on the keywords. X** ?subject X** ?alias X** This line is printed for help on "subject" and "alias". X** ? X** ?? X** Since there is a null keyword for this line, this section X** is printed when the user wants general help (when a help X** keyword isn't given). A command summary is usually here. X** Notice that the null keyword is equivalent to a "?" keyword X** here, because of the '?' and '??' topic lines above. X** If multiple keywords are given, the first is considered the X** 'primary' keyword. This affects a listing of available topics. X** ?last-subject X** Note that help sections are terminated by the start of the next X** '?' entry or by EOF. So you can't have a leading '?' on a line X** of any help section. You can re-define the magic character to X** recognize in column 1, though, if '?' is too useful. (Try ^A.) X*/ X X#define KEYFLAG '?' /* leading char in help file topic lines */ X X/* X** Calling sequence: X** int result; # 0 == success X** char *keyword; # topic to give help on X** char *pathname; # path of help file X** result = help(keyword, pathname); X** Sample: X** cmd = "search\n"; X** helpfile = "/usr/local/lib/program/program.help"; X** if (help(cmd, helpfile) != H_FOUND) X** printf("Sorry, no help for %s", cmd); X** X** X** Speed this up by replacing the stdio calls with open/close/read/write. X*/ X#ifdef WDLEN X# define PATHSIZE WDLEN X#else X# define PATHSIZE BUFSIZ X#endif X Xtypedef int boolean; X#ifndef TRUE X#define TRUE (1) X#define FALSE (0) X#endif X Xtypedef struct line_s LINEBUF; Xstruct line_s { X char *line; /* the text of this line */ X LINEBUF *next; /* the next line */ X}; X Xtypedef struct linkey_s LINKEY; Xstruct linkey_s { X char *key; /* the name of this key */ X LINEBUF *text; /* the text for this key */ X boolean primary; /* TRUE -> is a primary name for a text block */ X LINKEY *next; /* the next key in linked list */ X}; X Xtypedef struct key_s KEY; Xstruct key_s { X char *key; /* the name of this key */ X LINEBUF *text; /* the text for this key */ X boolean primary; /* TRUE -> is a primary name for a text block */ X}; Xstatic LINKEY *keylist; /* linked list of keys */ Xstatic KEY *keys = NULL; /* array of keys */ Xstatic int keycount = 0; /* number of keys */ X Xstatic int LoadHelp(); Xstatic void sortkeys(); Xstatic int keycomp(); Xstatic LINEBUF *storeline(); Xstatic void storekey(); Xstatic KEY *FindHelp(); Xstatic boolean Ambiguous(); X X/* Help output */ Xstatic void PrintHelp(); Xstatic void ShowSubtopics(); Xstatic void StartOutput(); Xstatic void OutLine(); Xstatic void EndOutput(); Xstatic FILE *outfile; /* for unix pager, if any */ Xstatic int pagelines; /* count for builtin pager */ X#define SCREENSIZE 24 /* lines on screen (most have at least 24) */ X X/* help: X * print a help message X * also print available subtopics, if subtopics is TRUE X */ Xhelp(keyword, path, subtopics) X char *keyword; /* on this topic */ X char *path; /* from this file */ X boolean *subtopics; /* (in) - subtopics only? */ X /* (out) - are there subtopics? */ X{ X static char oldpath[PATHSIZE] = ""; /* previous help file */ X char *oldpathp = oldpath; /* pointer to same */ X int status; /* result of LoadHelp */ X KEY *key; /* key that matches keyword */ X X /* X ** Load the help file if necessary (say, first time we enter this routine, X ** or if the help file changes from the last time we were called). X ** Also may occur if in-memory copy was freed. X ** Calling routine may access errno to determine cause of H_ERROR. X */ X errno = 0; X if (strncmp(oldpathp, path, sizeof oldpath) != SAME) X FreeHelp(); X if (keys == NULL) { X status = LoadHelp(path); X if (status == H_ERROR) X return(status); X X /* save the new path in oldpath */ X if (strlen(path) < sizeof oldpath) X (void) strcpy(oldpathp, path); X else { /* not enough room in oldpath, sigh */ X (void) strncpy(oldpathp, path, sizeof oldpath); X oldpath[sizeof oldpath] = NULL; X } X } X X /* look for the keyword in the help file */ X key = FindHelp(keyword); X if (key != NULL) { X /* found the keyword: print help and return */ X PrintHelp(key, subtopics); X status = H_FOUND; X } else { X status = H_NOTFOUND; X } X X return(status); X} X X/* we only read the file once, into memory */ Xstatic int XLoadHelp(path) X char *path; X{ X FILE *helpfp = NULL; X char buf[BUFSIZ]; /* line from help file */ X LINEBUF *head; /* head of text list */ X boolean primary; /* first ? line of a set is primary */ X X if ((helpfp = fopen(path, "r")) == NULL) { X /* can't open help file, so error exit */ X return (H_ERROR); X } X X /* X ** The help file is open. Look in there for the keyword. X */ X (void) fgets(buf, sizeof buf, helpfp); X while (!feof(helpfp)) { X /* X ** Make an entry for each synonym keyword, pointing X ** to same buffer. X */ X head = storeline( (char *)NULL ); /* make a dummy text entry */ X primary = TRUE; X while (buf[0] == KEYFLAG) { X storekey(buf+1, head, primary); /* store this key */ X primary = FALSE; X if (fgets(buf, sizeof buf, helpfp) == (char *)NULL) X break; X } X /* X ** Now store the text for this entry. X ** buf already contains the first line of text. X */ X while (buf[0] != KEYFLAG) { X /* save text line */ X head->next = storeline(buf); X head = head->next; X if (fgets(buf, sizeof buf, helpfp) == (char *)NULL) X break; X } X } X X (void) fclose(helpfp); X X /* we sort the keys so we can use binary search later */ X sortkeys(); X return(H_FOUND); /* ok */ X} X X/* make a new line buffer and save this string there */ Xstatic LINEBUF * Xstoreline(text) X char *text; X{ X LINEBUF *new; X X new = (LINEBUF *)malloc(sizeof(LINEBUF)); X if (new == NULL) X int_error("not enough memory to store help file", -1); X if (text != NULL) { X new->line = (char *) malloc((unsigned int)(strlen(text)+1)); X if (new->line == NULL) X int_error("not enough memory to store help file", -1); X (void) strcpy(new->line, text); X } else X new->line = NULL; X X new->next = NULL; X X return(new); X} X X/* Add this keyword to the keys list, with the given text */ Xstatic void Xstorekey(key, buffer, primary) X char *key; X LINEBUF *buffer; X boolean primary; X{ X LINKEY *new; X X key[strlen(key)-1] = '\0'; /* cut off \n */ X X new = (LINKEY *)malloc(sizeof(LINKEY)); X if (new == NULL) X int_error("not enough memory to store help file", -1); X new->key = (char *) malloc((unsigned int)(strlen(key)+1)); X if (new->key == NULL) X int_error("not enough memory to store help file", -1); X (void) strcpy(new->key, key); X new->text = buffer; X new->primary = primary; X X /* add to front of list */ X new->next = keylist; X keylist = new; X keycount++; X} X X/* we sort the keys so we can use binary search later */ X/* We have a linked list of keys and the number. X * to sort them we need an array, so we reform them into an array, X * and then throw away the list. X */ Xstatic void Xsortkeys() X{ X LINKEY *p,*n; /* pointers to linked list */ X int i; /* index into key array */ X X /* allocate the array */ X keys = (KEY *)malloc((unsigned int)((keycount+1) * sizeof(KEY))); X if (keys == NULL) X int_error("not enough memory to store help file", -1); X X /* copy info from list to array, freeing list */ X for (p = keylist, i = 0; p != NULL; p = n, i++) { X keys[i].key = p->key; X keys[i].text = p->text; X keys[i].primary = p->primary; X n = p->next; X free( (char *)p ); X } X X /* a null entry to terminate subtopic searches */ X keys[keycount].key = NULL; X keys[keycount].text = NULL; X X /* sort the array */ X /* note that it only moves objects of size (two pointers) */ X /* it moves no data */ X qsort((char *)keys, keycount, sizeof(KEY), keycomp); X} X Xstatic int Xkeycomp(a, b) X KEY *a,*b; X{ X return (strcmp(a->key, b->key)); X} X X/* Free the help file from memory. */ X/* May be called externally if space is needed */ Xvoid XFreeHelp() X{ X int i; /* index into keys[] */ X LINEBUF *t, *next; X X if (keys == NULL) X return; X X for (i = 0; i < keycount; i++) { X free( (char *)keys[i].key ); X for (t = keys[i].text; t != NULL; t = next) { X free( (char *)t->line ); X next = t->next; X free( (char *)t ); X } X free( (char *)keys[i].text ); X } X free( (char *)keys ); X keys = NULL; X keycount = 0; X} X X/* FindHelp: X * Find the key that matches the keyword. X * The keys[] array is sorted by key. X * We could use a binary search, but a linear search will aid our X * attempt to allow abbreviations. We search for the first thing that X * matches all the text we're given. If not an exact match, then X * it is an abbreviated match, and there must be no other abbreviated X * matches -- for if there are, the abbreviation is ambiguous. X * We print the ambiguous matches in that case, and return not found. X */ Xstatic KEY * /* NULL if not found */ XFindHelp(keyword) X char *keyword; /* string we look for */ X{ X KEY *key; X int len = strlen(keyword); X int compare; X X for (key = keys, compare = 1; key->key != NULL && compare > 0; key++) { X compare = strncmp(keyword, key->key, len); X if (compare == 0) /* we have a match! */ X if (!Ambiguous(key, len)) { X /* non-ambiguous abbreviation */ X (void) strcpy(keyword, key->key); /* give back the full spelling */ X return(key); /* found!! */ X } X } X X /* not found, or ambiguous */ X return(NULL); X} X X/* Ambiguous: X * Check the key for ambiguity up to the given length. X * It is ambiguous if it is not a complete string and there are other X * keys following it with the same leading substring. X */ Xstatic boolean XAmbiguous(key, len) X KEY *key; X int len; X{ X char *first; X char *prev; X boolean status = FALSE; /* assume not ambiguous */ X int compare; X int sublen; X X if (key->key[len] == '\0') X return(FALSE); X X for (prev = first = key->key, compare = 0, key++; X key->key != NULL && compare == 0; key++) { X compare = strncmp(first, key->key, len); X if (compare == 0) { X /* So this key matches the first one, up to len. X * But is it different enough from the previous one X * to bother printing it as a separate choice? X */ X sublen = instring(prev+len, ' '); X if (strncmp(key->key, prev, len+sublen) != 0) { X /* yup, this is different up to the next space */ X if (!status) { X /* first one we have printed is special */ X fprintf(stderr, X "Ambiguous request '%.*s'; possible matches:\n", X len, first); X fprintf(stderr, "\t%s\n", prev); X status = TRUE; X } X fprintf(stderr, "\t%s\n", key->key); X prev = key->key; X } X } X } X X return(status); X} X X/* PrintHelp: X * print the text for key X */ Xstatic void XPrintHelp(key, subtopics) X KEY *key; X boolean *subtopics; /* (in) - subtopics only? */ X /* (out) - are there subtopics? */ X{ X LINEBUF *t; X X StartOutput(); X X if (subtopics == NULL || !*subtopics) { X /* the first linebuf is a dummy, so we skip it */ X for (t = key->text->next; t != NULL; t = t->next) X OutLine(t->line); /* print text line */ X } X X ShowSubtopics(key, subtopics); X OutLine("\n"); X X EndOutput(); X} X X/* ShowSubtopics: X * Print a list of subtopic names X */ X#define PER_LINE 4 X Xstatic void XShowSubtopics(key, subtopics) X KEY *key; /* the topic */ X boolean *subtopics; /* (out) are there any subtopics */ X{ X int subt = 0; /* printed any subtopics yet? */ X KEY *subkey; /* subtopic key */ X int len; /* length of key name */ X char line[BUFSIZ]; /* subtopic output line */ X char *start; /* position of subname in key name */ X int sublen; /* length of subname */ X int pos; X char *prev = NULL; /* the last thing we put on the list */ X X *line = '\0'; X len = strlen(key->key); X X for (subkey = key+1; subkey->key != NULL; subkey++) { X if (strncmp(subkey->key, key->key, len) == 0) { X /* find this subtopic name */ X start = subkey->key + len; X if (len > 0) X if (*start == ' ') X start++; /* skip space */ X else X break; /* not the same topic after all */ X else /* here we are looking for main topics */ X if (!subkey->primary) X continue; /* not a main topic */ X sublen = instring(start, ' '); X if (prev == NULL || strncmp(start, prev, sublen) != 0) { X if (subt == 0) { X subt++; X if (len) X (void) sprintf(line, "\nSubtopics available for %s:\n", X key->key); X else X (void) sprintf(line, "\nHelp topics available:\n"); X OutLine(line); X *line = '\0'; X pos = 0; X } X if (pos == PER_LINE) { X (void) strcat(line, "\n"); X OutLine(line); X *line = '\0'; X pos = 0; X } X (void) strcat(line, "\t"); X (void) strncat(line, start, sublen); X pos++; X prev = start; X } X } else { X /* new topic */ X break; X } X } X X /* put out the last line */ X if (subt > 0 && pos > 0) { X (void) strcat(line, "\n"); X OutLine(line); X } X X/* X if (subt == 0) { X OutLine("\n"); X OutLine("No subtopics available\n"); X } X*/ X X if (subtopics) X *subtopics = (subt != 0); X} X X X/* StartOutput: X * Open a file pointer to a pipe to user's $PAGER, if there is one, X * otherwise use our own pager. X */ Xstatic void XStartOutput() X{ X#ifdef unix X char *pager_name = getenv("PAGER"); X extern FILE *popen(); X X if (pager_name != NULL && *pager_name != '\0') X if ((outfile = popen(pager_name, "w")) != (FILE *)NULL) X return; /* success */ X outfile = stderr; X /* fall through to built-in pager */ X#endif X X /* built-in pager */ X pagelines = 0; X} X X/* write a line of help output */ X/* line should contain only one \n, at the end */ Xstatic void XOutLine(line) X char *line; X{ X int c; /* dummy input char */ X#ifdef unix X if (outfile != stderr) { X fputs(line, outfile); X return; X } X#endif X X /* built-in dumb pager */ X /* leave room for prompt line */ X if (pagelines >= SCREENSIZE - 2) { X printf("Press return for more: "); X do X c = getchar(); X while (c != EOF && c != '\n'); X pagelines = 0; X } X fputs(line, stderr); X pagelines++; X} X Xstatic void XEndOutput() X{ X#ifdef unix X extern int pclose(); X X if (outfile != stderr) X (void) pclose(outfile); X#endif X} *-*-END-of-help.c-*-* echo x - graphics.c sed 's/^X//' >graphics.c <<'*-*-END-of-graphics.c-*-*' X/* GNUPLOT - graphics.c */ X/* X * Copyright (C) 1986, 1987, 1990 Thomas Williams, Colin Kelley X * X * Permission to use, copy, and distribute this software and its X * documentation for any purpose with or without fee is hereby granted, X * provided that the above copyright notice appear in all copies and X * that both that copyright notice and this permission notice appear X * in supporting documentation. X * X * Permission to modify the software is granted, but not the right to X * distribute the modified code. Modifications are to be distributed X * as patches to released version. X * X * This software is provided "as is" without express or implied warranty. X * X * X * AUTHORS X * X * Original Software: X * Thomas Williams, Colin Kelley. X * X * Gnuplot 2.0 additions: X * Russell Lang, Dave Kotz, John Campbell. X * X * send your comments or suggestions to (pixar!info-gnuplot@sun.com). X * X */ X X#include X#include X#include X#include "plot.h" X#include "setshow.h" X Xextern char *strcpy(),*strncpy(),*strcat(); X Xvoid plot_impulses(); Xvoid plot_lines(); Xvoid plot_points(); Xvoid plot_dots(); Xvoid edge_intersect(); XBOOLEAN two_edge_intersect(); X X#ifndef max /* Lattice C has max() in math.h, but shouldn't! */ X#define max(a,b) ((a > b) ? a : b) X#endif X X#ifndef min X#define min(a,b) ((a < b) ? a : b) X#endif X X#define inrange(z,min,max) ((min=min)&&(z<=max)) : ((z>=max)&&(z<=min)) ) X X/* Define the boundary of the plot X * These are computed at each call to do_plot, and are constant over X * the period of one do_plot. They actually only change when the term X * type changes and when the 'set size' factors change. X */ Xstatic int xleft, xright, ybot, ytop; X X/* Boundary and scale factors, in user coordinates */ Xstatic double xmin, xmax, ymin, ymax; Xstatic double xscale, yscale; X X/* And the functions to map from user to terminal coordinates */ X#define map_x(x) (int)(xleft+(x-xmin)*xscale+0.5) /* maps floating point x to screen */ X#define map_y(y) (int)(ybot+(y-ymin)*yscale+0.5) /* same for y */ X X/* (DFK) Watch for cancellation error near zero on axes labels */ X#define SIGNIF (0.01) /* less than one hundredth of a tic mark */ X#define CheckZero(x,tic) (fabs(x) < ((tic) * SIGNIF) ? 0.0 : (x)) X#define NearlyEqual(x,y,tic) (fabs((x)-(y)) < ((tic) * SIGNIF)) X X/* (DFK) For some reason, the Sun386i compiler screws up with the CheckLog X * macro, so I write it as a function on that machine. X */ X#ifndef sun386 X/* (DFK) Use 10^x if logscale is in effect, else x */ X#define CheckLog(log, x) ((log) ? pow(10., (x)) : (x)) X#else Xstatic double XCheckLog(log, x) X BOOLEAN log; X double x; X{ X if (log) X return(pow(10., x)); X else X return(x); X} X#endif /* sun386 */ X Xdouble XLogScale(coord, islog, what, axis) X double coord; /* the value */ X BOOLEAN islog; /* is this axis in logscale? */ X char *what; /* what is the coord for? */ X char *axis; /* which axis is this for ("x" or "y")? */ X{ X if (islog) { X if (coord <= 0.0) { X char errbuf[100]; /* place to write error message */ X (void) sprintf(errbuf,"%s has %s coord of %g; must be above 0 for log scale!", X what, axis, coord); X (*term_tbl[term].text)(); X (void) fflush(outfile); X int_error(errbuf, NO_CARET); X } else X return(log10(coord)); X } else { X return(coord); X } X return((double)NULL); /* shut lint up */ X} X X/* borders of plotting area */ X/* computed once on every call to do_plot */ Xboundary(scaling) X BOOLEAN scaling; /* TRUE if terminal is doing the scaling */ X{ X register struct termentry *t = &term_tbl[term]; X xleft = (t->h_char)*12; X xright = (scaling ? 1 : xsize) * (t->xmax) - (t->h_char)*2 - (t->h_tic); X ybot = (t->v_char)*5/2 + 1; X ytop = (scaling ? 1 : ysize) * (t->ymax) - (t->v_char)*3/2 - 1; X} X X Xdouble dbl_raise(x,y) Xdouble x; Xint y; X{ Xregister int i; Xdouble val; X X val = 1.0; X for (i=0; i < abs(y); i++) X val *= x; X if (y < 0 ) return (1.0/val); X return(val); X} X X Xdouble make_tics(tmin,tmax,logscale) Xdouble tmin,tmax; XBOOLEAN logscale; X{ Xregister double xr,xnorm,tics,tic,l10; X X xr = fabs(tmin-tmax); X X l10 = log10(xr); X if (logscale) { X tic = dbl_raise(10.0,(l10 >= 0.0 ) ? (int)l10 : ((int)l10-1)); X if (tic < 1.0) X tic = 1.0; X } else { X xnorm = pow(10.0,l10-(double)((l10 >= 0.0 ) ? (int)l10 : ((int)l10-1))); X if (xnorm <= 2) X tics = 0.2; X else if (xnorm <= 5) X tics = 0.5; X else tics = 1.0; X tic = tics * dbl_raise(10.0,(l10 >= 0.0 ) ? (int)l10 : ((int)l10-1)); X } X return(tic); X} X X Xdo_plot(plots, pcount, min_x, max_x, min_y, max_y) Xstruct curve_points *plots; Xint pcount; /* count of plots in linked list */ Xdouble min_x, max_x; Xdouble min_y, max_y; X{ Xregister struct termentry *t = &term_tbl[term]; Xregister int curve, xaxis_y, yaxis_x; Xregister struct curve_points *this_plot; Xregister double ytic, xtic; Xregister int xl, yl; X /* only a Pyramid would have this many registers! */ Xdouble xtemp, ytemp; Xstruct text_label *this_label; Xstruct arrow_def *this_arrow; XBOOLEAN scaling; X X/* store these in variables global to this file */ X/* otherwise, we have to pass them around a lot */ X xmin = min_x; X xmax = max_x; X ymin = min_y; X ymax = max_y; X X if (polar) { X /* will possibly change xmin, xmax, ymin, ymax */ X polar_xform(plots,pcount); X } X X if (ymin == VERYLARGE || ymax == -VERYLARGE) X int_error("all points undefined!", NO_CARET); X X if (xmin == VERYLARGE || xmax == -VERYLARGE) X int_error("all points undefined!", NO_CARET); X X/* Apply the desired viewport offsets. */ X if (ymin < ymax) { X ymin -= boff; X ymax += toff; X } else { X ymax -= boff; X ymin += toff; X } X if (xmin < xmax) { X xmin -= loff; X xmax += roff; X } else { X xmax -= loff; X xmin += roff; X } X X/* SETUP RANGES, SCALES AND TIC PLACES */ X if (ytics && yticdef.type == TIC_COMPUTED) { X ytic = make_tics(ymin,ymax,log_y); X X if (autoscale_ly) { X if (ymin < ymax) { X ymin = ytic * floor(ymin/ytic); X ymax = ytic * ceil(ymax/ytic); X } X else { /* reverse axis */ X ymin = ytic * ceil(ymin/ytic); X ymax = ytic * floor(ymax/ytic); X } X } X } X X if (xtics && xticdef.type == TIC_COMPUTED) { X xtic = make_tics(xmin,xmax,log_x); X X if (autoscale_lx) { X if (xmin < xmax) { X xmin = xtic * floor(xmin/xtic); X xmax = xtic * ceil(xmax/xtic); X } else { X xmin = xtic * ceil(xmin/xtic); X xmax = xtic * floor(xmax/xtic); X } X } X } X X/* This used be xmax == xmin, but that caused an infinite loop once. */ X if (fabs(xmax - xmin) < zero) X int_error("xmin should not equal xmax!",NO_CARET); X if (fabs(ymax - ymin) < zero) X int_error("ymin should not equal ymax!",NO_CARET); X X/* INITIALIZE TERMINAL */ X if (!term_init) { X (*t->init)(); X term_init = TRUE; X } X screen_ok = FALSE; X scaling = (*t->scale)(xsize, ysize); X (*t->graphics)(); X X /* now compute boundary for plot (xleft, xright, ytop, ybot) */ X boundary(scaling); X X/* SCALE FACTORS */ X yscale = (ytop - ybot)/(ymax - ymin); X xscale = (xright - xleft)/(xmax - xmin); X X/* DRAW AXES */ X (*t->linetype)(-1); /* axis line type */ X xaxis_y = map_y(0.0); X yaxis_x = map_x(0.0); X X if (xaxis_y < ybot) X xaxis_y = ybot; /* save for impulse plotting */ X else if (xaxis_y >= ytop) X xaxis_y = ytop ; X else if (!log_y) { X (*t->move)(xleft,xaxis_y); X (*t->vector)(xright,xaxis_y); X } X X if (!log_x && yaxis_x >= xleft && yaxis_x < xright ) { X (*t->move)(yaxis_x,ybot); X (*t->vector)(yaxis_x,ytop); X } X X/* DRAW TICS */ X (*t->linetype)(-2); /* border linetype */ X X /* label y axis tics */ X if (ytics) { X switch (yticdef.type) { X case TIC_COMPUTED: { X if (ymin < ymax) X draw_ytics(ytic * floor(ymin/ytic), X ytic, X ytic * ceil(ymax/ytic)); X else X draw_ytics(ytic * floor(ymax/ytic), X ytic, X ytic * ceil(ymin/ytic)); X X break; X } X case TIC_SERIES: { X draw_ytics(yticdef.def.series.start, X yticdef.def.series.incr, X yticdef.def.series.end); X break; X } X case TIC_USER: { X draw_user_ytics(yticdef.def.user); X break; X } X default: { X (*t->text)(); X (void) fflush(outfile); X int_error("unknown tic type in yticdef in do_plot", NO_CARET); X break; /* NOTREACHED */ X } X } X } X X /* label x axis tics */ X if (xtics) { X switch (xticdef.type) { X case TIC_COMPUTED: { X if (xmin < xmax) X draw_xtics(xtic * floor(xmin/xtic), X xtic, X xtic * ceil(xmax/xtic)); X else X draw_xtics(xtic * floor(xmax/xtic), X xtic, X xtic * ceil(xmin/xtic)); X X break; X } X case TIC_SERIES: { X draw_xtics(xticdef.def.series.start, X xticdef.def.series.incr, X xticdef.def.series.end); X break; X } X case TIC_USER: { X draw_user_xtics(xticdef.def.user); X break; X } X default: { X (*t->text)(); X (void) fflush(outfile); X int_error("unknown tic type in xticdef in do_plot", NO_CARET); X break; /* NOTREACHED */ X } X } X } X X/* DRAW PLOT BORDER */ X (*t->linetype)(-2); /* border linetype */ X (*t->move)(xleft,ybot); X (*t->vector)(xright,ybot); X (*t->vector)(xright,ytop); X (*t->vector)(xleft,ytop); X (*t->vector)(xleft,ybot); X X/* PLACE YLABEL */ X if (*ylabel != NULL) { X if ((*t->text_angle)(1)) { X if ((*t->justify_text)(CENTRE)) { X (*t->put_text)((t->v_char), X (ytop+ybot)/2, ylabel); X } X else { X (*t->put_text)((t->v_char), X (ytop+ybot)/2-(t->h_char)*strlen(ylabel)/2, X ylabel); X } X } X else { X (void)(*t->justify_text)(LEFT); X (*t->put_text)(0,ytop+(t->v_char), ylabel); X } X (void)(*t->text_angle)(0); X } X X/* PLACE XLABEL */ X if (*xlabel != NULL) { X if ((*t->justify_text)(CENTRE)) X (*t->put_text)( (xleft+xright)/2, X ybot-2*(t->v_char), xlabel); X else X (*t->put_text)( (xleft+xright)/2 - strlen(xlabel)*(t->h_char)/2, X ybot-2*(t->v_char), xlabel); X } X X/* PLACE TITLE */ X if (*title != NULL) { X if ((*t->justify_text)(CENTRE)) X (*t->put_text)( (xleft+xright)/2, X ytop+(t->v_char), title); X else X (*t->put_text)( (xleft+xright)/2 - strlen(title)*(t->h_char)/2, X ytop+(t->v_char), title); X } X X/* PLACE LABELS */ X for (this_label = first_label; this_label!=NULL; X this_label=this_label->next ) { X xtemp = LogScale(this_label->x, log_x, "label", "x"); X ytemp = LogScale(this_label->y, log_y, "label", "y"); X if ((*t->justify_text)(this_label->pos)) { X (*t->put_text)(map_x(xtemp),map_y(ytemp),this_label->text); X } X else { X switch(this_label->pos) { X case LEFT: X (*t->put_text)(map_x(xtemp),map_y(ytemp), X this_label->text); X break; X case CENTRE: X (*t->put_text)(map_x(xtemp)- X (t->h_char)*strlen(this_label->text)/2, X map_y(ytemp), this_label->text); X break; X case RIGHT: X (*t->put_text)(map_x(xtemp)- X (t->h_char)*strlen(this_label->text), X map_y(ytemp), this_label->text); X break; X } X } X } X X/* PLACE ARROWS */ X (*t->linetype)(0); /* arrow line type */ X for (this_arrow = first_arrow; this_arrow!=NULL; X this_arrow = this_arrow->next ) { X int sx = map_x(LogScale(this_arrow->sx, log_x, "arrow", "x")); X int sy = map_y(LogScale(this_arrow->sy, log_y, "arrow", "y")); X int ex = map_x(LogScale(this_arrow->ex, log_x, "arrow", "x")); X int ey = map_y(LogScale(this_arrow->ey, log_y, "arrow", "y")); X X (*t->arrow)(sx, sy, ex, ey); X } X X X/* DRAW CURVES */ X if (key == -1) { X xl = xright - (t->h_tic) - (t->h_char)*5; X yl = ytop - (t->v_tic) - (t->v_char); X } X if (key == 1) { X xl = map_x( LogScale(key_x, log_x, "key", "x") ); X yl = map_y( LogScale(key_y, log_y, "key", "y") ); X } X X this_plot = plots; X for (curve = 0; curve < pcount; this_plot = this_plot->next_cp, curve++) { X (*t->linetype)(this_plot->line_type); X if (key != 0) { X if ((*t->justify_text)(RIGHT)) { X (*t->put_text)(xl, X yl,this_plot->title); X } X else { X if (inrange(xl-(t->h_char)*strlen(this_plot->title), X xleft, xright)) X (*t->put_text)(xl-(t->h_char)*strlen(this_plot->title), X yl,this_plot->title); X } X } X X switch(this_plot->plot_style) { X case IMPULSES: { X if (key != 0) { X (*t->move)(xl+(t->h_char),yl); X (*t->vector)(xl+4*(t->h_char),yl); X } X plot_impulses(this_plot, xaxis_y); X break; X } X case LINES: { X if (key != 0) { X (*t->move)(xl+(int)(t->h_char),yl); X (*t->vector)(xl+(int)(4*(t->h_char)),yl); X } X plot_lines(this_plot); X break; X } X case POINTS: { X if (key != 0) { X (*t->point)(xl+2*(t->h_char),yl, X this_plot->point_type); X } X plot_points(this_plot); X break; X } X case LINESPOINTS: { X /* put lines */ X if (key != 0) { X (*t->move)(xl+(t->h_char),yl); X (*t->vector)(xl+4*(t->h_char),yl); X } X plot_lines(this_plot); X X /* put points */ X if (key != 0) { X (*t->point)(xl+2*(t->h_char),yl, X this_plot->point_type); X } X plot_points(this_plot); X break; X } X case DOTS: { X if (key != 0) { X (*t->point)(xl+2*(t->h_char),yl, -1); X } X plot_dots(this_plot); X break; X } X } X yl = yl - (t->v_char); X } X (*t->text)(); X (void) fflush(outfile); X} X X/* plot_impulses: X * Plot the curves in IMPULSES style X */ Xvoid Xplot_impulses(plot, xaxis_y) X struct curve_points *plot; X int xaxis_y; X{ X int i; X int x,y; X struct termentry *t = &term_tbl[term]; X X for (i = 0; i < plot->p_count; i++) { X switch (plot->points[i].type) { X case INRANGE: { X x = map_x(plot->points[i].x); X y = map_y(plot->points[i].y); X break; X } X case OUTRANGE: { X if (!inrange(plot->points[i].x, xmin,xmax)) X continue; X x = map_x(plot->points[i].x); X if ((ymin < ymax X && plot->points[i].y < ymin) X || (ymax < ymin X && plot->points[i].y > ymin)) X y = map_y(ymin); X if ((ymin < ymax X && plot->points[i].y > ymax) X || (ymaxpoints[i].y < ymax)) X y = map_y(ymax); X break; X } X default: /* just a safety */ X case UNDEFINED: { X continue; X } X } X X (*t->move)(x,xaxis_y); X (*t->vector)(x,y); X } X X} X X/* plot_lines: X * Plot the curves in LINES style X */ Xvoid Xplot_lines(plot) X struct curve_points *plot; X{ X int i; /* point index */ X int x,y; /* point in terminal coordinates */ X struct termentry *t = &term_tbl[term]; X enum coord_type prev = UNDEFINED; /* type of previous point */ X double ex, ey; /* an edge point */ X double lx[2], ly[2]; /* two edge points */ X X for (i = 0; i < plot->p_count; i++) { X switch (plot->points[i].type) { X case INRANGE: { X x = map_x(plot->points[i].x); X y = map_y(plot->points[i].y); X X if (prev == INRANGE) { X (*t->vector)(x,y); X } else if (prev == OUTRANGE) { X /* from outrange to inrange */ X if (!clip_lines1) { X (*t->move)(x,y); X } else { X edge_intersect(plot->points, i, &ex, &ey); X (*t->move)(map_x(ex), map_y(ey)); X (*t->vector)(x,y); X } X } else { /* prev == UNDEFINED */ X (*t->move)(x,y); X (*t->vector)(x,y); X } X X break; X } X case OUTRANGE: { X if (prev == INRANGE) { X /* from inrange to outrange */ X if (clip_lines1) { X edge_intersect(plot->points, i, &ex, &ey); X (*t->vector)(map_x(ex), map_y(ey)); X } X } else if (prev == OUTRANGE) { X /* from outrange to outrange */ X if (clip_lines2) { X if (two_edge_intersect(plot->points, i, lx, ly)) { X (*t->move)(map_x(lx[0]), map_y(ly[0])); X (*t->vector)(map_x(lx[1]), map_y(ly[1])); X } X } X } X break; X } X default: /* just a safety */ X case UNDEFINED: { X break; X } X } X prev = plot->points[i].type; X } X} X X/* plot_points: X * Plot the curves in POINTS style X */ Xvoid Xplot_points(plot) X struct curve_points *plot; X{ X int i; X int x,y; X struct termentry *t = &term_tbl[term]; X X for (i = 0; i < plot->p_count; i++) { X if (plot->points[i].type == INRANGE) { X x = map_x(plot->points[i].x); X y = map_y(plot->points[i].y); X /* do clipping if necessary */ X if (!clip_points || X ( x >= xleft + t->h_tic && y >= ybot + t->v_tic X && x <= xright - t->h_tic && y <= ytop - t->v_tic)) X (*t->point)(x,y, plot->point_type); X } X } X} X X/* plot_dots: X * Plot the curves in DOTS style X */ Xvoid Xplot_dots(plot) X struct curve_points *plot; X{ X int i; X int x,y; X struct termentry *t = &term_tbl[term]; X X for (i = 0; i < plot->p_count; i++) { X if (plot->points[i].type == INRANGE) { X x = map_x(plot->points[i].x); X y = map_y(plot->points[i].y); X /* point type -1 is a dot */ X (*t->point)(x,y, -1); X } X } X} X X/* single edge intersection algorithm */ X/* Given two points, one inside and one outside the plot, return X * the point where an edge of the plot intersects the line segment defined X * by the two points. X */ Xvoid Xedge_intersect(points, i, ex, ey) X struct coordinate *points; /* the points array */ X int i; /* line segment from point i-1 to point i */ X double *ex, *ey; /* the point where it crosses an edge */ X{ X /* global xmin, xmax, ymin, xmax */ X double ax = points[i-1].x; X double ay = points[i-1].y; X double bx = points[i].x; X double by = points[i].y; X double x, y; /* possible intersection point */ X X if (by == ay) { X /* horizontal line */ X /* assume inrange(by, ymin, ymax) */ X *ey = by; /* == ay */ X X if (inrange(xmax, ax, bx)) X *ex = xmax; X else if (inrange(xmin, ax, bx)) X *ex = xmin; X else { X (*term_tbl[term].text)(); X (void) fflush(outfile); X int_error("error in edge_intersect", NO_CARET); X } X return; X } else if (bx == ax) { X /* vertical line */ X /* assume inrange(bx, xmin, xmax) */ X *ex = bx; /* == ax */ X X if (inrange(ymax, ay, by)) X *ey = ymax; X else if (inrange(ymin, ay, by)) X *ey = ymin; X else { X (*term_tbl[term].text)(); X (void) fflush(outfile); X int_error("error in edge_intersect", NO_CARET); X } X return; X } X X /* slanted line of some kind */ X X /* does it intersect ymin edge */ X if (inrange(ymin, ay, by) && ymin != ay && ymin != by) { X x = ax + (ymin-ay) * ((bx-ax) / (by-ay)); X if (inrange(x, xmin, xmax)) { X *ex = x; X *ey = ymin; X return; /* yes */ X } X } X X /* does it intersect ymax edge */ X if (inrange(ymax, ay, by) && ymax != ay && ymax != by) { X x = ax + (ymax-ay) * ((bx-ax) / (by-ay)); X if (inrange(x, xmin, xmax)) { X *ex = x; X *ey = ymax; X return; /* yes */ X } X } X X /* does it intersect xmin edge */ X if (inrange(xmin, ax, bx) && xmin != ax && xmin != bx) { X y = ay + (xmin-ax) * ((by-ay) / (bx-ax)); X if (inrange(y, ymin, ymax)) { X *ex = xmin; X *ey = y; X return; X } X } X X /* does it intersect xmax edge */ X if (inrange(xmax, ax, bx) && xmax != ax && xmax != bx) { X y = ay + (xmax-ax) * ((by-ay) / (bx-ax)); X if (inrange(y, ymin, ymax)) { X *ex = xmax; X *ey = y; X return; X } X } X X /* It is possible for one or two of the [ab][xy] values to be -VERYLARGE. X * If ax=bx=-VERYLARGE or ay=by=-VERYLARGE we have already returned X * FALSE above. Otherwise we fall through all the tests above. X * If two are -VERYLARGE, it is ax=ay=-VERYLARGE or bx=by=-VERYLARGE X * since either a or b must be INRANGE. X * Note that for ax=ay=-VERYLARGE or bx=by=-VERYLARGE we can do nothing. X * Handle them carefully here. As yet we have no way for them to be X * +VERYLARGE. X */ X if (ax == -VERYLARGE) { X if (ay != -VERYLARGE) { X *ex = min(xmin, xmax); X *ey = by; X return; X } X } else if (bx == -VERYLARGE) { X if (by != -VERYLARGE) { X *ex = min(xmin, xmax); X *ey = ay; X return; X } X } else if (ay == -VERYLARGE) { X /* note we know ax != -VERYLARGE */ X *ex = bx; X *ey = min(ymin, ymax); X return; X } else if (by == -VERYLARGE) { X /* note we know bx != -VERYLARGE */ X *ex = ax; X *ey = min(ymin, ymax); X return; X } X X /* If we reach here, then either one point is (-VERYLARGE,-VERYLARGE), X * or the inrange point is on the edge, and X * the line segment from the outrange point does not cross any X * other edges to get there. In either case, we return the inrange X * point as the 'edge' intersection point. This will basically draw X * line. X */ X if (points[i].type == INRANGE) { X *ex = bx; X *ey = by; X } else { X *ex = ax; X *ey = ay; X } X return; X} X X/* double edge intersection algorithm */ X/* Given two points, both outside the plot, return X * the points where an edge of the plot intersects the line segment defined X * by the two points. There may be zero, one, two, or an infinite number X * of intersection points. (One means an intersection at a corner, infinite X * means overlaying the edge itself). We return FALSE when there is nothing X * to draw (zero intersections), and TRUE when there is something to X * draw (the one-point case is a degenerate of the two-point case and we do X * not distinguish it - we draw it anyway). X */ XBOOLEAN /* any intersection? */ Xtwo_edge_intersect(points, i, lx, ly) X struct coordinate *points; /* the points array */ X int i; /* line segment from point i-1 to point i */ X double *lx, *ly; /* lx[2], ly[2]: points where it crosses edges */ X{ X /* global xmin, xmax, ymin, xmax */ X double ax = points[i-1].x; X double ay = points[i-1].y; X double bx = points[i].x; X double by = points[i].y; X double x, y; /* possible intersection point */ X BOOLEAN intersect = FALSE; X X if (by == ay) { X /* horizontal line */ X /* y coord must be in range, and line must span both xmin and xmax */ X /* note that spanning xmin implies spanning xmax */ X if (inrange(by, ymin, ymax) && inrange(xmin, ax, bx)) { X *lx++ = xmin; X *ly++ = by; X *lx++ = xmax; X *ly++ = by; X return(TRUE); X } else X return(FALSE); X } else if (bx == ax) { X /* vertical line */ X /* x coord must be in range, and line must span both ymin and ymax */ X /* note that spanning ymin implies spanning ymax */ X if (inrange(bx, xmin, xmax) && inrange(ymin, ay, by)) { X *lx++ = bx; X *ly++ = ymin; X *lx++ = bx; X *ly++ = ymax; X return(TRUE); X } else X return(FALSE); X } X X /* slanted line of some kind */ X /* there can be only zero or two intersections below */ X X /* does it intersect ymin edge */ X if (inrange(ymin, ay, by)) { X x = ax + (ymin-ay) * ((bx-ax) / (by-ay)); X if (inrange(x, xmin, xmax)) { X *lx++ = x; X *ly++ = ymin; X intersect = TRUE; X } X } X X /* does it intersect ymax edge */ X if (inrange(ymax, ay, by)) { X x = ax + (ymax-ay) * ((bx-ax) / (by-ay)); X if (inrange(x, xmin, xmax)) { X *lx++ = x; X *ly++ = ymax; X intersect = TRUE; X } X } X X /* does it intersect xmin edge */ X if (inrange(xmin, ax, bx)) { X y = ay + (xmin-ax) * ((by-ay) / (bx-ax)); X if (inrange(y, ymin, ymax)) { X *lx++ = xmin; X *ly++ = y; X intersect = TRUE; X } X } X X /* does it intersect xmax edge */ X if (inrange(xmax, ax, bx)) { X y = ay + (xmax-ax) * ((by-ay) / (bx-ax)); X if (inrange(y, ymin, ymax)) { X *lx++ = xmax; X *ly++ = y; X intersect = TRUE; X } X } X X if (intersect) X return(TRUE); X X /* It is possible for one or more of the [ab][xy] values to be -VERYLARGE. X * If ax=bx=-VERYLARGE or ay=by=-VERYLARGE we have already returned X * FALSE above. X * Note that for ax=ay=-VERYLARGE or bx=by=-VERYLARGE we can do nothing. X * Otherwise we fall through all the tests above. X * Handle them carefully here. As yet we have no way for them to be +VERYLARGE. X */ X if (ax == -VERYLARGE) { X if (ay != -VERYLARGE X && inrange(by, ymin, ymax) && inrange(xmax, ax, bx)) { X *lx++ = xmin; X *ly = by; X *lx++ = xmax; X *ly = by; X intersect = TRUE; X } X } else if (bx == -VERYLARGE) { X if (by != -VERYLARGE X && inrange(ay, ymin, ymax) && inrange(xmax, ax, bx)) { X *lx++ = xmin; X *ly = ay; X *lx++ = xmax; X *ly = ay; X intersect = TRUE; X } X } else if (ay == -VERYLARGE) { X /* note we know ax != -VERYLARGE */ X if (inrange(bx, xmin, xmax) && inrange(ymax, ay, by)) { X *lx++ = bx; X *ly = ymin; X *lx++ = bx; X *ly = ymax; X intersect = TRUE; X } X } else if (by == -VERYLARGE) { X /* note we know bx != -VERYLARGE */ X if (inrange(ax, xmin, xmax) && inrange(ymax, ay, by)) { X *lx++ = ax; X *ly = ymin; X *lx++ = ax; X *ly = ymax; X intersect = TRUE; X } X } X X return(intersect); X} X X/* Polar transform of all curves */ X/* Original code by John Campbell (CAMPBELL@NAUVAX.bitnet) */ Xpolar_xform (plots, pcount) X struct curve_points *plots; X int pcount; /* count of curves in plots array */ X{ X struct curve_points *this_plot; X int curve; /* loop var, for curves */ X register int i, p_cnt; /* loop/limit var, for points */ X struct coordinate *pnts; /* abbrev. for points array */ X double x, y; /* new cartesian value */ X BOOLEAN anydefined = FALSE; X X/* X Cycle through all the plots converting polar to rectangular. X If autoscaling, adjust max and mins. Ignore previous values. X If not autoscaling, use the yrange for both x and y ranges. X*/ X if (autoscale_ly) { X xmin = VERYLARGE; X ymin = VERYLARGE; X xmax = -VERYLARGE; X ymax = -VERYLARGE; X autoscale_lx = TRUE; X } else { X xmin = ymin; X xmax = ymax; X } X X this_plot = plots; X for (curve = 0; curve < pcount; this_plot = this_plot->next_cp, curve++) { X p_cnt = this_plot->p_count; X pnts = &(this_plot->points[0]); X X /* Convert to cartesian all points in this curve. */ X for (i = 0; i < p_cnt; i++) { X if (pnts[i].type != UNDEFINED) { X anydefined = TRUE; X x = pnts[i].y*cos(pnts[i].x); X y = pnts[i].y*sin(pnts[i].x); X pnts[i].x = x; X pnts[i].y = y; X if (autoscale_ly) { X if (xmin > x) xmin = x; X if (xmax < x) xmax = x; X if (ymin > y) ymin = y; X if (ymax < y) ymax = y; X pnts[i].type = INRANGE; X } else if(inrange(x, xmin, xmax) && inrange(y, ymin, ymax)) X pnts[i].type = INRANGE; X else X pnts[i].type = OUTRANGE; X } X } X } X X if (autoscale_lx && anydefined && fabs(xmax - xmin) < zero) { X /* This happens at least for the plot of 1/cos(x) (vertical line). */ X fprintf(stderr, "Warning: empty x range [%g:%g], ", xmin,xmax); X if (xmin == 0.0) { X xmin = -1; X xmax = 1; X } else { X xmin *= 0.9; X xmax *= 1.1; X } X fprintf(stderr, "adjusting to [%g:%g]\n", xmin,xmax); X } X if (autoscale_ly && anydefined && fabs(ymax - ymin) < zero) { X /* This happens at least for the plot of 1/sin(x) (horiz. line). */ X fprintf(stderr, "Warning: empty y range [%g:%g], ", ymin, ymax); X if (ymin == 0.0) { X ymin = -1; X ymax = 1; X } else { X ymin *= 0.9; X ymax *= 1.1; X } X fprintf(stderr, "adjusting to [%g:%g]\n", ymin, ymax); X } X} X X/* DRAW_YTICS: draw a regular tic series, y axis */ Xdraw_ytics(start, incr, end) X double start, incr, end; /* tic series definition */ X /* assume start < end, incr > 0 */ X{ X double ticplace; X int ltic; /* for mini log tics */ X double lticplace; /* for mini log tics */ X double ticmin, ticmax; /* for checking if tic is almost inrange */ X X if (end == VERYLARGE) /* for user-def series */ X end = max(ymin,ymax); X X /* limit to right side of plot */ X end = min(end, max(ymin,ymax)); X X /* to allow for rounding errors */ X ticmin = min(ymin,ymax) - SIGNIF*incr; X ticmax = max(ymin,ymax) + SIGNIF*incr; X end = end + SIGNIF*incr; X X for (ticplace = start; ticplace <= end; ticplace +=incr) { X if ( inrange(ticplace,ticmin,ticmax) ) X ytick(ticplace, yformat, incr, 1.0); X if (log_y && incr == 1.0) { X /* add mini-ticks to log scale ticmarks */ X for (ltic = 2; ltic <= 9; ltic++) { X lticplace = ticplace+log10((double)ltic); X if ( inrange(lticplace,ticmin,ticmax) ) X ytick(lticplace, (char *)NULL, incr, 0.5); X } X } X } X} X X X/* DRAW_XTICS: draw a regular tic series, x axis */ Xdraw_xtics(start, incr, end) X double start, incr, end; /* tic series definition */ X /* assume start < end, incr > 0 */ X{ X double ticplace; X int ltic; /* for mini log tics */ X double lticplace; /* for mini log tics */ X double ticmin, ticmax; /* for checking if tic is almost inrange */ X X if (end == VERYLARGE) /* for user-def series */ X end = max(xmin,xmax); X X /* limit to right side of plot */ X end = min(end, max(xmin,xmax)); X X /* to allow for rounding errors */ X ticmin = min(xmin,xmax) - SIGNIF*incr; X ticmax = max(xmin,xmax) + SIGNIF*incr; X end = end + SIGNIF*incr; X X for (ticplace = start; ticplace <= end; ticplace +=incr) { X if ( inrange(ticplace,ticmin,ticmax) ) X xtick(ticplace, xformat, incr, 1.0); X if (log_x && incr == 1.0) { X /* add mini-ticks to log scale ticmarks */ X for (ltic = 2; ltic <= 9; ltic++) { X lticplace = ticplace+log10((double)ltic); X if ( inrange(lticplace,ticmin,ticmax) ) X xtick(lticplace, (char *)NULL, incr, 0.5); X } X } X } X} X X/* DRAW_USER_YTICS: draw a user tic series, y axis */ Xdraw_user_ytics(list) X struct ticmark *list; /* list of tic marks */ X{ X double ticplace; X double incr = (ymax - ymin) / 10; X /* global xmin, xmax, xscale, ymin, ymax, yscale */ X X while (list != NULL) { X ticplace = list->position; X if ( inrange(ticplace, ymin, ymax) /* in range */ X || NearlyEqual(ticplace, ymin, incr) /* == ymin */ X || NearlyEqual(ticplace, ymax, incr)) /* == ymax */ X ytick(ticplace, list->label, incr, 1.0); X X list = list->next; X } X} X X/* DRAW_USER_XTICS: draw a user tic series, x axis */ Xdraw_user_xtics(list) X struct ticmark *list; /* list of tic marks */ X{ X double ticplace; X double incr = (xmax - xmin) / 10; X /* global xmin, xmax, xscale, ymin, ymax, yscale */ X X while (list != NULL) { X ticplace = list->position; X if ( inrange(ticplace, xmin, xmax) /* in range */ X || NearlyEqual(ticplace, xmin, incr) /* == xmin */ X || NearlyEqual(ticplace, xmax, incr)) /* == xmax */ X xtick(ticplace, list->label, incr, 1.0); X X list = list->next; X } X} X X/* draw and label a y-axis ticmark */ Xytick(place, text, spacing, ticscale) X double place; /* where on axis to put it */ X char *text; /* optional text label */ X double spacing; /* something to use with checkzero */ X float ticscale; /* scale factor for tic mark (0..1] */ X{ X register struct termentry *t = &term_tbl[term]; X char ticlabel[101]; X int ticsize = (int)((t->h_tic) * ticscale); X X place = CheckZero(place,spacing); /* to fix rounding error near zero */ X if (grid) { X (*t->linetype)(-1); /* axis line type */ X (*t->move)(xleft, map_y(place)); X (*t->vector)(xright, map_y(place)); X (*t->linetype)(-2); /* border linetype */ X } X if (tic_in) { X (*t->move)(xleft, map_y(place)); X (*t->vector)(xleft + ticsize, map_y(place)); X (*t->move)(xright, map_y(place)); X (*t->vector)(xright - ticsize, map_y(place)); X } else { X (*t->move)(xleft, map_y(place)); X (*t->vector)(xleft - ticsize, map_y(place)); X } X X /* label the ticmark */ X if (text) { X (void) sprintf(ticlabel, text, CheckLog(log_y, place)); X if ((*t->justify_text)(RIGHT)) { X (*t->put_text)(xleft-(t->h_char), X map_y(place), ticlabel); X } else { X (*t->put_text)(xleft-(t->h_char)*(strlen(ticlabel)+1), X map_y(place), ticlabel); X } X } X} X X/* draw and label an x-axis ticmark */ Xxtick(place, text, spacing, ticscale) X double place; /* where on axis to put it */ X char *text; /* optional text label */ X double spacing; /* something to use with checkzero */ X float ticscale; /* scale factor for tic mark (0..1] */ X{ X register struct termentry *t = &term_tbl[term]; X char ticlabel[101]; X int ticsize = (int)((t->v_tic) * ticscale); X X place = CheckZero(place,spacing); /* to fix rounding error near zero */ X if (grid) { X (*t->linetype)(-1); /* axis line type */ X (*t->move)(map_x(place), ybot); X (*t->vector)(map_x(place), ytop); X (*t->linetype)(-2); /* border linetype */ X } X if (tic_in) { X (*t->move)(map_x(place), ybot); X (*t->vector)(map_x(place), ybot + ticsize); X (*t->move)(map_x(place), ytop); X (*t->vector)(map_x(place), ytop - ticsize); X } else { X (*t->move)(map_x(place), ybot); X (*t->vector)(map_x(place), ybot - ticsize); X } X X /* label the ticmark */ X if (text) { X (void) sprintf(ticlabel, text, CheckLog(log_x, place)); X if ((*t->justify_text)(CENTRE)) { X (*t->put_text)(map_x(place), X ybot-(t->v_char), ticlabel); X } else { X (*t->put_text)(map_x(place)-(t->h_char)*strlen(ticlabel)/2, X ybot-(t->v_char), ticlabel); X } X } X} *-*-END-of-graphics.c-*-* echo x - internal.c sed 's/^X//' >internal.c <<'*-*-END-of-internal.c-*-*' X/* GNUPLOT - internal.c */ X/* X * Copyright (C) 1986, 1987, 1990 Thomas Williams, Colin Kelley X * X * Permission to use, copy, and distribute this software and its X * documentation for any purpose with or without fee is hereby granted, X * provided that the above copyright notice appear in all copies and X * that both that copyright notice and this permission notice appear X * in supporting documentation. X * X * Permission to modify the software is granted, but not the right to X * distribute the modified code. Modifications are to be distributed X * as patches to released version. X * X * This software is provided "as is" without express or implied warranty. X * X * X * AUTHORS X * X * Original Software: X * Thomas Williams, Colin Kelley. X * X * Gnuplot 2.0 additions: X * Russell Lang, Dave Kotz, John Campbell. X * X * send your comments or suggestions to (pixar!info-gnuplot@sun.com). X * X */ X X#include X#include X#include "plot.h" X XBOOLEAN undefined; X Xchar *strcpy(); X Xstruct value *pop(), *complex(), *integer(); Xdouble magnitude(), angle(), real(); X Xstruct value stack[STACK_DEPTH]; X Xint s_p = -1; /* stack pointer */ X X X/* X * System V and MSC 4.0 call this when they wants to print an error message. X * Don't! X */ X#ifdef MSDOS X#ifdef __TURBOC__ Xint matherr() /* Turbo C */ X#else Xint matherr(x) /* MSC 5.1 */ Xstruct exception *x; X#endif /* TURBOC */ X#else /* MSDOS */ Xint matherr() X#endif /* MSDOS */ X{ X return (undefined = TRUE); /* don't print error message */ X} X X Xreset_stack() X{ X s_p = -1; X} X X Xcheck_stack() /* make sure stack's empty */ X{ X if (s_p != -1) X fprintf(stderr,"\nwarning: internal error--stack not empty!\n"); X} X X Xstruct value *pop(x) Xstruct value *x; X{ X if (s_p < 0 ) X int_error("stack underflow",NO_CARET); X *x = stack[s_p--]; X return(x); X} X X Xpush(x) Xstruct value *x; X{ X if (s_p == STACK_DEPTH - 1) X int_error("stack overflow",NO_CARET); X stack[++s_p] = *x; X} X X X#define ERR_VAR "undefined variable: " X Xf_push(x) Xunion argument *x; /* contains pointer to value to push; */ X{ Xstatic char err_str[sizeof(ERR_VAR) + MAX_ID_LEN] = ERR_VAR; Xstruct udvt_entry *udv; X X udv = x->udv_arg; X if (udv->udv_undef) { /* undefined */ X (void) strcpy(&err_str[sizeof(ERR_VAR) - 1], udv->udv_name); X int_error(err_str,NO_CARET); X } X push(&(udv->udv_value)); X} X X Xf_pushc(x) Xunion argument *x; X{ X push(&(x->v_arg)); X} X X Xf_pushd(x) Xunion argument *x; X{ X push(&(x->udf_arg->dummy_value)); X} X X X#define ERR_FUN "undefined function: " X Xf_call(x) /* execute a udf */ Xunion argument *x; X{ Xstatic char err_str[sizeof(ERR_FUN) + MAX_ID_LEN] = ERR_FUN; Xregister struct udft_entry *udf; X X udf = x->udf_arg; X if (!udf->at) { /* undefined */ X (void) strcpy(&err_str[sizeof(ERR_FUN) - 1], X udf->udf_name); X int_error(err_str,NO_CARET); X } X (void) pop(&(udf->dummy_value)); X X execute_at(udf->at); X} X X Xstatic int_check(v) Xstruct value *v; X{ X if (v->type != INT) X int_error("non-integer passed to boolean operator",NO_CARET); X} X X Xf_lnot() X{ Xstruct value a; X int_check(pop(&a)); X push(integer(&a,!a.v.int_val) ); X} X X Xf_bnot() X{ Xstruct value a; X int_check(pop(&a)); X push( integer(&a,~a.v.int_val) ); X} X X Xf_bool() X{ /* converts top-of-stack to boolean */ X int_check(&top_of_stack); X top_of_stack.v.int_val = !!top_of_stack.v.int_val; X} X X Xf_lor() X{ Xstruct value a,b; X int_check(pop(&b)); X int_check(pop(&a)); X push( integer(&a,a.v.int_val || b.v.int_val) ); X} X Xf_land() X{ Xstruct value a,b; X int_check(pop(&b)); X int_check(pop(&a)); X push( integer(&a,a.v.int_val && b.v.int_val) ); X} X X Xf_bor() X{ Xstruct value a,b; X int_check(pop(&b)); X int_check(pop(&a)); X push( integer(&a,a.v.int_val | b.v.int_val) ); X} X X Xf_xor() X{ Xstruct value a,b; X int_check(pop(&b)); X int_check(pop(&a)); X push( integer(&a,a.v.int_val ^ b.v.int_val) ); X} X X Xf_band() X{ Xstruct value a,b; X int_check(pop(&b)); X int_check(pop(&a)); X push( integer(&a,a.v.int_val & b.v.int_val) ); X} X X Xf_uminus() X{ Xstruct value a; X (void) pop(&a); X switch(a.type) { X case INT: X a.v.int_val = -a.v.int_val; X break; X case CMPLX: X a.v.cmplx_val.real = X -a.v.cmplx_val.real; X a.v.cmplx_val.imag = X -a.v.cmplx_val.imag; X } X push(&a); X} X X Xf_eq() /* note: floating point equality is rare because of roundoff error! */ X{ Xstruct value a, b; X register int result; X (void) pop(&b); X (void) pop(&a); X switch(a.type) { X case INT: X switch (b.type) { X case INT: X result = (a.v.int_val == X b.v.int_val); X break; X case CMPLX: X result = (a.v.int_val == X b.v.cmplx_val.real && X b.v.cmplx_val.imag == 0.0); X } X break; X case CMPLX: X switch (b.type) { X case INT: X result = (b.v.int_val == a.v.cmplx_val.real && X a.v.cmplx_val.imag == 0.0); X break; X case CMPLX: X result = (a.v.cmplx_val.real== X b.v.cmplx_val.real && X a.v.cmplx_val.imag== X b.v.cmplx_val.imag); X } X } X push(integer(&a,result)); X} X X Xf_ne() X{ Xstruct value a, b; X register int result; X (void) pop(&b); X (void) pop(&a); X switch(a.type) { X case INT: X switch (b.type) { X case INT: X result = (a.v.int_val != X b.v.int_val); X break; X case CMPLX: X result = (a.v.int_val != X b.v.cmplx_val.real || X b.v.cmplx_val.imag != 0.0); X } X break; X case CMPLX: X switch (b.type) { X case INT: X result = (b.v.int_val != X a.v.cmplx_val.real || X a.v.cmplx_val.imag != 0.0); X break; X case CMPLX: X result = (a.v.cmplx_val.real != X b.v.cmplx_val.real || X a.v.cmplx_val.imag != X b.v.cmplx_val.imag); X } X } X push(integer(&a,result)); X} X X Xf_gt() X{ Xstruct value a, b; X register int result; X (void) pop(&b); X (void) pop(&a); X switch(a.type) { X case INT: X switch (b.type) { X case INT: X result = (a.v.int_val > X b.v.int_val); X break; X case CMPLX: X result = (a.v.int_val > X b.v.cmplx_val.real); X } X break; X case CMPLX: X switch (b.type) { X case INT: X result = (a.v.cmplx_val.real > X b.v.int_val); X break; X case CMPLX: X result = (a.v.cmplx_val.real > X b.v.cmplx_val.real); X } X } X push(integer(&a,result)); X} X X Xf_lt() X{ Xstruct value a, b; X register int result; X (void) pop(&b); X (void) pop(&a); X switch(a.type) { X case INT: X switch (b.type) { X case INT: X result = (a.v.int_val < X b.v.int_val); X break; X case CMPLX: X result = (a.v.int_val < X b.v.cmplx_val.real); X } X break; X case CMPLX: X switch (b.type) { X case INT: X result = (a.v.cmplx_val.real < X b.v.int_val); X break; X case CMPLX: X result = (a.v.cmplx_val.real < X b.v.cmplx_val.real); X } X } X push(integer(&a,result)); X} X X Xf_ge() X{ Xstruct value a, b; X register int result; X (void) pop(&b); X (void) pop(&a); X switch(a.type) { X case INT: X switch (b.type) { X case INT: X result = (a.v.int_val >= X b.v.int_val); X break; X case CMPLX: X result = (a.v.int_val >= X b.v.cmplx_val.real); X } X break; X case CMPLX: X switch (b.type) { X case INT: X result = (a.v.cmplx_val.real >= X b.v.int_val); X break; X case CMPLX: X result = (a.v.cmplx_val.real >= X b.v.cmplx_val.real); X } X } X push(integer(&a,result)); X} X X Xf_le() X{ Xstruct value a, b; X register int result; X (void) pop(&b); X (void) pop(&a); X switch(a.type) { X case INT: X switch (b.type) { X case INT: X result = (a.v.int_val <= X b.v.int_val); X break; X case CMPLX: X result = (a.v.int_val <= X b.v.cmplx_val.real); X } X break; X case CMPLX: X switch (b.type) { X case INT: X result = (a.v.cmplx_val.real <= X b.v.int_val); X break; X case CMPLX: X result = (a.v.cmplx_val.real <= X b.v.cmplx_val.real); X } X } X push(integer(&a,result)); X} X X Xf_plus() X{ Xstruct value a, b, result; X (void) pop(&b); X (void) pop(&a); X switch(a.type) { X case INT: X switch (b.type) { X case INT: X (void) integer(&result,a.v.int_val + X b.v.int_val); X break; X case CMPLX: X (void) complex(&result,a.v.int_val + X b.v.cmplx_val.real, X b.v.cmplx_val.imag); X } X break; X case CMPLX: X switch (b.type) { X case INT: X (void) complex(&result,b.v.int_val + X a.v.cmplx_val.real, X a.v.cmplx_val.imag); X break; X case CMPLX: X (void) complex(&result,a.v.cmplx_val.real+ X b.v.cmplx_val.real, X a.v.cmplx_val.imag+ X b.v.cmplx_val.imag); X } X } X push(&result); X} X X Xf_minus() X{ Xstruct value a, b, result; X (void) pop(&b); X (void) pop(&a); /* now do a - b */ X switch(a.type) { X case INT: X switch (b.type) { X case INT: X (void) integer(&result,a.v.int_val - X b.v.int_val); X break; X case CMPLX: X (void) complex(&result,a.v.int_val - X b.v.cmplx_val.real, X -b.v.cmplx_val.imag); X } X break; X case CMPLX: X switch (b.type) { X case INT: X (void) complex(&result,a.v.cmplx_val.real - X b.v.int_val, X a.v.cmplx_val.imag); X break; X case CMPLX: X (void) complex(&result,a.v.cmplx_val.real- X b.v.cmplx_val.real, X a.v.cmplx_val.imag- X b.v.cmplx_val.imag); X } X } X push(&result); X} X X Xf_mult() X{ Xstruct value a, b, result; X (void) pop(&b); X (void) pop(&a); /* now do a*b */ X X switch(a.type) { X case INT: X switch (b.type) { X case INT: X (void) integer(&result,a.v.int_val * X b.v.int_val); X break; X case CMPLX: X (void) complex(&result,a.v.int_val * X b.v.cmplx_val.real, X a.v.int_val * X b.v.cmplx_val.imag); X } X break; X case CMPLX: X switch (b.type) { X case INT: X (void) complex(&result,b.v.int_val * X a.v.cmplx_val.real, X b.v.int_val * X a.v.cmplx_val.imag); X break; X case CMPLX: X (void) complex(&result,a.v.cmplx_val.real* X b.v.cmplx_val.real- X a.v.cmplx_val.imag* X b.v.cmplx_val.imag, X a.v.cmplx_val.real* X b.v.cmplx_val.imag+ X a.v.cmplx_val.imag* X b.v.cmplx_val.real); X } X } X push(&result); X} X X Xf_div() X{ Xstruct value a, b, result; Xregister double square; X (void) pop(&b); X (void) pop(&a); /* now do a/b */ X X switch(a.type) { X case INT: X switch (b.type) { X case INT: X if (b.v.int_val) X (void) integer(&result,a.v.int_val / X b.v.int_val); X else { X (void) integer(&result,0); X undefined = TRUE; X } X break; X case CMPLX: X square = b.v.cmplx_val.real* X b.v.cmplx_val.real + X b.v.cmplx_val.imag* X b.v.cmplx_val.imag; X if (square) X (void) complex(&result,a.v.int_val* X b.v.cmplx_val.real/square, X -a.v.int_val* X b.v.cmplx_val.imag/square); X else { X (void) complex(&result,0.0,0.0); X undefined = TRUE; X } X } X break; X case CMPLX: X switch (b.type) { X case INT: X if (b.v.int_val) X X (void) complex(&result,a.v.cmplx_val.real/ X b.v.int_val, X a.v.cmplx_val.imag/ X b.v.int_val); X else { X (void) complex(&result,0.0,0.0); X undefined = TRUE; X } X break; X case CMPLX: X square = b.v.cmplx_val.real* X b.v.cmplx_val.real + X b.v.cmplx_val.imag* X b.v.cmplx_val.imag; X if (square) X (void) complex(&result,(a.v.cmplx_val.real* X b.v.cmplx_val.real+ X a.v.cmplx_val.imag* X b.v.cmplx_val.imag)/square, X (a.v.cmplx_val.imag* X b.v.cmplx_val.real- X a.v.cmplx_val.real* X b.v.cmplx_val.imag)/ X square); X else { X (void) complex(&result,0.0,0.0); X undefined = TRUE; X } X } X } X push(&result); X} X X Xf_mod() X{ Xstruct value a, b; X (void) pop(&b); X (void) pop(&a); /* now do a%b */ X X if (a.type != INT || b.type != INT) X int_error("can only mod ints",NO_CARET); X if (b.v.int_val) X push(integer(&a,a.v.int_val % b.v.int_val)); X else { X push(integer(&a,0)); X undefined = TRUE; X } X} X X Xf_power() X{ Xstruct value a, b, result; Xregister int i, t, count; Xregister double mag, ang; X (void) pop(&b); X (void) pop(&a); /* now find a**b */ X X switch(a.type) { X case INT: X switch (b.type) { X case INT: X count = abs(b.v.int_val); X t = 1; X for(i = 0; i < count; i++) X t *= a.v.int_val; X if (b.v.int_val >= 0) X (void) integer(&result,t); X else X (void) complex(&result,1.0/t,0.0); X break; X case CMPLX: X mag = X pow(magnitude(&a),fabs(b.v.cmplx_val.real)); X if (b.v.cmplx_val.real < 0.0) X mag = 1.0/mag; X ang = angle(&a)*b.v.cmplx_val.real+ X b.v.cmplx_val.imag; X (void) complex(&result,mag*cos(ang), X mag*sin(ang)); X } X break; X case CMPLX: X switch (b.type) { X case INT: X if (a.v.cmplx_val.imag == 0.0) { X mag = pow(a.v.cmplx_val.real,(double)abs(b.v.int_val)); X if (b.v.int_val < 0) X mag = 1.0/mag; X (void) complex(&result,mag,0.0); X } X else { X /* not so good, but...! */ X mag = pow(magnitude(&a),(double)abs(b.v.int_val)); X if (b.v.int_val < 0) X mag = 1.0/mag; X ang = angle(&a)*b.v.int_val; X (void) complex(&result,mag*cos(ang), X mag*sin(ang)); X } X break; X case CMPLX: X mag = pow(magnitude(&a),fabs(b.v.cmplx_val.real)); X if (b.v.cmplx_val.real < 0.0) X mag = 1.0/mag; X ang = angle(&a)*b.v.cmplx_val.real+ b.v.cmplx_val.imag; X (void) complex(&result,mag*cos(ang), X mag*sin(ang)); X } X } X push(&result); X} X X Xf_factorial() X{ Xstruct value a; Xregister int i; Xregister double val; X X (void) pop(&a); /* find a! (factorial) */ X X switch (a.type) { X case INT: X val = 1.0; X for (i = a.v.int_val; i > 1; i--) /*fpe's should catch overflows*/ X val *= i; X break; X default: X int_error("factorial (!) argument must be an integer", X NO_CARET); X } X X push(complex(&a,val,0.0)); X X} X X Xint Xf_jump(x) Xunion argument *x; X{ X return(x->j_arg); X} X X Xint Xf_jumpz(x) Xunion argument *x; X{ Xstruct value a; X int_check(&top_of_stack); X if (top_of_stack.v.int_val) { /* non-zero */ X (void) pop(&a); X return 1; /* no jump */ X } X else X return(x->j_arg); /* leave the argument on TOS */ X} X X Xint Xf_jumpnz(x) Xunion argument *x; X{ Xstruct value a; X int_check(&top_of_stack); X if (top_of_stack.v.int_val) /* non-zero */ X return(x->j_arg); /* leave the argument on TOS */ X else { X (void) pop(&a); X return 1; /* no jump */ X } X} X X Xint Xf_jtern(x) Xunion argument *x; X{ Xstruct value a; X X int_check(pop(&a)); X if (a.v.int_val) X return(1); /* no jump; fall through to TRUE code */ X else X return(x->j_arg); /* go jump to FALSE code */ X} *-*-END-of-internal.c-*-* exit