Annotation of OpenXM_contrib/gnuplot/graphics.c, Revision 1.1
1.1 ! maekawa 1: #ifndef lint
! 2: static char *RCSid = "$Id: graphics.c,v 1.138 1998/06/18 14:55:08 ddenholm Exp $";
! 3: #endif
! 4:
! 5: /* GNUPLOT - graphics.c */
! 6:
! 7: /*[
! 8: * Copyright 1986 - 1993, 1998 Thomas Williams, Colin Kelley
! 9: *
! 10: * Permission to use, copy, and distribute this software and its
! 11: * documentation for any purpose with or without fee is hereby granted,
! 12: * provided that the above copyright notice appear in all copies and
! 13: * that both that copyright notice and this permission notice appear
! 14: * in supporting documentation.
! 15: *
! 16: * Permission to modify the software is granted, but not the right to
! 17: * distribute the complete modified source code. Modifications are to
! 18: * be distributed as patches to the released version. Permission to
! 19: * distribute binaries produced by compiling modified sources is granted,
! 20: * provided you
! 21: * 1. distribute the corresponding source modifications from the
! 22: * released version in the form of a patch file along with the binaries,
! 23: * 2. add special version identification to distinguish your version
! 24: * in addition to the base release version number,
! 25: * 3. provide your name and address as the primary contact for the
! 26: * support of your modified version, and
! 27: * 4. retain our contact information in regard to use of the base
! 28: * software.
! 29: * Permission to distribute the released version of the source code along
! 30: * with corresponding source modifications in the form of a patch file is
! 31: * granted with same provisions 2 through 4 for binary distributions.
! 32: *
! 33: * This software is provided "as is" without express or implied warranty
! 34: * to the extent permitted by applicable law.
! 35: ]*/
! 36:
! 37:
! 38: #include "plot.h"
! 39: #include "setshow.h"
! 40:
! 41: /* key placement is calculated in boundary, so we need file-wide variables
! 42: * To simplify adjustments to the key, we set all these once [depends on
! 43: * key_reverse] and use them throughout.
! 44: */
! 45:
! 46: /*{{{ local and global variables */
! 47: static int key_sample_width; /* width of line sample */
! 48: static int key_sample_left; /* offset from x for left of line sample */
! 49: static int key_sample_right; /* offset from x for right of line sample */
! 50: static int key_point_offset; /* offset from x for point sample */
! 51: static int key_text_left; /* offset from x for left-justified text */
! 52: static int key_text_right; /* offset from x for right-justified text */
! 53: static int key_size_left; /* size of left bit of key (text or sample, depends on key_reverse) */
! 54: static int key_size_right; /* size of right part of key (including padding) */
! 55:
! 56: /* I think the following should also be static ?? */
! 57:
! 58: static int key_xl, key_xr, key_yt, key_yb; /* boundarys for key field */
! 59: static int max_ptitl_len = 0; /* max length of plot-titles (keys) */
! 60: static int ktitl_lines = 0; /* no lines in key_title (key header) */
! 61: static int ptitl_cnt; /* count keys with len > 0 */
! 62: static int key_cols; /* no cols of keys */
! 63: static int key_rows, key_col_wth, yl_ref;
! 64:
! 65:
! 66: /* penalty for doing tics by callback in gen_tics is need for
! 67: * global variables to communicate with the tic routines
! 68: * Dont need to be arrays for this
! 69: */
! 70: static int tic_start, tic_direction, tic_text, rotate_tics, tic_hjust, tic_vjust, tic_mirror;
! 71:
! 72: /* set by tic_callback - how large to draw polar radii */
! 73: static double largest_polar_circle;
! 74:
! 75: /* either xformat etc or invented time format
! 76: * index with FIRST_X_AXIS etc
! 77: * global because used in gen_tics, which graph3d also uses
! 78: */
! 79: char ticfmt[8][MAX_ID_LEN+1]; /* HBB 990106: fix buffer overrun */
! 80: int timelevel[8];
! 81: double ticstep[8];
! 82:
! 83: static int xlablin, x2lablin, ylablin, y2lablin, titlelin, xticlin, x2ticlin;
! 84:
! 85: static int key_entry_height; /* bigger of t->v_size, pointsize*t->v_tick */
! 86: static int p_width, p_height; /* pointsize * { t->h_tic | t->v_tic } */
! 87:
! 88:
! 89: /* there are several things on right of plot - key, y2tics and y2label
! 90: * when working out boundary, save posn of y2label for later...
! 91: * Same goes for x2label.
! 92: * key posn is also stored in key_xl, and tics go at xright
! 93: */
! 94: static int ylabel_x, y2label_x, xlabel_y, x2label_y, title_y, time_y, time_x;
! 95: static int ylabel_y, y2label_y, xtic_y, x2tic_y, ytic_x, y2tic_x;
! 96: /*}}} */
! 97:
! 98: /*{{{ static fns and local macros */
! 99: static void plot_impulses __PROTO((struct curve_points * plot, int yaxis_x,
! 100: int xaxis_y));
! 101: static void plot_lines __PROTO((struct curve_points * plot));
! 102: static void plot_points __PROTO((struct curve_points * plot));
! 103: static void plot_dots __PROTO((struct curve_points * plot));
! 104: static void plot_bars __PROTO((struct curve_points * plot));
! 105: static void plot_boxes __PROTO((struct curve_points * plot, int xaxis_y));
! 106: static void plot_vectors __PROTO((struct curve_points * plot));
! 107: static void plot_f_bars __PROTO((struct curve_points * plot));
! 108: static void plot_c_bars __PROTO((struct curve_points * plot));
! 109:
! 110: static void edge_intersect __PROTO((struct coordinate GPHUGE * points, int i,
! 111: double *ex, double *ey));
! 112: static int two_edge_intersect __PROTO((struct coordinate GPHUGE * points,
! 113: int i, double *lx, double *ly));
! 114: static TBOOLEAN two_edge_intersect_steps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));
! 115:
! 116: static void plot_steps __PROTO((struct curve_points * plot)); /* JG */
! 117: static void plot_fsteps __PROTO((struct curve_points * plot)); /* HOE */
! 118: static void plot_histeps __PROTO((struct curve_points * plot)); /* CAC */
! 119: static void histeps_horizontal __PROTO((int *xl, int *yl, double x1, double x2,
! 120: double y)); /* CAC */
! 121: static void histeps_vertical __PROTO((int *xl, int *yl, double x, double y1,
! 122: double y2)); /* CAC */
! 123: static void edge_intersect_steps __PROTO((struct coordinate GPHUGE * points,
! 124: int i, double *ex, double *ey)); /* JG */
! 125: static void edge_intersect_fsteps __PROTO((struct coordinate GPHUGE * points,
! 126: int i, double *ex, double *ey)); /* HOE */
! 127: static TBOOLEAN two_edge_intersect_steps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly)); /* JG */
! 128: static TBOOLEAN two_edge_intersect_fsteps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));
! 129:
! 130: static double LogScale __PROTO((double coord, int is_log, double log_base_log,
! 131: char *what, char *axis));
! 132: static double dbl_raise __PROTO((double x, int y));
! 133: static void boundary __PROTO((int scaling, struct curve_points * plots,
! 134: int count));
! 135: static double make_tics __PROTO((int axis, int guide));
! 136:
! 137: /* widest2d_callback keeps longest so far in here */
! 138: static int widest_tic;
! 139:
! 140: static void widest2d_callback __PROTO((int axis, double place, char *text,
! 141: struct lp_style_type grid));
! 142: static void ytick2d_callback __PROTO((int axis, double place, char *text,
! 143: struct lp_style_type grid));
! 144: static void xtick2d_callback __PROTO((int axis, double place, char *text,
! 145: struct lp_style_type grid));
! 146: static void map_position __PROTO((struct position * pos, unsigned int *x,
! 147: unsigned int *y, char *what));
! 148: static void mant_exp __PROTO((double log_base, double x, int scientific,
! 149: double *m, int *p));
! 150: static void gprintf __PROTO((char *dest, size_t count, char *format,
! 151: double log_base, double x));
! 152:
! 153: #if defined(sun386) || defined(AMIGA_SC_6_1)
! 154: static double CheckLog __PROTO((TBOOLEAN is_log, double base_log, double x));
! 155: #endif
! 156:
! 157: /* for plotting error bars
! 158: * half the width of error bar tic mark
! 159: */
! 160: #define ERRORBARTIC (t->h_tic/2)
! 161:
! 162: /*
! 163: * The Amiga SAS/C 6.2 compiler moans about macro envocations causing
! 164: * multiple calls to functions. I converted these macros to inline
! 165: * functions coping with the problem without loosing speed.
! 166: * If your compiler supports __inline, you should add it to the
! 167: * #ifdef directive
! 168: * (MGR, 1993)
! 169: */
! 170:
! 171: #ifdef AMIGA_SC_6_1
! 172: GP_INLINE static TBOOLEAN i_inrange(int z, int min, int max)
! 173: {
! 174: return ((min < max) ? ((z >= min) && (z <= max)) : ((z >= max) && (z <= min)));
! 175: }
! 176:
! 177: GP_INLINE static double f_max(double a, double b)
! 178: {
! 179: return (GPMAX(a, b));
! 180: }
! 181:
! 182: GP_INLINE static double f_min(double a, double b)
! 183: {
! 184: return (GPMIN(a, b));
! 185: }
! 186:
! 187: #else
! 188: #define f_max(a,b) GPMAX((a),(b))
! 189: #define f_min(a,b) GPMIN((a),(b))
! 190: #define i_inrange(z,a,b) inrange((z),(a),(b))
! 191: #endif
! 192:
! 193: /* True if a and b have the same sign or zero (positive or negative) */
! 194: #define samesign(a,b) ((a) * (b) >= 0)
! 195: /*}}} */
! 196:
! 197: /*{{{ more variables */
! 198: /* Define the boundary of the plot
! 199: * These are computed at each call to do_plot, and are constant over
! 200: * the period of one do_plot. They actually only change when the term
! 201: * type changes and when the 'set size' factors change.
! 202: * - no longer true, for 'set key out' or 'set key under'. also depend
! 203: * on tic marks and multi-line labels.
! 204: * They are shared with graph3d.c since we want to use its draw_clip_line()
! 205: */
! 206: int xleft, xright, ybot, ytop;
! 207:
! 208:
! 209: /* we make a local copy of the 'key' variable so that if something
! 210: * goes wrong, we can switch it off temporarily
! 211: */
! 212:
! 213: static int lkey;
! 214:
! 215: /* First attempt at double axes...
! 216: * x_min etc are now accessed from a global array min_array[], max_array[]
! 217: * put the scale factors into a similar array
! 218: * for convenience in this first attack on double axes, just define x_min etc
! 219: * since code already uses x_min, etc Eventually it will be done properly
! 220: */
! 221:
! 222:
! 223: extern double min_array[], max_array[];
! 224: extern int auto_array[];
! 225:
! 226: extern int log_array[];
! 227: extern double base_array[], log_base_array[];
! 228:
! 229: static int x_axis = FIRST_X_AXIS, y_axis = FIRST_Y_AXIS; /* current axes */
! 230:
! 231: static double scale[AXIS_ARRAY_SIZE]; /* scale factors for mapping for each axis */
! 232:
! 233: /* BODGES BEFORE I FIX IT UP */
! 234: #define x_min min_array[x_axis]
! 235: #define x_max max_array[x_axis]
! 236: #define y_min min_array[y_axis]
! 237: #define y_max max_array[y_axis]
! 238:
! 239: /* And the functions to map from user to terminal coordinates */
! 240: /* maps floating point x to screen */
! 241: #define map_x(x) (int)(xleft+(x-min_array[x_axis])*scale[x_axis]+0.5)
! 242: /* same for y */
! 243: #define map_y(y) (int)(ybot +(y-min_array[y_axis])*scale[y_axis]+0.5)
! 244:
! 245: /* (DFK) Watch for cancellation error near zero on axes labels */
! 246: /* less than one hundredth of a tic mark */
! 247: #define SIGNIF (0.01)
! 248: #define CheckZero(x,tic) (fabs(x) < ((tic) * SIGNIF) ? 0.0 : (x))
! 249: #define NearlyEqual(x,y,tic) (fabs((x)-(y)) < ((tic) * SIGNIF))
! 250: /*}}} */
! 251:
! 252: /*{{{ CheckLog() */
! 253: /* (DFK) For some reason, the Sun386i compiler screws up with the CheckLog
! 254: * macro, so I write it as a function on that machine.
! 255: *
! 256: * Amiga SAS/C 6.2 thinks it will do too much work calling functions in
! 257: * macro arguments twice, thus I inline theese functions. (MGR, 1993)
! 258: * If your compiler doesn't handle those macros correctly, you should
! 259: * also subscribe here. Even without inlining you gain speed with log plots
! 260: */
! 261: #if defined(sun386) || defined(AMIGA_SC_6_1)
! 262: GP_INLINE static double CheckLog(is_log, base_log, x)
! 263: TBOOLEAN is_log;
! 264: double base_log;
! 265: double x;
! 266: {
! 267: if (is_log)
! 268: return (pow(base_log, x));
! 269: else
! 270: return (x);
! 271: }
! 272: #else
! 273: /* (DFK) Use 10^x if logscale is in effect, else x */
! 274: #define CheckLog(is_log, base_log, x) ((is_log) ? pow(base_log, (x)) : (x))
! 275: #endif /* sun386 || SAS/C */
! 276: /*}}} */
! 277:
! 278: /*{{{ LogScale() */
! 279: static double LogScale(coord, is_log, log_base_log, what, axis)
! 280: double coord; /* the value */
! 281: TBOOLEAN is_log; /* is this axis in logscale? */
! 282: double log_base_log; /* if so, the log of its base */
! 283: char *what; /* what is the coord for? */
! 284: char *axis; /* which axis is this for ("x" or "y")? */
! 285: {
! 286: if (is_log) {
! 287: if (coord <= 0.0) {
! 288: char errbuf[100]; /* place to write error message */
! 289: (void) sprintf(errbuf, "%s has %s coord of %g; must be above 0 for log scale!",
! 290: what, axis, coord);
! 291: graph_error(errbuf);
! 292: } else
! 293: return (log(coord) / log_base_log);
! 294: }
! 295: return (coord);
! 296: }
! 297: /*}}} */
! 298:
! 299: /*{{{ graph_error() */
! 300: /* handle errors during graph-plot in a consistent way */
! 301: void graph_error(text)
! 302: char *text;
! 303: {
! 304: multiplot = FALSE;
! 305: term_end_plot();
! 306: int_error(text, NO_CARET);
! 307: }
! 308:
! 309: /*}}} */
! 310:
! 311:
! 312: /*{{{ fixup_range() */
! 313: /*
! 314: * === SYNOPSIS ===
! 315: *
! 316: * This function checks whether the data and/or plot range in a given axis
! 317: * is too small (which would cause divide-by-zero and/or infinite-loop
! 318: * problems later on). If so,
! 319: * - if autoscaling is in effect for this axis, we widen the range
! 320: * - otherwise, we abort with a call to int_error() (which prints out
! 321: * a suitable error message, then (hopefully) aborts this command and
! 322: * returns to the command prompt or whatever).
! 323: *
! 324: *
! 325: * === HISTORY AND DESIGN NOTES ===
! 326: *
! 327: * 1998 Oct 4, Jonathan Thornburg <jthorn@galileo.thp.univie.ac.at>
! 328: *
! 329: * This function used to be a (long) macro FIXUP_RANGE(AXIS, WHICH)
! 330: * which was (identically!) defined in plot2d.c and plot3d.c . As
! 331: * well as now being a function instead of a macro, the logic is also
! 332: * changed: The "too small" range test no longer depends on 'set zero'
! 333: * and is now properly scaled relative to the data magnitude.
! 334: *
! 335: * The key question in designing this function is the policy for just how
! 336: * much to widen the data range by, as a function of the data magnitude.
! 337: * This is to some extent a matter of taste. IMHO the key criterion is
! 338: * that (at least) all of the following should (a) not infinite-loop, and
! 339: * (b) give correct plots, regardless of the 'set zero' setting:
! 340: * plot 6.02e23 # a huge number >> 1 / FP roundoff level
! 341: * plot 3 # a "reasonable-sized" number
! 342: * plot 1.23e-12 # a small number still > FP roundoff level
! 343: * plot 1.23e-12 * sin(x) # a small function still > FP roundoff level
! 344: * plot 1.23e-45 # a tiny number << FP roundoff level
! 345: * plot 1.23e-45 * sin(x) # a tiny function << FP roundoff level
! 346: * plot 0 # or (more commonly) a data file of all zeros
! 347: * That is, IMHO gnuplot should *never* infinite-loop, and it should *never*
! 348: * producing an incorrect or misleading plot. In contrast, the old code
! 349: * would infinite-loop on most of these examples with 'set zero 0.0' in
! 350: * effect, or would plot the small-amplitude sine waves as the zero function
! 351: * with 'zero' set larger than the sine waves' amplitude.
! 352: *
! 353: * The current code plots all the above examples correctly and without
! 354: * infinite looping.
! 355: *
! 356: *
! 357: * === USAGE ===
! 358: *
! 359: * Arguments:
! 360: * axis = (in) An integer specifying which axis (x1, x2, y1, y2, z, etc)
! 361: * we should do our stuff for. We use this argument as an
! 362: * index into the global arrays {min,max,auto}_array . In
! 363: * practice this argument will typically be one of the constants
! 364: * {FIRST,SECOND}_{X,Y,Z}_AXIS defined in plot.h.
! 365: * axis_name --> (in) This argument should point to the character string
! 366: * name corresponding to axis , e.g. "x", "y2", etc.
! 367: * We use this (only) in formatting warning/error messages.
! 368: *
! 369: * Global Variables:
! 370: * auto_array[axis] = (in) (defined in command.c) Bit-flags which tell
! 371: * [in some manner I don't fully understand :=( ]
! 372: * whether and/or how autoscaling is in effect for
! 373: * this axis.
! 374: * {min,max}_array = (in out) (defined in command.c) The data ranges which
! 375: * this function manipulates.
! 376: * c_token = (in) (defined in plot.h) Used in formatting an error message.
! 377: *
! 378: * Bugs:
! 379: * - If strlen(axis_name) > strlen("%s") , we may overflow an
! 380: * error-message buffer, which would be A Bad Thing. Caveat caller...
! 381: */
! 382: void fixup_range(axis, axis_name)
! 383: int axis;
! 384: char *axis_name;
! 385: {
! 386: #define MAX_AXIS_NAME_LEN 2 /* max legal strlen(axis_name) */
! 387:
! 388: /* These two macro definitions set the range-widening policy: */
! 389: #define FIXUP_RANGE__WIDEN_ZERO_ABS 1.0 /* widen [0:0] by
! 390: +/- this absolute amount */
! 391: #define FIXUP_RANGE__WIDEN_NONZERO_REL 0.01 /* widen [nonzero:nonzero] by
! 392: -/+ this relative amount */
! 393:
! 394: double dmin = min_array[axis];
! 395: double dmax = max_array[axis];
! 396: if (dmax - dmin == 0.0) {
! 397: /* empty range */
! 398: if (auto_array[axis]) {
! 399: /* range came from autoscaling ==> widen it */
! 400: double widen = (dmax == 0.0)
! 401: ? FIXUP_RANGE__WIDEN_ZERO_ABS
! 402: : FIXUP_RANGE__WIDEN_NONZERO_REL * dmax;
! 403: fprintf(stderr,
! 404: "Warning: empty %s range [%g:%g], ",
! 405: axis_name, dmin, dmax);
! 406: min_array[axis] -= widen;
! 407: max_array[axis] += widen;
! 408: fprintf(stderr,
! 409: "adjusting to [%g:%g]\n",
! 410: min_array[axis], max_array[axis]);
! 411: } else {
! 412: /* user has explicitly set the range */
! 413: /* (to something empty) ==> we're in trouble */
! 414: char msg_buffer[MAX_LINE_LEN + 1];
! 415: sprintf(msg_buffer, "Can't plot with an empty %s range!", axis_name);
! 416: int_error(msg_buffer, c_token); /* never returns */
! 417: }
! 418: }
! 419: }
! 420:
! 421: /*}}} */
! 422:
! 423:
! 424: /*{{{ widest2d_callback() */
! 425: /* we determine widest tick label by getting gen_ticks to call this
! 426: * routine with every label
! 427: */
! 428:
! 429: static void widest2d_callback(axis, place, text, grid)
! 430: int axis;
! 431: double place;
! 432: char *text;
! 433: struct lp_style_type grid;
! 434: {
! 435: int len = label_width(text, NULL);
! 436: if (len > widest_tic)
! 437: widest_tic = len;
! 438: }
! 439: /*}}} */
! 440:
! 441:
! 442: /*{{{ boundary() */
! 443: /* borders of plotting area
! 444: * computed once on every call to do_plot
! 445: *
! 446: * The order in which things is done is getting pretty critical:
! 447: * ytop depends on title, x2label, ylabels (if no rotated text)
! 448: * ybot depends on key, if TUNDER
! 449: * once we have these, we can setup the y1 and y2 tics and the
! 450: * only then can we calculate xleft and xright
! 451: * xright depends also on key TRIGHT
! 452: * then we can do x and x2 tics
! 453: *
! 454: * For set size ratio ..., everything depends on everything else...
! 455: * not really a lot we can do about that, so we lose if the plot has to
! 456: * be reduced vertically. But the chances are the
! 457: * change will not be very big, so the number of tics will not
! 458: * change dramatically.
! 459: *
! 460: * Margin computation redone by Dick Crawford (rccrawford@lanl.gov) 4/98
! 461: */
! 462:
! 463: static void boundary(scaling, plots, count)
! 464: TBOOLEAN scaling; /* TRUE if terminal is doing the scaling */
! 465: struct curve_points *plots;
! 466: int count;
! 467: {
! 468: int ytlen;
! 469: int yticlin = 0, y2ticlin = 0, timelin = 0;
! 470:
! 471: register struct termentry *t = term;
! 472: int key_h, key_w;
! 473: int can_rotate = (*t->text_angle) (1);
! 474:
! 475: int xtic_textheight; /* height of xtic labels */
! 476: int x2tic_textheight; /* height of x2tic labels */
! 477: int title_textheight; /* height of title */
! 478: int xlabel_textheight; /* height of xlabel */
! 479: int x2label_textheight; /* height of x2label */
! 480: int timetop_textheight; /* height of timestamp (if at top) */
! 481: int timebot_textheight; /* height of timestamp (if at bottom) */
! 482: int ylabel_textheight; /* height of (unrotated) ylabel */
! 483: int y2label_textheight; /* height of (unrotated) y2label */
! 484: int ylabel_textwidth; /* width of (rotated) ylabel */
! 485: int y2label_textwidth; /* width of (rotated) y2label */
! 486: int timelabel_textwidth; /* width of timestamp */
! 487: int ytic_textwidth; /* width of ytic labels */
! 488: int y2tic_textwidth; /* width of y2tic labels */
! 489: int x2tic_height; /* 0 for tic_in or no x2tics, ticscale*v_tic otherwise */
! 490: int xtic_height;
! 491: int ytic_width;
! 492: int y2tic_width;
! 493:
! 494: /* figure out which rotatable items are to be rotated
! 495: * (ylabel and y2label are rotated if possible) */
! 496: int vertical_timelabel = can_rotate && timelabel_rotate;
! 497: int vertical_xtics = can_rotate && rotate_xtics;
! 498: int vertical_x2tics = can_rotate && rotate_x2tics;
! 499: int vertical_ytics = can_rotate && rotate_ytics;
! 500: int vertical_y2tics = can_rotate && rotate_y2tics;
! 501:
! 502: lkey = key; /* but we may have to disable it later */
! 503:
! 504: xticlin = ylablin = y2lablin = xlablin = x2lablin = titlelin = 0;
! 505:
! 506: /*{{{ count lines in labels and tics */
! 507: if (*title.text)
! 508: label_width(title.text, &titlelin);
! 509: if (*xlabel.text)
! 510: label_width(xlabel.text, &xlablin);
! 511: if (*x2label.text)
! 512: label_width(x2label.text, &x2lablin);
! 513: if (*ylabel.text)
! 514: label_width(ylabel.text, &ylablin);
! 515: if (*y2label.text)
! 516: label_width(y2label.text, &y2lablin);
! 517: if (xtics)
! 518: label_width(xformat, &xticlin);
! 519: if (x2tics)
! 520: label_width(x2format, &x2ticlin);
! 521: if (ytics)
! 522: label_width(yformat, &yticlin);
! 523: if (y2tics)
! 524: label_width(y2format, &y2ticlin);
! 525: if (*timelabel.text)
! 526: label_width(timelabel.text, &timelin);
! 527: /*}}} */
! 528:
! 529: /*{{{ preliminary ytop calculation */
! 530:
! 531: /* first compute heights of things to be written in the margin */
! 532:
! 533: /* title */
! 534: if (titlelin)
! 535: title_textheight = (int) ((titlelin + title.yoffset + 1) * (t->v_char));
! 536: else
! 537: title_textheight = 0;
! 538:
! 539: /* x2label */
! 540: if (x2lablin) {
! 541: x2label_textheight = (int) ((x2lablin + x2label.yoffset) * (t->v_char));
! 542: if (!x2tics)
! 543: x2label_textheight += 0.5 * t->v_char;
! 544: } else
! 545: x2label_textheight = 0;
! 546:
! 547: /* tic labels */
! 548: if (x2tics & TICS_ON_BORDER) {
! 549: /* ought to consider tics on axes if axis near border */
! 550: if (vertical_x2tics) {
! 551: /* guess at tic length, since we don't know it yet
! 552: --- we'll fix it after the tic labels have been created */
! 553: x2tic_textheight = (int) (5 * (t->h_char));
! 554: } else
! 555: x2tic_textheight = (int) ((x2ticlin) * (t->v_char));
! 556: } else
! 557: x2tic_textheight = 0;
! 558:
! 559: /* tics */
! 560: if (!tic_in && ((x2tics & TICS_ON_BORDER) || ((xtics & TICS_MIRROR) && (xtics & TICS_ON_BORDER))))
! 561: x2tic_height = (int) ((t->v_tic) * ticscale);
! 562: else
! 563: x2tic_height = 0;
! 564:
! 565: /* timestamp */
! 566: if (*timelabel.text && !timelabel_bottom)
! 567: timetop_textheight = (int) ((timelin + timelabel.yoffset + 2) * (t->v_char));
! 568: else
! 569: timetop_textheight = 0;
! 570:
! 571: /* horizontal ylabel */
! 572: if (*ylabel.text && !can_rotate)
! 573: ylabel_textheight = (int) ((ylablin + ylabel.yoffset) * (t->v_char));
! 574: else
! 575: ylabel_textheight = 0;
! 576:
! 577: /* horizontal y2label */
! 578: if (*y2label.text && !can_rotate)
! 579: y2label_textheight = (int) ((y2lablin + y2label.yoffset) * (t->v_char));
! 580: else
! 581: y2label_textheight = 0;
! 582:
! 583: /* compute ytop from the various components
! 584: * unless tmargin is explicitly specified */
! 585:
! 586: ytop = (int) ((ysize + yoffset) * (t->ymax));
! 587:
! 588: if (tmargin < 0) {
! 589: int top_margin = x2label_textheight + title_textheight;
! 590: if (timetop_textheight + ylabel_textheight > top_margin)
! 591: top_margin = timetop_textheight + ylabel_textheight;
! 592: if (y2label_textheight > top_margin)
! 593: top_margin = y2label_textheight;
! 594:
! 595: top_margin += x2tic_height + x2tic_textheight;
! 596: /* FIXME: what is this additional space reservation for??? */
! 597: if (top_margin > x2tic_height)
! 598: top_margin += (int) (t->v_char);
! 599:
! 600: ytop -= top_margin;
! 601: if (ytop == (int) ((ysize + yoffset) * (t->ymax))) {
! 602: /* make room for the end of rotated ytics or y2tics */
! 603: ytop -= (int) ((t->h_char) * 2);
! 604: }
! 605: } else
! 606: ytop -= (int) ((t->v_char) * tmargin);
! 607:
! 608: /* end of preliminary ytop calculation }}} */
! 609:
! 610:
! 611: /*{{{ tentative xleft, needed for TUNDER */
! 612: if (lmargin >= 0)
! 613: xleft = (int) (xoffset * (t->xmax) + (t->h_char) * lmargin);
! 614: else
! 615: xleft = (int) (xoffset * (t->xmax) + (t->h_char) * 2);
! 616: /*}}} */
! 617:
! 618:
! 619: /*{{{ tentative xright, needed for TUNDER */
! 620: if (rmargin >= 0)
! 621: xright = (int) ((xsize + xoffset) * (t->xmax) - (t->h_char) * rmargin);
! 622: else
! 623: xright = (int) ((xsize + xoffset) * (t->xmax) - (t->h_char) * 2);
! 624: /*}}} */
! 625:
! 626:
! 627: /*{{{ preliminary ybot calculation
! 628: * first compute heights of labels and tics */
! 629:
! 630: /* tic labels */
! 631: if (xtics & TICS_ON_BORDER) {
! 632: /* ought to consider tics on axes if axis near border */
! 633: if (vertical_xtics) {
! 634: /* guess at tic length, since we don't know it yet */
! 635: xtic_textheight = (int) ((t->h_char) * 5);
! 636: } else
! 637: xtic_textheight = (int) ((t->v_char) * (xticlin + 1));
! 638: } else
! 639: xtic_textheight = 0;
! 640:
! 641: /* tics */
! 642: if (!tic_in && ((xtics & TICS_ON_BORDER) || ((x2tics & TICS_MIRROR) && (x2tics & TICS_ON_BORDER))))
! 643: xtic_height = (int) ((t->v_tic) * ticscale);
! 644: else
! 645: xtic_height = 0;
! 646:
! 647: /* xlabel */
! 648: if (xlablin) {
! 649: /* offset is subtracted because if . 0, the margin is smaller */
! 650: xlabel_textheight = (int) ((xlablin - xlabel.yoffset) * (t->v_char));
! 651: if (!xtics)
! 652: xlabel_textheight += 0.5 * t->v_char;
! 653: } else
! 654: xlabel_textheight = 0;
! 655:
! 656: /* timestamp */
! 657: if (*timelabel.text && timelabel_bottom) {
! 658: /* && !vertical_timelabel)
! 659: * DBT 11-18-98 resize plot for vertical timelabels too !
! 660: */
! 661: /* offset is subtracted because if . 0, the margin is smaller */
! 662: timebot_textheight = (int) ((timelin - timelabel.yoffset + 1.5) * (t->v_char));
! 663: }
! 664: else
! 665: timebot_textheight = 0;
! 666:
! 667: /* compute ybot from the various components
! 668: * unless bmargin is explicitly specified */
! 669:
! 670: ybot = (int) ((t->ymax) * yoffset);
! 671:
! 672: if (bmargin < 0) {
! 673: ybot += xtic_height + xtic_textheight;
! 674: if (timebot_textheight > 0 || xlabel_textheight > 0)
! 675: ybot += (timebot_textheight > xlabel_textheight) ? timebot_textheight : xlabel_textheight;
! 676: if (ybot == (t->ymax) * yoffset) {
! 677: /* make room for the end of rotated ytics or y2tics */
! 678: ybot += (int) ((t->h_char) * 2);
! 679: }
! 680: } else
! 681: ybot += (int) (bmargin * (t->v_char));
! 682:
! 683: /* end of preliminary ybot calculation }}} */
! 684:
! 685:
! 686: #define KEY_PANIC(x) if (x) { lkey = 0; goto key_escape; }
! 687:
! 688: if (lkey) {
! 689: /*{{{ essential key features */
! 690: p_width = pointsize * t->h_tic;
! 691: p_height = pointsize * t->v_tic;
! 692:
! 693: if (key_swidth >= 0)
! 694: key_sample_width = key_swidth * (t->h_char) + p_width;
! 695: else
! 696: key_sample_width = 0;
! 697:
! 698: key_entry_height = p_height * 1.25 * key_vert_factor;
! 699: if (key_entry_height < (t->v_char))
! 700: key_entry_height = (t->v_char) * key_vert_factor;
! 701:
! 702: /* count max_len key and number keys with len > 0 */
! 703: max_ptitl_len = find_maxl_keys(plots, count, &ptitl_cnt);
! 704: if ((ytlen = label_width(key_title, &ktitl_lines)) > max_ptitl_len)
! 705: max_ptitl_len = ytlen;
! 706:
! 707: if (key_reverse) {
! 708: key_sample_left = -key_sample_width;
! 709: key_sample_right = 0;
! 710: /* if key width is being used, adjust right-justified text */
! 711: key_text_left = t->h_char;
! 712: key_text_right = (t->h_char) * (max_ptitl_len + 1 + key_width_fix);
! 713: key_size_left = t->h_char - key_sample_left; /* sample left is -ve */
! 714: key_size_right = key_text_right;
! 715: } else {
! 716: key_sample_left = 0;
! 717: key_sample_right = key_sample_width;
! 718: /* if key width is being used, adjust left-justified text */
! 719: key_text_left = -(int) ((t->h_char) * (max_ptitl_len + 1 + key_width_fix));
! 720: key_text_right = -(int) (t->h_char);
! 721: key_size_left = -key_text_left;
! 722: key_size_right = key_sample_right + t->h_char;
! 723: }
! 724: key_point_offset = (key_sample_left + key_sample_right) / 2;
! 725:
! 726: /* advance width for cols */
! 727: key_col_wth = key_size_left + key_size_right;
! 728:
! 729: key_rows = ptitl_cnt;
! 730: key_cols = 1;
! 731:
! 732: /* calculate rows and cols for key - if something goes wrong,
! 733: * the tidiest way out is to set lkey = 0, and a goto
! 734: */
! 735:
! 736: if (lkey == -1) {
! 737: if (key_vpos == TUNDER) {
! 738: /* maximise no cols, limited by label-length */
! 739: key_cols = (int) (xright - xleft) / key_col_wth;
! 740: KEY_PANIC(key_cols == 0);
! 741: key_rows = (int) (ptitl_cnt + key_cols - 1) / key_cols;
! 742: KEY_PANIC(key_rows == 0);
! 743: /* now calculate actual no cols depending on no rows */
! 744: key_cols = (int) (ptitl_cnt + key_rows - 1) / key_rows;
! 745: KEY_PANIC(key_cols == 0);
! 746: key_col_wth = (int) (xright - xleft) / key_cols;
! 747: /* we divide into columns, then centre in column by considering
! 748: * ratio of * key_left_size to key_right_size
! 749: *
! 750: * key_size_left/(key_size_left+key_size_right) * (xright-xleft)/key_cols
! 751: * do one integer division to maximise accuracy (hope we
! 752: * don't overflow !)
! 753: */
! 754: key_xl = xleft - key_size_left + ((xright - xleft) * key_size_left) / (key_cols * (key_size_left + key_size_right));
! 755: key_xr = key_xl + key_col_wth * (key_cols - 1) + key_size_left + key_size_right;
! 756: key_yb = t->ymax * yoffset;
! 757: key_yt = key_yb + key_rows * key_entry_height + ktitl_lines * t->v_char;
! 758: ybot += key_entry_height * key_rows + (int) ((t->v_char) * (ktitl_lines + 1));
! 759: } else {
! 760: /* maximise no rows, limited by ytop-ybot */
! 761: int i = (int) (ytop - ybot - (ktitl_lines + 1) * (t->v_char)) / key_entry_height;
! 762: KEY_PANIC(i == 0);
! 763: if (ptitl_cnt > i) {
! 764: key_cols = (int) (ptitl_cnt + i - 1) / i;
! 765: /* now calculate actual no rows depending on no cols */
! 766: KEY_PANIC(key_cols == 0);
! 767: key_rows = (int) (ptitl_cnt + key_cols - 1) / key_cols;
! 768: }
! 769: }
! 770: /* come here if we detect a division by zero in key calculations */
! 771: key_escape:
! 772: ; /* ansi requires this */
! 773: }
! 774: /*}}} */
! 775: }
! 776: /*{{{ set up y and y2 tics */
! 777: {
! 778: /* setup_tics allows max number of tics to be specified
! 779: * but users dont like it to change with size and font,
! 780: * so we use value of 20, which is 3.5 behaviour.
! 781: * Note also that if format is '', yticlin = 0, so this gives
! 782: * division by zero.
! 783: * int guide = (ytop-ybot)/term->v_char;
! 784: */
! 785: if (ytics)
! 786: setup_tics(FIRST_Y_AXIS, &yticdef, yformat, 20 /*(int) (guide/yticlin) */ );
! 787: if (y2tics)
! 788: setup_tics(SECOND_Y_AXIS, &y2ticdef, y2format, 20 /*(int) (guide/y2ticlin) */ );
! 789: }
! 790: /*}}} */
! 791:
! 792:
! 793: /*{{{ recompute xleft based on widths of ytics, ylabel etc
! 794: unless it has been explicitly set by lmargin */
! 795:
! 796: /* tic labels */
! 797: if (ytics & TICS_ON_BORDER) {
! 798: if (vertical_ytics)
! 799: /* HBB: we will later add some white space as part of this, so
! 800: * reserve two more rows (one above, one below the text ...).
! 801: * Same will be done to similar calc.'s elsewhere */
! 802: ytic_textwidth = (int) ((t->v_char) * (yticlin + 2));
! 803: else {
! 804: widest_tic = 0; /* reset the global variable ... */
! 805: /* get gen_tics to call widest2d_callback with all labels
! 806: * the latter sets widest_tic to the length of the widest one
! 807: * ought to consider tics on axis if axis near border...
! 808: */
! 809: gen_tics(FIRST_Y_AXIS, &yticdef, 0, 0, 0.0, widest2d_callback);
! 810:
! 811: ytic_textwidth = (int) ((t->h_char) * (widest_tic + 2));
! 812: }
! 813: } else {
! 814: ytic_textwidth = 0;
! 815: }
! 816:
! 817: /* tics */
! 818: if (!tic_in && ((ytics & TICS_ON_BORDER) || ((y2tics & TICS_MIRROR) && (y2tics & TICS_ON_BORDER))))
! 819: ytic_width = (int) ((t->h_tic) * ticscale);
! 820: else
! 821: ytic_width = 0;
! 822:
! 823: /* ylabel */
! 824: if (*ylabel.text && can_rotate) {
! 825: ylabel_textwidth = (int) ((ylablin - ylabel.xoffset) * (t->v_char));
! 826: if (!ytics)
! 827: ylabel_textwidth += 0.5 * t->v_char;
! 828: }
! 829: /* this should get large for NEGATIVE ylabel.xoffsets DBT 11-5-98 */
! 830: else
! 831: ylabel_textwidth = 0;
! 832:
! 833: /* timestamp */
! 834: if (*timelabel.text && vertical_timelabel)
! 835: timelabel_textwidth = (int) ((timelin - timelabel.xoffset + 1.5) * (t->v_char));
! 836: else
! 837: timelabel_textwidth = 0;
! 838:
! 839: /* compute xleft from the various components
! 840: * unless lmargin is explicitly specified */
! 841:
! 842: xleft = (int) ((t->xmax) * xoffset);
! 843:
! 844: if (lmargin < 0) {
! 845: xleft += (timelabel_textwidth > ylabel_textwidth ? timelabel_textwidth : ylabel_textwidth)
! 846: + ytic_width + ytic_textwidth;
! 847:
! 848: if (xleft == (t->xmax) * xoffset) {
! 849: /* make room for end of xtic or x2tic label */
! 850: xleft += (int) ((t->h_char) * 2);
! 851: }
! 852: /* DBT 12-3-98 extra margin just in case */
! 853: xleft += 0.5 * t->v_char;
! 854: } else
! 855: xleft += (int) (lmargin * (t->h_char));
! 856:
! 857: /* make sure xleft is wide enough for a negatively
! 858: * x-offset horizontal timestamp
! 859: */
! 860: if (!vertical_timelabel && xleft - ytic_width - ytic_textwidth < -(int) (timelabel.xoffset * (t->h_char)))
! 861: xleft = ytic_width + ytic_textwidth - (int) (timelabel.xoffset * (t->h_char));
! 862: /* end of xleft calculation }}} */
! 863:
! 864:
! 865: /*{{{ recompute xright based on widest y2tic. y2labels, key TOUT
! 866: unless it has been explicitly set by rmargin */
! 867:
! 868: /* tic labels */
! 869: if (y2tics & TICS_ON_BORDER) {
! 870: if (vertical_y2tics)
! 871: y2tic_textwidth = (int) ((t->v_char) * (y2ticlin + 2));
! 872: else {
! 873: widest_tic = 0; /* reset the global variable ... */
! 874: /* get gen_tics to call widest2d_callback with all labels
! 875: * the latter sets widest_tic to the length of the widest one
! 876: * ought to consider tics on axis if axis near border...
! 877: */
! 878: gen_tics(SECOND_Y_AXIS, &y2ticdef, 0, 0, 0.0, widest2d_callback);
! 879:
! 880: y2tic_textwidth = (int) ((t->h_char) * (widest_tic + 2));
! 881: }
! 882: } else {
! 883: y2tic_textwidth = 0;
! 884: }
! 885:
! 886: /* tics */
! 887: if (!tic_in && ((y2tics & TICS_ON_BORDER) || ((ytics & TICS_MIRROR) && (ytics & TICS_ON_BORDER))))
! 888: y2tic_width = (int) ((t->h_tic) * ticscale);
! 889: else
! 890: y2tic_width = 0;
! 891:
! 892: /* y2label */
! 893: if (can_rotate && *y2label.text) {
! 894: y2label_textwidth = (int) ((y2lablin + y2label.xoffset) * (t->v_char));
! 895: if (!y2tics)
! 896: y2label_textwidth += 0.5 * t->v_char;
! 897: } else
! 898: y2label_textwidth = 0;
! 899:
! 900: /* compute xright from the various components
! 901: * unless rmargin is explicitly specified */
! 902:
! 903: xright = (int) ((t->xmax) * (xsize + xoffset));
! 904:
! 905: if (rmargin < 0) {
! 906: /* xright -= y2label_textwidth + y2tic_width + y2tic_textwidth; */
! 907: xright -= y2tic_width + y2tic_textwidth;
! 908: if (y2label_textwidth > 0)
! 909: xright -= y2label_textwidth;
! 910:
! 911: /* adjust for outside key */
! 912: if (lkey == -1 && key_hpos == TOUT) {
! 913: xright -= key_col_wth * key_cols;
! 914: key_xl = xright + (int) (t->h_tic);
! 915: }
! 916: if (xright == (t->xmax) * (xsize + xoffset)) {
! 917: /* make room for end of xtic or x2tic label */
! 918: xright -= (int) ((t->h_char) * 2);
! 919: }
! 920: xright -= 0.5 * t->v_char; /* DBT 12-3-98 extra margin just in case */
! 921:
! 922: } else
! 923: xright -= (int) (rmargin * (t->h_char));
! 924:
! 925: /* end of xright calculation }}} */
! 926:
! 927:
! 928: if (aspect_ratio != 0.0) {
! 929: double current_aspect_ratio;
! 930:
! 931: if (aspect_ratio < 0 && (max_array[x_axis] - min_array[x_axis]) != 0.0) {
! 932: current_aspect_ratio = -aspect_ratio * (max_array[y_axis] - min_array[y_axis]) / (max_array[x_axis] - min_array[x_axis]);
! 933: } else
! 934: current_aspect_ratio = aspect_ratio;
! 935:
! 936: /*{{{ set aspect ratio if valid and sensible */
! 937: if (current_aspect_ratio >= 0.01 && current_aspect_ratio <= 100.0) {
! 938: double current = ((double) (ytop - ybot)) / ((double) (xright - xleft));
! 939: double required = (current_aspect_ratio * (double) t->v_tic) / ((double) t->h_tic);
! 940:
! 941: if (current > required) {
! 942: /* too tall */
! 943: ytop = ybot + required * (xright - xleft);
! 944: } else {
! 945: /* HBB: y2label_x wasn't defined yet, and would be
! 946: * overwritten later */
! 947: xright = xleft + (ytop - ybot) / required;
! 948: }
! 949: }
! 950: /*}}} */
! 951: }
! 952: /*{{{ set up x and x2 tics */
! 953: /* we should base the guide on the width of the xtics, but we cannot
! 954: * use widest_tics until tics are set up. Bit of a downer - let us
! 955: * assume tics are 5 characters wide
! 956: */
! 957:
! 958: {
! 959: /* see equivalent code for ytics above
! 960: * int guide = (xright - xleft) / (5*t->h_char);
! 961: */
! 962:
! 963: if (xtics)
! 964: setup_tics(FIRST_X_AXIS, &xticdef, xformat, 20 /*guide */ );
! 965: if (x2tics)
! 966: setup_tics(SECOND_X_AXIS, &x2ticdef, x2format, 20 /*guide */ );
! 967: }
! 968: /*}}} */
! 969:
! 970:
! 971: /* adjust top and bottom margins for tic label rotation */
! 972:
! 973: if (tmargin < 0 && x2tics & TICS_ON_BORDER && vertical_x2tics) {
! 974: widest_tic = 0; /* reset the global variable ... */
! 975: gen_tics(SECOND_X_AXIS, &x2ticdef, 0, 0, 0.0, widest2d_callback);
! 976: /* HBB: redid this: remove rough guess value first. Among other reasons,
! 977: * I suspected the '-4 lines' of the original code to be in error, as the
! 978: * original calc. of x2tic_textheight uses *5* lines */
! 979: ytop += x2tic_textheight;
! 980: /* Now compute a new one and use that instead: */
! 981: x2tic_textheight = (int) ((t->h_char) * (widest_tic));
! 982: ytop -= x2tic_textheight;
! 983: }
! 984: if (bmargin < 0 && xtics & TICS_ON_BORDER && vertical_xtics) {
! 985: widest_tic = 0; /* reset the global variable ... */
! 986: gen_tics(FIRST_X_AXIS, &xticdef, 0, 0, 0.0, widest2d_callback);
! 987: /* HBB: same changes as for tmargin/ytop above */
! 988: ybot -= xtic_textheight;
! 989: xtic_textheight = (int) ((t->h_char) * widest_tic);
! 990: ybot += xtic_textheight;
! 991: }
! 992: /* compute coordinates for axis labels, title et al
! 993: * (some of these may not be used) */
! 994:
! 995: x2label_y = ytop + x2tic_height + x2tic_textheight + x2label_textheight;
! 996: if (x2tic_textheight && (title_textheight || x2label_textheight))
! 997: x2label_y += t->v_char;
! 998:
! 999: title_y = x2label_y + title_textheight;
! 1000:
! 1001: ylabel_y = ytop + x2tic_height + x2tic_textheight + ylabel_textheight;
! 1002:
! 1003: y2label_y = ytop + x2tic_height + x2tic_textheight + y2label_textheight;
! 1004:
! 1005: xlabel_y = ybot - xtic_height - xtic_textheight - xlabel_textheight + t->v_char;
! 1006: ylabel_x = xleft - ytic_width - ytic_textwidth;
! 1007: if (*ylabel.text && can_rotate)
! 1008: ylabel_x -= ylabel_textwidth;
! 1009:
! 1010: y2label_x = xright + y2tic_width + y2tic_textwidth;
! 1011: if (*y2label.text && can_rotate)
! 1012: y2label_x += y2label_textwidth - t->v_char;
! 1013:
! 1014: if (vertical_timelabel) {
! 1015: if (timelabel_bottom)
! 1016: time_y = xlabel_y - timebot_textheight + xlabel_textheight;
! 1017: else {
! 1018: time_y = title_y + timetop_textheight - title_textheight - x2label_textheight;
! 1019: }
! 1020: } else {
! 1021: if (timelabel_bottom)
! 1022: time_y = xlabel_y - timebot_textheight + xlabel_textheight;
! 1023: else if (ylabel_textheight > 0)
! 1024: time_y = ylabel_y + timetop_textheight;
! 1025: else
! 1026: time_y = ytop + x2tic_height + x2tic_textheight + timetop_textheight + (int) (t->h_char);
! 1027: }
! 1028: if (vertical_timelabel)
! 1029: time_x = xleft - ytic_width - ytic_textwidth - timelabel_textwidth;
! 1030: else
! 1031: time_x = xleft - ytic_width - ytic_textwidth + (int) (timelabel.xoffset * (t->h_char));
! 1032:
! 1033: xtic_y = ybot - xtic_height - (vertical_xtics ? (int) (t->h_char) : (int) (t->v_char));
! 1034:
! 1035: x2tic_y = ytop + x2tic_height + (vertical_x2tics ? (int) (t->h_char) : x2tic_textheight);
! 1036:
! 1037: ytic_x = xleft - ytic_width - (vertical_ytics ? (ytic_textwidth - (int) t->v_char) : (int) (t->h_char));
! 1038:
! 1039: y2tic_x = xright + y2tic_width + (vertical_y2tics ? (int) (t->v_char) : (int) (t->h_char));
! 1040:
! 1041: /* restore text to horizontal [we tested rotation above] */
! 1042: (void) (*t->text_angle) (0);
! 1043:
! 1044: /* needed for map_position() below */
! 1045:
! 1046: scale[FIRST_Y_AXIS] = (ytop - ybot) / (max_array[FIRST_Y_AXIS] - min_array[FIRST_Y_AXIS]);
! 1047: scale[FIRST_X_AXIS] = (xright - xleft) / (max_array[FIRST_X_AXIS] - min_array[FIRST_X_AXIS]);
! 1048: scale[SECOND_Y_AXIS] = (ytop - ybot) / (max_array[SECOND_Y_AXIS] - min_array[SECOND_Y_AXIS]);
! 1049: scale[SECOND_X_AXIS] = (xright - xleft) / (max_array[SECOND_X_AXIS] - min_array[SECOND_X_AXIS]);
! 1050:
! 1051: /*{{{ calculate the window in the grid for the key */
! 1052: if (lkey == 1 || (lkey == -1 && key_vpos != TUNDER)) {
! 1053: /* calculate space for keys to prevent grid overwrite the keys */
! 1054: /* do it even if there is no grid, as do_plot will use these to position key */
! 1055: key_w = key_col_wth * key_cols;
! 1056: key_h = (ktitl_lines) * t->v_char + key_rows * key_entry_height;
! 1057: if (lkey == -1) {
! 1058: if (key_vpos == TTOP) {
! 1059: key_yt = (int) ytop - (t->v_tic);
! 1060: key_yb = key_yt - key_h;
! 1061: } else {
! 1062: key_yb = ybot + (t->v_tic);
! 1063: key_yt = key_yb + key_h;
! 1064: }
! 1065: if (key_hpos == TLEFT) {
! 1066: key_xl = xleft + (t->h_char); /* for Left just */
! 1067: key_xr = key_xl + key_w;
! 1068: } else if (key_hpos == TRIGHT) {
! 1069: key_xr = xright - (t->h_char); /* for Right just */
! 1070: key_xl = key_xr - key_w;
! 1071: } else { /* TOUT */
! 1072: /* do this here for do_plot() */
! 1073: /* align right first since rmargin may be manual */
! 1074: key_xr = (xsize + xoffset) * (t->xmax) - (t->h_char);
! 1075: key_xl = key_xr - key_w;
! 1076: }
! 1077: } else {
! 1078: unsigned int x, y;
! 1079: map_position(&key_user_pos, &x, &y, "key");
! 1080: key_xl = x - key_size_left;
! 1081: key_xr = key_xl + key_w;
! 1082: key_yt = y + (ktitl_lines ? t->v_char : key_entry_height) / 2;
! 1083: key_yb = key_yt - key_h;
! 1084: }
! 1085: }
! 1086: /*}}} */
! 1087:
! 1088: }
! 1089:
! 1090: /*}}} */
! 1091:
! 1092: /*{{{ dbl_raise() */
! 1093: static double dbl_raise(x, y)
! 1094: double x;
! 1095: int y;
! 1096: {
! 1097: register int i = abs(y);
! 1098: double val = 1.0;
! 1099:
! 1100: while (--i >= 0)
! 1101: val *= x;
! 1102:
! 1103: if (y < 0)
! 1104: return (1.0 / val);
! 1105: return (val);
! 1106: }
! 1107: /*}}} */
! 1108:
! 1109: /*{{{ timetic_fmt() */
! 1110: void timetic_format(axis, amin, amax)
! 1111: int axis;
! 1112: double amin, amax;
! 1113: {
! 1114: struct tm tmin, tmax;
! 1115:
! 1116: *ticfmt[axis] = 0; /* make sure we strcat to empty string */
! 1117:
! 1118: ggmtime(&tmin, (double) time_tic_just(timelevel[axis], amin));
! 1119: ggmtime(&tmax, (double) time_tic_just(timelevel[axis], amax));
! 1120: /*
! 1121: if ( tmax.tm_year == tmin.tm_year &&
! 1122: tmax.tm_mon == tmin.tm_mon &&
! 1123: tmax.tm_mday == tmin.tm_mday ) {
! 1124: */
! 1125: if (tmax.tm_year == tmin.tm_year &&
! 1126: tmax.tm_yday == tmin.tm_yday) {
! 1127: /* same day, skip date */
! 1128: if (tmax.tm_hour != tmin.tm_hour) {
! 1129: strcpy(ticfmt[axis], "%H");
! 1130: }
! 1131: if (timelevel[axis] < 3) {
! 1132: if (strlen(ticfmt[axis]))
! 1133: strcat(ticfmt[axis], ":");
! 1134: strcat(ticfmt[axis], "%M");
! 1135: }
! 1136: if (timelevel[axis] < 2) {
! 1137: strcat(ticfmt[axis], ":%S");
! 1138: }
! 1139: } else {
! 1140: if (tmax.tm_year != tmin.tm_year) {
! 1141: /* different years, include year in ticlabel */
! 1142: /* check convention, day/month or month/day */
! 1143: if (strchr(timefmt, 'm') < strchr(timefmt, 'd')) {
! 1144: strcpy(ticfmt[axis], "%m/%d/%");
! 1145: } else {
! 1146: strcpy(ticfmt[axis], "%d/%m/%");
! 1147: }
! 1148: if (((int) (tmax.tm_year / 100)) != ((int) (tmin.tm_year / 100))) {
! 1149: strcat(ticfmt[axis], "Y");
! 1150: } else {
! 1151: strcat(ticfmt[axis], "y");
! 1152: }
! 1153:
! 1154: } else {
! 1155: if (strchr(timefmt, 'm') < strchr(timefmt, 'd')) {
! 1156: strcpy(ticfmt[axis], "%m/%d");
! 1157: } else {
! 1158: strcpy(ticfmt[axis], "%d/%m");
! 1159: }
! 1160: }
! 1161: if (timelevel[axis] < 4) {
! 1162: strcat(ticfmt[axis], "\n%H:%M");
! 1163: }
! 1164: }
! 1165: }
! 1166: /*}}} */
! 1167:
! 1168: /*{{{ set_tic() */
! 1169: /* the guide parameter was intended to allow the number of tics
! 1170: * to depend on the relative sizes of the plot and the font.
! 1171: * It is the approximate upper limit on number of tics allowed.
! 1172: * But it did not go down well with the users.
! 1173: * A value of 20 gives the same behaviour as 3.5, so that is
! 1174: * hardwired into the calls to here. Maybe we will restore it
! 1175: * to the automatic calculation one day
! 1176: */
! 1177:
! 1178: double set_tic(l10, guide)
! 1179: double l10;
! 1180: int guide;
! 1181: {
! 1182: double xnorm, tics, posns;
! 1183:
! 1184: int fl = (int) floor(l10);
! 1185: xnorm = pow(10.0, l10 - fl); /* approx number of decades */
! 1186:
! 1187: posns = guide / xnorm; /* approx number of tic posns per decade */
! 1188:
! 1189: if (posns > 40)
! 1190: tics = 0.05; /* eg 0, .05, .10, ... */
! 1191: else if (posns > 20)
! 1192: tics = 0.1; /* eg 0, .1, .2, ... */
! 1193: else if (posns > 10)
! 1194: tics = 0.2; /* eg 0,0.2,0.4,... */
! 1195: else if (posns > 4)
! 1196: tics = 0.5; /* 0,0.5,1, */
! 1197: else if (posns > 1)
! 1198: tics = 1; /* 0,1,2,.... */
! 1199: else if (posns > 0.5)
! 1200: tics = 2; /* 0, 2, 4, 6 */
! 1201: else
! 1202: /* getting desperate... the ceil is to make sure we
! 1203: * go over rather than under - eg plot [-10:10] x*x
! 1204: * gives a range of about 99.999 - tics=xnorm gives
! 1205: * tics at 0, 99.99 and 109.98 - BAD !
! 1206: * This way, inaccuracy the other way will round
! 1207: * up (eg 0->100.0001 => tics at 0 and 101
! 1208: * I think latter is better than former
! 1209: */
! 1210: tics = ceil(xnorm);
! 1211:
! 1212: return (tics * dbl_raise(10.0, fl));
! 1213: }
! 1214: /*}}} */
! 1215:
! 1216: /*{{{ make_tics() */
! 1217: static double make_tics(axis, guide)
! 1218: int axis, guide;
! 1219: {
! 1220: register double xr, tic, l10;
! 1221:
! 1222: xr = fabs(min_array[axis] - max_array[axis]);
! 1223:
! 1224: l10 = log10(xr);
! 1225: tic = set_tic(l10, guide);
! 1226: if (log_array[axis] && tic < 1.0)
! 1227: tic = 1.0;
! 1228: if (datatype[axis] == TIME) {
! 1229: struct tm ftm, etm;
! 1230: /* this is not fun */
! 1231: ggmtime(&ftm, (double) min_array[axis]);
! 1232: ggmtime(&etm, (double) max_array[axis]);
! 1233:
! 1234: timelevel[axis] = 0; /* seconds */
! 1235: if (tic > 20) {
! 1236: /* turn tic into units of minutes */
! 1237: tic = set_tic(log10(xr / 60.0), guide) * 60;
! 1238: timelevel[axis] = 1; /* minutes */
! 1239: }
! 1240: if (tic > 20 * 60) {
! 1241: /* turn tic into units of hours */
! 1242: tic = set_tic(log10(xr / 3600.0), guide) * 3600;
! 1243: timelevel[axis] = 2; /* hours */
! 1244: }
! 1245: if (tic > 2 * 3600) {
! 1246: /* need some tickling */
! 1247: tic = set_tic(log10(xr / (3 * 3600.0)), guide) * 3 * 3600;
! 1248: }
! 1249: if (tic > 6 * 3600) {
! 1250: /* turn tic into units of days */
! 1251: tic = set_tic(log10(xr / DAY_SEC), guide) * DAY_SEC;
! 1252: timelevel[axis] = 3; /* days */
! 1253: }
! 1254: if (tic > 3 * DAY_SEC) {
! 1255: /* turn tic into units of weeks */
! 1256: tic = set_tic(log10(xr / WEEK_SEC), guide) * WEEK_SEC;
! 1257: if (tic < WEEK_SEC) { /* force */
! 1258: tic = WEEK_SEC;
! 1259: }
! 1260: timelevel[axis] = 4; /* weeks */
! 1261: }
! 1262: if (tic > 3 * WEEK_SEC) {
! 1263: /* turn tic into units of month */
! 1264: tic = set_tic(log10(xr / MON_SEC), guide) * MON_SEC;
! 1265: if (tic < MON_SEC) { /* force */
! 1266: tic = MON_SEC;
! 1267: }
! 1268: timelevel[axis] = 5; /* month */
! 1269: }
! 1270: if (tic > 2 * MON_SEC) {
! 1271: /* turn tic into units of month */
! 1272: tic = set_tic(log10(xr / (3 * MON_SEC)), guide) * 3 * MON_SEC;
! 1273: }
! 1274: if (tic > 6 * MON_SEC) {
! 1275: /* turn tic into units of years */
! 1276: tic = set_tic(log10(xr / YEAR_SEC), guide) * YEAR_SEC;
! 1277: if (tic < (YEAR_SEC / 2)) {
! 1278: tic = YEAR_SEC / 2;
! 1279: }
! 1280: timelevel[axis] = 6; /* year */
! 1281: }
! 1282: }
! 1283: return (tic);
! 1284: }
! 1285: /*}}} */
! 1286:
! 1287:
! 1288: void do_plot(plots, pcount)
! 1289: struct curve_points *plots;
! 1290: int pcount; /* count of plots in linked list */
! 1291: {
! 1292:
! 1293: /* BODGES BEFORE I FIX IT UP */
! 1294: #define ytic ticstep[y_axis]
! 1295: #define xtic ticstep[x_axis]
! 1296:
! 1297: register struct termentry *t = term;
! 1298: register int curve;
! 1299: int axis_zero[AXIS_ARRAY_SIZE]; /* axes in terminal coords for FIRST_X_AXIS, etc */
! 1300: register struct curve_points *this_plot = NULL;
! 1301: register int xl = 0, yl = 0; /* avoid gcc -Wall warning */
! 1302: register int key_count = 0;
! 1303: /* only a Pyramid would have this many registers! */
! 1304: struct text_label *this_label;
! 1305: struct arrow_def *this_arrow;
! 1306: TBOOLEAN scaling;
! 1307: char ss[MAX_LINE_LEN + 1], *s, *e;
! 1308:
! 1309: /* so that macros for x_min etc pick up correct values
! 1310: * until this is done properly
! 1311: */
! 1312:
! 1313: x_axis = FIRST_X_AXIS;
! 1314: y_axis = FIRST_Y_AXIS;
! 1315:
! 1316: /* Apply the desired viewport offsets. */
! 1317: if (y_min < y_max) {
! 1318: y_min -= boff;
! 1319: y_max += toff;
! 1320: } else {
! 1321: y_max -= boff;
! 1322: y_min += toff;
! 1323: }
! 1324: if (x_min < x_max) {
! 1325: x_min -= loff;
! 1326: x_max += roff;
! 1327: } else {
! 1328: x_max -= loff;
! 1329: x_min += roff;
! 1330: }
! 1331:
! 1332: /*
! 1333: * In the beginning, this "empty range" test was for exact
! 1334: * equality, eg y_min == y_max , but that caused an infinite
! 1335: * loop once. Then the test was changed to check for being
! 1336: * within the 'zero' threshold, fabs(y_max - y_min) < zero) ,
! 1337: * but that prevented plotting data with ranges below 'zero'.
! 1338: * Now it's an absolute equality test again, since fixup_range()
! 1339: * should have widened empty ranges before we get here.
! 1340: */
! 1341: if (x_min == x_max)
! 1342: int_error("x_min should not equal x_max!", NO_CARET);
! 1343: if (y_min == y_max)
! 1344: int_error("y_min should not equal y_max!", NO_CARET);
! 1345:
! 1346: term_init(); /* may set xmax/ymax */
! 1347:
! 1348: /* compute boundary for plot (xleft, xright, ytop, ybot)
! 1349: * also calculates tics, since xtics depend on xleft
! 1350: * but xleft depends on ytics. Boundary calculations
! 1351: * depend on term->v_char etc, so terminal must be
! 1352: * initialised.
! 1353: */
! 1354:
! 1355: scaling = (*t->scale) (xsize, ysize);
! 1356:
! 1357: boundary(scaling, plots, pcount);
! 1358:
! 1359: screen_ok = FALSE;
! 1360:
! 1361: term_start_plot();
! 1362:
! 1363: /* DRAW TICS AND GRID */
! 1364: term_apply_lp_properties(&border_lp); /* border linetype */
! 1365: largest_polar_circle = 0;
! 1366:
! 1367: /* select first mapping */
! 1368: x_axis = FIRST_X_AXIS;
! 1369: y_axis = FIRST_Y_AXIS;
! 1370:
! 1371: /* label first y axis tics */
! 1372: if (ytics) {
! 1373: int axis = map_x(ZERO);
! 1374: /* set the globals ytick2d_callback() needs */
! 1375:
! 1376: if (rotate_ytics && (*t->text_angle) (1)) {
! 1377: tic_hjust = CENTRE;
! 1378: tic_vjust = JUST_BOT;
! 1379: rotate_tics = 1; /* HBB 980629 */
! 1380: ytic_x += t->v_char / 2;
! 1381: } else {
! 1382: tic_hjust = RIGHT;
! 1383: tic_vjust = JUST_CENTRE;
! 1384: rotate_tics = 0; /* HBB 980629 */
! 1385: }
! 1386:
! 1387: if (ytics & TICS_MIRROR)
! 1388: tic_mirror = xright;
! 1389: else
! 1390: tic_mirror = -1; /* no thank you */
! 1391:
! 1392: if ((ytics & TICS_ON_AXIS) && !log_array[FIRST_X_AXIS] && inrange(axis, xleft, xright)) {
! 1393: tic_start = axis;
! 1394: tic_direction = -1;
! 1395: if (ytics & TICS_MIRROR)
! 1396: tic_mirror = tic_start;
! 1397: /* put text at boundary if axis is close to boundary */
! 1398: tic_text = (((tic_start - xleft) > (3 * t->h_char)) ? tic_start : xleft) - t->h_char;
! 1399: } else {
! 1400: tic_start = xleft;
! 1401: tic_direction = tic_in ? 1 : -1;
! 1402: tic_text = ytic_x;
! 1403: }
! 1404: /* go for it */
! 1405: gen_tics(FIRST_Y_AXIS, &yticdef,
! 1406: work_grid.l_type & (GRID_Y | GRID_MY),
! 1407: mytics, mytfreq, ytick2d_callback);
! 1408: (*t->text_angle) (0); /* reset rotation angle */
! 1409:
! 1410: }
! 1411: /* label first x axis tics */
! 1412: if (xtics) {
! 1413: int axis = map_y(ZERO);
! 1414: /* set the globals xtick2d_callback() needs */
! 1415:
! 1416: if (rotate_xtics && (*t->text_angle) (1)) {
! 1417: tic_hjust = RIGHT;
! 1418: tic_vjust = JUST_CENTRE;
! 1419: rotate_tics = 1; /* HBB 980629 */
! 1420: } else {
! 1421: tic_hjust = CENTRE;
! 1422: tic_vjust = JUST_TOP;
! 1423: rotate_tics = 0; /* HBB 980629 */
! 1424: }
! 1425:
! 1426: if (xtics & TICS_MIRROR)
! 1427: tic_mirror = ytop;
! 1428: else
! 1429: tic_mirror = -1; /* no thank you */
! 1430: if ((xtics & TICS_ON_AXIS) && !log_array[FIRST_Y_AXIS] && inrange(axis, ybot, ytop)) {
! 1431: tic_start = axis;
! 1432: tic_direction = -1;
! 1433: if (xtics & TICS_MIRROR)
! 1434: tic_mirror = tic_start;
! 1435: /* put text at boundary if axis is close to boundary */
! 1436: if (tic_start - ybot > 2 * t->v_char)
! 1437: tic_text = tic_start - ticscale * t->v_tic - t->v_char;
! 1438: else
! 1439: tic_text = ybot - t->v_char;
! 1440: } else {
! 1441: tic_start = ybot;
! 1442: tic_direction = tic_in ? 1 : -1;
! 1443: tic_text = xtic_y;
! 1444: }
! 1445: /* go for it */
! 1446: gen_tics(FIRST_X_AXIS, &xticdef,
! 1447: work_grid.l_type & (GRID_X | GRID_MX),
! 1448: mxtics, mxtfreq, xtick2d_callback);
! 1449: (*t->text_angle) (0); /* reset rotation angle */
! 1450: }
! 1451: /* select second mapping */
! 1452: x_axis = SECOND_X_AXIS;
! 1453: y_axis = SECOND_Y_AXIS;
! 1454:
! 1455: /* label second y axis tics */
! 1456: if (y2tics) {
! 1457: /* set the globalss ytick2d_callback() needs */
! 1458: int axis = map_x(ZERO);
! 1459:
! 1460: if (rotate_y2tics && (*t->text_angle) (1)) {
! 1461: tic_hjust = CENTRE;
! 1462: tic_vjust = JUST_TOP;
! 1463: rotate_tics = 1; /* HBB 980629 */
! 1464: } else {
! 1465: tic_hjust = LEFT;
! 1466: tic_vjust = JUST_CENTRE;
! 1467: rotate_tics = 0; /* HBB 980629 */
! 1468: }
! 1469:
! 1470: if (y2tics & TICS_MIRROR)
! 1471: tic_mirror = xleft;
! 1472: else
! 1473: tic_mirror = -1; /* no thank you */
! 1474: if ((y2tics & TICS_ON_AXIS) && !log_array[FIRST_X_AXIS] && inrange(axis, xleft, xright)) {
! 1475: tic_start = axis;
! 1476: tic_direction = 1;
! 1477: if (y2tics & TICS_MIRROR)
! 1478: tic_mirror = tic_start;
! 1479: /* put text at boundary if axis is close to boundary */
! 1480: tic_text = (((xright - tic_start) > (3 * t->h_char)) ? tic_start : xright) + t->h_char;
! 1481: } else {
! 1482: tic_start = xright;
! 1483: tic_direction = tic_in ? -1 : 1;
! 1484: tic_text = y2tic_x;
! 1485: }
! 1486: /* go for it */
! 1487: gen_tics(SECOND_Y_AXIS, &y2ticdef,
! 1488: work_grid.l_type & (GRID_Y2 | GRID_MY2),
! 1489: my2tics, my2tfreq, ytick2d_callback);
! 1490: (*t->text_angle) (0); /* reset rotation angle */
! 1491: }
! 1492: /* label second x axis tics */
! 1493: if (x2tics) {
! 1494: int axis = map_y(ZERO);
! 1495: /* set the globals xtick2d_callback() needs */
! 1496:
! 1497: if (rotate_x2tics && (*t->text_angle) (1)) {
! 1498: tic_hjust = LEFT;
! 1499: tic_vjust = JUST_CENTRE;
! 1500: rotate_tics = 1; /* HBB 980629 */
! 1501: } else {
! 1502: tic_hjust = CENTRE;
! 1503: tic_vjust = JUST_BOT;
! 1504: rotate_tics = 0; /* HBB 980629 */
! 1505: }
! 1506:
! 1507: if (x2tics & TICS_MIRROR)
! 1508: tic_mirror = ybot;
! 1509: else
! 1510: tic_mirror = -1; /* no thank you */
! 1511: if ((x2tics & TICS_ON_AXIS) && !log_array[SECOND_Y_AXIS] && inrange(axis, ybot, ytop)) {
! 1512: tic_start = axis;
! 1513: tic_direction = 1;
! 1514: if (x2tics & TICS_MIRROR)
! 1515: tic_mirror = tic_start;
! 1516: /* put text at boundary if axis is close to boundary */
! 1517: tic_text = (((ytop - tic_start) > (2 * t->v_char)) ? tic_start : ytop) + t->v_char;
! 1518: } else {
! 1519: tic_start = ytop;
! 1520: tic_direction = tic_in ? -1 : 1;
! 1521: tic_text = x2tic_y;
! 1522: }
! 1523: /* go for it */
! 1524: gen_tics(SECOND_X_AXIS, &x2ticdef,
! 1525: work_grid.l_type & (GRID_X2 | GRID_MX2),
! 1526: mx2tics, mx2tfreq, xtick2d_callback);
! 1527: (*t->text_angle) (0); /* reset rotation angle */
! 1528: }
! 1529: /* select first mapping */
! 1530: x_axis = FIRST_X_AXIS;
! 1531: y_axis = FIRST_Y_AXIS;
! 1532:
! 1533: /* RADIAL LINES FOR POLAR GRID */
! 1534:
! 1535: /* note that draw_clip_line takes unsigneds, but (fortunately)
! 1536: * clip_line takes signeds
! 1537: */
! 1538: if (polar_grid_angle) {
! 1539: double theta = 0;
! 1540: int ox = map_x(0);
! 1541: int oy = map_y(0);
! 1542: term_apply_lp_properties(&grid_lp);
! 1543: for (theta = 0; theta < 6.29; theta += polar_grid_angle) {
! 1544: /* copy ox in case it gets moved (but it shouldn't) */
! 1545: int oox = ox;
! 1546: int ooy = oy;
! 1547: int x = map_x(largest_polar_circle * cos(theta));
! 1548: int y = map_y(largest_polar_circle * sin(theta));
! 1549: if (clip_line(&oox, &ooy, &x, &y)) {
! 1550: (*t->move) ((unsigned int) oox, (unsigned int) ooy);
! 1551: (*t->vector) ((unsigned int) x, (unsigned int) y);
! 1552: }
! 1553: }
! 1554: draw_clip_line(ox, oy,
! 1555: map_x(largest_polar_circle * cos(theta)),
! 1556: map_y(largest_polar_circle * sin(theta)));
! 1557: }
! 1558: /* DRAW AXES */
! 1559:
! 1560: /* after grid so that axes linetypes are on top */
! 1561:
! 1562: x_axis = FIRST_X_AXIS;
! 1563: y_axis = FIRST_Y_AXIS; /* chose scaling */
! 1564: axis_zero[FIRST_X_AXIS] = map_y(0.0);
! 1565: axis_zero[FIRST_Y_AXIS] = map_x(0.0);
! 1566:
! 1567: if (axis_zero[FIRST_X_AXIS] < ybot || is_log_y)
! 1568: axis_zero[FIRST_X_AXIS] = ybot; /* save for impulse plotting */
! 1569: else if (axis_zero[FIRST_X_AXIS] >= ytop)
! 1570: axis_zero[FIRST_X_AXIS] = ytop;
! 1571: else if (xzeroaxis.l_type > -3) {
! 1572: term_apply_lp_properties(&xzeroaxis);
! 1573: (*t->move) (xleft, axis_zero[FIRST_X_AXIS]);
! 1574: (*t->vector) (xright, axis_zero[FIRST_X_AXIS]);
! 1575: }
! 1576: if ((yzeroaxis.l_type > -3) && !is_log_x
! 1577: && axis_zero[FIRST_Y_AXIS] >= xleft
! 1578: && axis_zero[FIRST_Y_AXIS] < xright) {
! 1579: term_apply_lp_properties(&yzeroaxis);
! 1580: (*t->move) (axis_zero[FIRST_Y_AXIS], ybot);
! 1581: (*t->vector) (axis_zero[FIRST_Y_AXIS], ytop);
! 1582: }
! 1583: x_axis = SECOND_X_AXIS;
! 1584: y_axis = SECOND_Y_AXIS; /* chose scaling */
! 1585: axis_zero[SECOND_X_AXIS] = map_y(0.0);
! 1586: axis_zero[SECOND_Y_AXIS] = map_x(0.0);
! 1587:
! 1588: if (axis_zero[SECOND_X_AXIS] < ybot || is_log_y2)
! 1589: axis_zero[SECOND_X_AXIS] = ybot; /* save for impulse plotting */
! 1590: else if (axis_zero[SECOND_X_AXIS] >= ytop)
! 1591: axis_zero[SECOND_X_AXIS] = ytop;
! 1592: else if (x2zeroaxis.l_type > -3) {
! 1593: term_apply_lp_properties(&x2zeroaxis);
! 1594: (*t->move) (xleft, axis_zero[SECOND_X_AXIS]);
! 1595: (*t->vector) (xright, axis_zero[SECOND_X_AXIS]);
! 1596: }
! 1597: if ((y2zeroaxis.l_type > -3) && !is_log_x2 && axis_zero[SECOND_Y_AXIS] >= xleft &&
! 1598: axis_zero[SECOND_Y_AXIS] < xright) {
! 1599: term_apply_lp_properties(&y2zeroaxis);
! 1600: (*t->move) (axis_zero[SECOND_Y_AXIS], ybot);
! 1601: (*t->vector) (axis_zero[SECOND_Y_AXIS], ytop);
! 1602: }
! 1603: /* DRAW PLOT BORDER */
! 1604: if (draw_border) {
! 1605: /* HBB 980609: just in case: move over to border linestyle only
! 1606: * if border is to be drawn */
! 1607: term_apply_lp_properties(&border_lp); /* border linetype */
! 1608: (*t->move) (xleft, ybot);
! 1609: if (border_south) {
! 1610: (*t->vector) (xright, ybot);
! 1611: } else {
! 1612: (*t->move) (xright, ybot);
! 1613: }
! 1614: if (border_east) {
! 1615: (*t->vector) (xright, ytop);
! 1616: } else {
! 1617: (*t->move) (xright, ytop);
! 1618: }
! 1619: if (border_north) {
! 1620: (*t->vector) (xleft, ytop);
! 1621: } else {
! 1622: (*t->move) (xleft, ytop);
! 1623: }
! 1624: if (border_west) {
! 1625: (*t->vector) (xleft, ybot);
! 1626: } else {
! 1627: (*t->move) (xleft, ybot);
! 1628: }
! 1629: }
! 1630: /* YLABEL */
! 1631: if (*ylabel.text) {
! 1632: strcpy(ss, ylabel.text);
! 1633: /* we worked out x-posn in boundary() */
! 1634: if ((*t->text_angle) (1)) {
! 1635: unsigned int x = ylabel_x + (t->v_char / 2);
! 1636: unsigned int y = (ytop + ybot) / 2 + ylabel.yoffset * (t->h_char);
! 1637: write_multiline(x, y, ss, CENTRE, JUST_TOP, 1, ylabel.font);
! 1638: (*t->text_angle) (0);
! 1639: } else {
! 1640: /* really bottom just, but we know number of lines
! 1641: so we need to adjust x-posn by one line */
! 1642: unsigned int x = ylabel_x;
! 1643: unsigned int y = ylabel_y;
! 1644: write_multiline(x, y, ss, LEFT, JUST_TOP, 0, ylabel.font);
! 1645: }
! 1646: }
! 1647: /* Y2LABEL */
! 1648: if (*y2label.text) {
! 1649: strcpy(ss, y2label.text);
! 1650: /* we worked out coordinates in boundary() */
! 1651: if ((*t->text_angle) (1)) {
! 1652: unsigned int x = y2label_x + (t->v_char / 2) - 1;
! 1653: unsigned int y = (ytop + ybot) / 2 + y2label.yoffset * (t->h_char);
! 1654: write_multiline(x, y, ss, CENTRE, JUST_TOP, 1, y2label.font);
! 1655: (*t->text_angle) (0);
! 1656: } else {
! 1657: /* really bottom just, but we know number of lines */
! 1658: unsigned int x = y2label_x;
! 1659: unsigned int y = y2label_y;
! 1660: write_multiline(x, y, ss, RIGHT, JUST_TOP, 0, y2label.font);
! 1661: }
! 1662: }
! 1663: /* XLABEL */
! 1664: if (*xlabel.text) {
! 1665: unsigned int x = (xright + xleft) / 2 + xlabel.xoffset * (t->h_char);
! 1666: unsigned int y = xlabel_y - t->v_char / 2; /* HBB */
! 1667: strcpy(ss, xlabel.text);
! 1668: write_multiline(x, y, ss, CENTRE, JUST_TOP, 0, xlabel.font);
! 1669: }
! 1670: /* PLACE TITLE */
! 1671: if (*title.text) {
! 1672: /* we worked out y-coordinate in boundary() */
! 1673: unsigned int x = (xleft + xright) / 2 + title.xoffset * t->h_char;
! 1674: unsigned int y = title_y - t->v_char / 2;
! 1675: strcpy(ss, title.text);
! 1676: write_multiline(x, y, ss, CENTRE, JUST_TOP, 0, title.font);
! 1677: }
! 1678: /* X2LABEL */
! 1679: if (*x2label.text) {
! 1680: /* we worked out y-coordinate in boundary() */
! 1681: unsigned int x = (xright + xleft) / 2 + x2label.xoffset * (t->h_char);
! 1682: unsigned int y = x2label_y - t->v_char / 2 - 1;
! 1683: strcpy(ss, x2label.text);
! 1684: write_multiline(x, y, ss, CENTRE, JUST_TOP, 0, x2label.font);
! 1685: }
! 1686: /* PLACE TIMEDATE */
! 1687: if (*timelabel.text) {
! 1688: /* we worked out coordinates in boundary() */
! 1689: char str[MAX_LINE_LEN + 1];
! 1690: time_t now;
! 1691: unsigned int x = time_x;
! 1692: unsigned int y = time_y;
! 1693: time(&now);
! 1694: strftime(str, MAX_LINE_LEN, timelabel.text, localtime(&now));
! 1695:
! 1696: if (timelabel_rotate && (*t->text_angle) (1)) {
! 1697: x += t->v_char / 2; /* HBB */
! 1698: if (timelabel_bottom)
! 1699: write_multiline(x, y, str, LEFT, JUST_TOP, 1, timelabel.font);
! 1700: else
! 1701: write_multiline(x, y, str, RIGHT, JUST_TOP, 1, timelabel.font);
! 1702: (*t->text_angle) (0);
! 1703: } else {
! 1704: y -= t->v_char / 2; /* HBB */
! 1705: if (timelabel_bottom)
! 1706: write_multiline(x, y, str, LEFT, JUST_BOT, 0, timelabel.font);
! 1707: else
! 1708: write_multiline(x, y, str, LEFT, JUST_TOP, 0, timelabel.font);
! 1709: }
! 1710: }
! 1711: /* PLACE LABELS */
! 1712: for (this_label = first_label; this_label != NULL;
! 1713: this_label = this_label->next) {
! 1714: unsigned int x, y;
! 1715: map_position(&this_label->place, &x, &y, "label");
! 1716: strcpy(ss, this_label->text);
! 1717: if (this_label->rotate && (*t->text_angle) (1)) {
! 1718: write_multiline(x, y, ss, this_label->pos, JUST_TOP, 1, this_label->font);
! 1719: (*t->text_angle) (0);
! 1720: } else {
! 1721: write_multiline(x, y, ss, this_label->pos, JUST_TOP, 0, this_label->font);
! 1722: }
! 1723: }
! 1724:
! 1725: /* PLACE ARROWS */
! 1726: for (this_arrow = first_arrow; this_arrow != NULL;
! 1727: this_arrow = this_arrow->next) {
! 1728: unsigned int sx, sy, ex, ey;
! 1729: map_position(&this_arrow->start, &sx, &sy, "arrow");
! 1730: map_position(&this_arrow->end, &ex, &ey, "arrow");
! 1731:
! 1732: term_apply_lp_properties(&(this_arrow->lp_properties));
! 1733: (*t->arrow) (sx, sy, ex, ey, this_arrow->head);
! 1734: }
! 1735:
! 1736: /* WORK OUT KEY SETTINGS AND DO KEY TITLE / BOX */
! 1737:
! 1738:
! 1739: if (lkey) { /* may have been cancelled if something went wrong */
! 1740: /* just use key_xl etc worked out in boundary() */
! 1741: xl = key_xl + key_size_left;
! 1742: yl = key_yt;
! 1743:
! 1744: if (*key_title) {
! 1745: sprintf(ss, "%s\n", key_title);
! 1746: s = ss;
! 1747: yl -= t->v_char / 2;
! 1748: while ((e = (char *) strchr(s, '\n')) != NULL) {
! 1749: *e = '\0';
! 1750: if (key_just == JLEFT) {
! 1751: (*t->justify_text) (LEFT);
! 1752: (*t->put_text) (xl + key_text_left, yl, s);
! 1753: } else {
! 1754: if ((*t->justify_text) (RIGHT)) {
! 1755: (*t->put_text) (xl + key_text_right, yl, s);
! 1756: } else {
! 1757: int x = xl + key_text_right - (t->h_char) * strlen(s);
! 1758: if (key_hpos == TOUT || inrange(x, xleft, xright))
! 1759: (*t->put_text) (x, yl, s);
! 1760: }
! 1761: }
! 1762: s = ++e;
! 1763: yl -= t->v_char;
! 1764: }
! 1765: yl += t->v_char / 2;
! 1766: }
! 1767: yl_ref = yl -= key_entry_height / 2; /* centralise the keys */
! 1768: key_count = 0;
! 1769:
! 1770: if (key_box.l_type > -3) {
! 1771: term_apply_lp_properties(&key_box);
! 1772: (*t->move) (key_xl, key_yb);
! 1773: (*t->vector) (key_xl, key_yt);
! 1774: (*t->vector) (key_xr, key_yt);
! 1775: (*t->vector) (key_xr, key_yb);
! 1776: (*t->vector) (key_xl, key_yb);
! 1777: /* draw a horizontal line between key title
! 1778: and first entry *//* JFi */
! 1779: (*t->move) (key_xl, key_yt - (ktitl_lines) * t->v_char); /* JFi */
! 1780: (*t->vector) (key_xr, key_yt - (ktitl_lines) * t->v_char); /* JFi */
! 1781: }
! 1782: } /* lkey */
! 1783: /* DRAW CURVES */
! 1784: this_plot = plots;
! 1785: for (curve = 0; curve < pcount; this_plot = this_plot->next_cp, curve++) {
! 1786: int localkey = lkey; /* a local copy */
! 1787:
! 1788: /* set scaling for this plot's axes */
! 1789: x_axis = this_plot->x_axis;
! 1790: y_axis = this_plot->y_axis;
! 1791:
! 1792: term_apply_lp_properties(&(this_plot->lp_properties));
! 1793:
! 1794: if (this_plot->title && !*this_plot->title) {
! 1795: localkey = 0;
! 1796: } else {
! 1797: if (localkey != 0 && this_plot->title) {
! 1798: key_count++;
! 1799: if (key_just == JLEFT) {
! 1800: (*t->justify_text) (LEFT);
! 1801: (*t->put_text) (xl + key_text_left,
! 1802: yl, this_plot->title);
! 1803: } else {
! 1804: if ((*t->justify_text) (RIGHT)) {
! 1805: (*t->put_text) (xl + key_text_right,
! 1806: yl, this_plot->title);
! 1807: } else {
! 1808: int x = xl + key_text_right - (t->h_char) * strlen(this_plot->title);
! 1809: if (key_hpos == TOUT ||
! 1810: i_inrange(x, xleft, xright))
! 1811: (*t->put_text) (x, yl, this_plot->title);
! 1812: }
! 1813: }
! 1814:
! 1815: /* draw sample depending on bits set in plot_style */
! 1816: if ((this_plot->plot_style & 1) ||
! 1817: ((this_plot->plot_style & 4) && this_plot->plot_type == DATA)) { /* errors for data plots only */
! 1818: (*t->move) (xl + key_sample_left, yl);
! 1819: (*t->vector) (xl + key_sample_right, yl);
! 1820: }
! 1821: /* oops - doing the point sample now breaks postscript
! 1822: * terminal for example, which changes current line style
! 1823: * when drawing a point, but does not restore it.
! 1824: * We simply draw the point sample after plotting
! 1825: */
! 1826:
! 1827: if (this_plot->plot_type == DATA &&
! 1828: (this_plot->plot_style & 4) &&
! 1829: bar_size > 0.0) {
! 1830: (*t->move) (xl + key_sample_left, yl + ERRORBARTIC);
! 1831: (*t->vector) (xl + key_sample_left, yl - ERRORBARTIC);
! 1832: (*t->move) (xl + key_sample_right, yl + ERRORBARTIC);
! 1833: (*t->vector) (xl + key_sample_right, yl - ERRORBARTIC);
! 1834: }
! 1835: }
! 1836: }
! 1837:
! 1838: /* and now the curves, plus any special key requirements */
! 1839: /* be sure to draw all lines before drawing any points */
! 1840:
! 1841: switch (this_plot->plot_style) {
! 1842: /*{{{ IMPULSE */
! 1843: case IMPULSES:
! 1844: plot_impulses(this_plot, axis_zero[y_axis], axis_zero[x_axis]);
! 1845: break;
! 1846: /*}}} */
! 1847: /*{{{ LINES */
! 1848: case LINES:
! 1849: plot_lines(this_plot);
! 1850: break;
! 1851: /*}}} */
! 1852: /*{{{ STEPS */
! 1853: case STEPS:
! 1854: plot_steps(this_plot);
! 1855: break;
! 1856: /*}}} */
! 1857: /*{{{ FSTEPS */
! 1858: case FSTEPS:
! 1859: plot_fsteps(this_plot);
! 1860: break;
! 1861: /*}}} */
! 1862: /*{{{ HISTEPS */
! 1863: case HISTEPS:
! 1864: plot_histeps(this_plot);
! 1865: break;
! 1866: /*}}} */
! 1867: /*{{{ POINTSTYLE */
! 1868: case POINTSTYLE:
! 1869: plot_points(this_plot);
! 1870: break;
! 1871: /*}}} */
! 1872: /*{{{ LINESPOINTS */
! 1873: case LINESPOINTS:
! 1874: plot_lines(this_plot);
! 1875: plot_points(this_plot);
! 1876: break;
! 1877: /*}}} */
! 1878: /*{{{ DOTS */
! 1879: case DOTS:
! 1880: if (localkey != 0 && this_plot->title) {
! 1881: (*t->point) (xl + key_point_offset, yl, -1);
! 1882: }
! 1883: plot_dots(this_plot);
! 1884: break;
! 1885: /*}}} */
! 1886: /*{{{ YERRORBARS */
! 1887: case YERRORBARS:{
! 1888: plot_bars(this_plot);
! 1889: plot_points(this_plot);
! 1890: break;
! 1891: }
! 1892: /*}}} */
! 1893: /*{{{ XERRORBARS */
! 1894: case XERRORBARS:{
! 1895: plot_bars(this_plot);
! 1896: plot_points(this_plot);
! 1897: }
! 1898: /*}}} */
! 1899: /*{{{ XYERRORBARS */
! 1900: case XYERRORBARS:
! 1901: plot_bars(this_plot);
! 1902: plot_points(this_plot);
! 1903: break;
! 1904:
! 1905: /*}}} */
! 1906: /*{{{ BOXXYERROR */
! 1907: case BOXXYERROR:
! 1908: plot_boxes(this_plot, axis_zero[x_axis]);
! 1909: break;
! 1910: /*}}} */
! 1911: /*{{{ BOXERROR (falls through to) */
! 1912: case BOXERROR:
! 1913: plot_bars(this_plot);
! 1914: /* no break */
! 1915:
! 1916: /*}}} */
! 1917: /*{{{ BOXES */
! 1918: case BOXES:
! 1919: plot_boxes(this_plot, axis_zero[x_axis]);
! 1920: break;
! 1921: /*}}} */
! 1922: /*{{{ VECTOR */
! 1923: case VECTOR:
! 1924: plot_vectors(this_plot);
! 1925: break;
! 1926:
! 1927: /*}}} */
! 1928: /*{{{ FINANCEBARS */
! 1929: case FINANCEBARS:
! 1930: plot_f_bars(this_plot);
! 1931: break;
! 1932: /*}}} */
! 1933: /*{{{ CANDLESTICKS */
! 1934: case CANDLESTICKS:
! 1935: plot_c_bars(this_plot);
! 1936: break;
! 1937: /*}}} */
! 1938: }
! 1939:
! 1940:
! 1941: if (localkey && this_plot->title) {
! 1942: /* we deferred point sample until now */
! 1943: if (this_plot->plot_style & 2)
! 1944: (*t->point) (xl + key_point_offset, yl,
! 1945: this_plot->lp_properties.p_type);
! 1946:
! 1947: if (key_count >= key_rows) {
! 1948: yl = yl_ref;
! 1949: xl += key_col_wth;
! 1950: key_count = 0;
! 1951: } else
! 1952: yl = yl - key_entry_height;
! 1953: }
! 1954: }
! 1955:
! 1956: term_end_plot();
! 1957: }
! 1958:
! 1959:
! 1960: /* BODGES */
! 1961: #undef ytic
! 1962: #undef xtic
! 1963:
! 1964: /* plot_impulses:
! 1965: * Plot the curves in IMPULSES style
! 1966: */
! 1967:
! 1968: static void plot_impulses(plot, yaxis_x, xaxis_y)
! 1969: struct curve_points *plot;
! 1970: int yaxis_x, xaxis_y;
! 1971: {
! 1972: int i;
! 1973: int x, y;
! 1974: struct termentry *t = term;
! 1975:
! 1976: for (i = 0; i < plot->p_count; i++) {
! 1977: switch (plot->points[i].type) {
! 1978: case INRANGE:{
! 1979: x = map_x(plot->points[i].x);
! 1980: y = map_y(plot->points[i].y);
! 1981: break;
! 1982: }
! 1983: case OUTRANGE:{
! 1984: if (!inrange(plot->points[i].x, x_min, x_max))
! 1985: continue;
! 1986: x = map_x(plot->points[i].x);
! 1987: if ((y_min < y_max
! 1988: && plot->points[i].y < y_min)
! 1989: || (y_max < y_min
! 1990: && plot->points[i].y > y_min))
! 1991: y = map_y(y_min);
! 1992: else
! 1993: y = map_y(y_max);
! 1994:
! 1995: break;
! 1996: }
! 1997: default: /* just a safety */
! 1998: case UNDEFINED:{
! 1999: continue;
! 2000: }
! 2001: }
! 2002:
! 2003: if (polar)
! 2004: (*t->move) (yaxis_x, xaxis_y);
! 2005: else
! 2006: (*t->move) (x, xaxis_y);
! 2007: (*t->vector) (x, y);
! 2008: }
! 2009:
! 2010: }
! 2011:
! 2012: /* plot_lines:
! 2013: * Plot the curves in LINES style
! 2014: */
! 2015: static void plot_lines(plot)
! 2016: struct curve_points *plot;
! 2017: {
! 2018: int i; /* point index */
! 2019: int x, y; /* point in terminal coordinates */
! 2020: struct termentry *t = term;
! 2021: enum coord_type prev = UNDEFINED; /* type of previous point */
! 2022: double ex, ey; /* an edge point */
! 2023: double lx[2], ly[2]; /* two edge points */
! 2024:
! 2025: for (i = 0; i < plot->p_count; i++) {
! 2026: switch (plot->points[i].type) {
! 2027: case INRANGE:{
! 2028: x = map_x(plot->points[i].x);
! 2029: y = map_y(plot->points[i].y);
! 2030:
! 2031: if (prev == INRANGE) {
! 2032: (*t->vector) (x, y);
! 2033: } else if (prev == OUTRANGE) {
! 2034: /* from outrange to inrange */
! 2035: if (!clip_lines1) {
! 2036: (*t->move) (x, y);
! 2037: } else {
! 2038: edge_intersect(plot->points, i, &ex, &ey);
! 2039: (*t->move) (map_x(ex), map_y(ey));
! 2040: (*t->vector) (x, y);
! 2041: }
! 2042: } else { /* prev == UNDEFINED */
! 2043: (*t->move) (x, y);
! 2044: (*t->vector) (x, y);
! 2045: }
! 2046:
! 2047: break;
! 2048: }
! 2049: case OUTRANGE:{
! 2050: if (prev == INRANGE) {
! 2051: /* from inrange to outrange */
! 2052: if (clip_lines1) {
! 2053: edge_intersect(plot->points, i, &ex, &ey);
! 2054: (*t->vector) (map_x(ex), map_y(ey));
! 2055: }
! 2056: } else if (prev == OUTRANGE) {
! 2057: /* from outrange to outrange */
! 2058: if (clip_lines2) {
! 2059: if (two_edge_intersect(plot->points, i, lx, ly)) {
! 2060: (*t->move) (map_x(lx[0]), map_y(ly[0]));
! 2061: (*t->vector) (map_x(lx[1]), map_y(ly[1]));
! 2062: }
! 2063: }
! 2064: }
! 2065: break;
! 2066: }
! 2067: default: /* just a safety */
! 2068: case UNDEFINED:{
! 2069: break;
! 2070: }
! 2071: }
! 2072: prev = plot->points[i].type;
! 2073: }
! 2074: }
! 2075:
! 2076: /* XXX - JG */
! 2077: /* plot_steps:
! 2078: * Plot the curves in STEPS style
! 2079: */
! 2080: static void plot_steps(plot)
! 2081: struct curve_points *plot;
! 2082: {
! 2083: int i; /* point index */
! 2084: int x, y; /* point in terminal coordinates */
! 2085: struct termentry *t = term;
! 2086: enum coord_type prev = UNDEFINED; /* type of previous point */
! 2087: double ex, ey; /* an edge point */
! 2088: double lx[2], ly[2]; /* two edge points */
! 2089: int yprev = 0; /* previous point coordinates */
! 2090:
! 2091: for (i = 0; i < plot->p_count; i++) {
! 2092: switch (plot->points[i].type) {
! 2093: case INRANGE:{
! 2094: x = map_x(plot->points[i].x);
! 2095: y = map_y(plot->points[i].y);
! 2096:
! 2097: if (prev == INRANGE) {
! 2098: (*t->vector) (x, yprev);
! 2099: (*t->vector) (x, y);
! 2100: } else if (prev == OUTRANGE) {
! 2101: /* from outrange to inrange */
! 2102: if (!clip_lines1) {
! 2103: (*t->move) (x, y);
! 2104: } else { /* find edge intersection */
! 2105: edge_intersect_steps(plot->points, i, &ex, &ey);
! 2106: (*t->move) (map_x(ex), map_y(ey));
! 2107: (*t->vector) (x, map_y(ey));
! 2108: (*t->vector) (x, y);
! 2109: }
! 2110: } else { /* prev == UNDEFINED */
! 2111: (*t->move) (x, y);
! 2112: (*t->vector) (x, y);
! 2113: }
! 2114: yprev = y;
! 2115: break;
! 2116: }
! 2117: case OUTRANGE:{
! 2118: if (prev == INRANGE) {
! 2119: /* from inrange to outrange */
! 2120: if (clip_lines1) {
! 2121: edge_intersect_steps(plot->points, i, &ex, &ey);
! 2122: (*t->vector) (map_x(ex), yprev);
! 2123: (*t->vector) (map_x(ex), map_y(ey));
! 2124: }
! 2125: } else if (prev == OUTRANGE) {
! 2126: /* from outrange to outrange */
! 2127: if (clip_lines2) {
! 2128: if (two_edge_intersect_steps(plot->points, i, lx, ly)) {
! 2129: (*t->move) (map_x(lx[0]), map_y(ly[0]));
! 2130: (*t->vector) (map_x(lx[1]), map_y(ly[0]));
! 2131: (*t->vector) (map_x(lx[1]), map_y(ly[1]));
! 2132: }
! 2133: }
! 2134: }
! 2135: break;
! 2136: }
! 2137: default: /* just a safety */
! 2138: case UNDEFINED:{
! 2139: break;
! 2140: }
! 2141: }
! 2142: prev = plot->points[i].type;
! 2143: }
! 2144: }
! 2145: /* XXX - HOE */
! 2146: /* plot_fsteps:
! 2147: * Plot the curves in STEPS style by step on forward yvalue
! 2148: */
! 2149: static void plot_fsteps(plot)
! 2150: struct curve_points *plot;
! 2151: {
! 2152: int i; /* point index */
! 2153: int x, y; /* point in terminal coordinates */
! 2154: struct termentry *t = term;
! 2155: enum coord_type prev = UNDEFINED; /* type of previous point */
! 2156: double ex, ey; /* an edge point */
! 2157: double lx[2], ly[2]; /* two edge points */
! 2158: int xprev = 0; /* previous point coordinates */
! 2159:
! 2160: for (i = 0; i < plot->p_count; i++) {
! 2161: switch (plot->points[i].type) {
! 2162: case INRANGE:{
! 2163: x = map_x(plot->points[i].x);
! 2164: y = map_y(plot->points[i].y);
! 2165:
! 2166: if (prev == INRANGE) {
! 2167: (*t->vector) (xprev, y);
! 2168: (*t->vector) (x, y);
! 2169: } else if (prev == OUTRANGE) {
! 2170: /* from outrange to inrange */
! 2171: if (!clip_lines1) {
! 2172: (*t->move) (x, y);
! 2173: } else { /* find edge intersection */
! 2174: edge_intersect_fsteps(plot->points, i, &ex, &ey);
! 2175: (*t->move) (map_x(ex), map_y(ey));
! 2176: (*t->vector) (map_x(ex), y);
! 2177: (*t->vector) (x, y);
! 2178: }
! 2179: } else { /* prev == UNDEFINED */
! 2180: (*t->move) (x, y);
! 2181: (*t->vector) (x, y);
! 2182: }
! 2183: xprev = x;
! 2184: break;
! 2185: }
! 2186: case OUTRANGE:{
! 2187: if (prev == INRANGE) {
! 2188: /* from inrange to outrange */
! 2189: if (clip_lines1) {
! 2190: edge_intersect_fsteps(plot->points, i, &ex, &ey);
! 2191: (*t->vector) (xprev, map_y(ey));
! 2192: (*t->vector) (map_x(ex), map_y(ey));
! 2193: }
! 2194: } else if (prev == OUTRANGE) {
! 2195: /* from outrange to outrange */
! 2196: if (clip_lines2) {
! 2197: if (two_edge_intersect_fsteps(plot->points, i, lx, ly)) {
! 2198: (*t->move) (map_x(lx[0]), map_y(ly[0]));
! 2199: (*t->vector) (map_x(lx[0]), map_y(ly[1]));
! 2200: (*t->vector) (map_x(lx[1]), map_y(ly[1]));
! 2201: }
! 2202: }
! 2203: }
! 2204: break;
! 2205: }
! 2206: default: /* just a safety */
! 2207: case UNDEFINED:{
! 2208: break;
! 2209: }
! 2210: }
! 2211: prev = plot->points[i].type;
! 2212: }
! 2213: }
! 2214:
! 2215: /* CAC */
! 2216: /* plot_histeps:
! 2217: * Plot the curves in HISTEPS style
! 2218: */
! 2219: static void plot_histeps(plot)
! 2220: struct curve_points *plot;
! 2221: {
! 2222: int i; /* point index */
! 2223: int hold, bigi; /* indices for sorting */
! 2224: int xl, yl; /* cursor position in terminal coordinates */
! 2225: struct termentry *t = term;
! 2226: double x, y, xn, yn; /* point position */
! 2227: int *gl, goodcount; /* array to hold list of valid points */
! 2228:
! 2229: /* preliminary count of points inside array */
! 2230: goodcount = 0;
! 2231: for (i = 0; i < plot->p_count; i++)
! 2232: if (plot->points[i].type == INRANGE ||
! 2233: plot->points[i].type == OUTRANGE)
! 2234: ++goodcount;
! 2235: if (goodcount < 2)
! 2236: return; /* cannot plot less than 2 points */
! 2237:
! 2238: gl = (int *) gp_alloc(goodcount * sizeof(int), "histeps valid point mapping");
! 2239: if (gl == NULL)
! 2240: return;
! 2241:
! 2242: /* fill gl array with indexes of valid (non-undefined) points. */
! 2243: goodcount = 0;
! 2244: for (i = 0; i < plot->p_count; i++)
! 2245: if (plot->points[i].type == INRANGE ||
! 2246: plot->points[i].type == OUTRANGE) {
! 2247: gl[goodcount] = i;
! 2248: ++goodcount;
! 2249: }
! 2250: /* sort the data */
! 2251: for (bigi = i = 1; i < goodcount;) {
! 2252: if (plot->points[gl[i]].x < plot->points[gl[i - 1]].x) {
! 2253: hold = gl[i];
! 2254: gl[i] = gl[i - 1];
! 2255: gl[i - 1] = hold;
! 2256: if (i > 1) {
! 2257: i--;
! 2258: continue;
! 2259: }
! 2260: }
! 2261: i = ++bigi;
! 2262: }
! 2263:
! 2264: x = (3.0 * plot->points[gl[0]].x - plot->points[gl[1]].x) / 2.0;
! 2265: y = 0.0;
! 2266:
! 2267: xl = map_x(x);
! 2268: yl = map_y(y);
! 2269: (*t->move) (xl, yl);
! 2270:
! 2271: for (i = 0; i < goodcount - 1; i++) { /* loop over all points except last */
! 2272:
! 2273: yn = plot->points[gl[i]].y;
! 2274: xn = (plot->points[gl[i]].x + plot->points[gl[i + 1]].x) / 2.0;
! 2275: histeps_vertical(&xl, &yl, x, y, yn);
! 2276: histeps_horizontal(&xl, &yl, x, xn, yn);
! 2277:
! 2278: x = xn;
! 2279: y = yn;
! 2280: }
! 2281:
! 2282: yn = plot->points[gl[i]].y;
! 2283: xn = (3.0 * plot->points[gl[i]].x - plot->points[gl[i - 1]].x) / 2.0;
! 2284: histeps_vertical(&xl, &yl, x, y, yn);
! 2285: histeps_horizontal(&xl, &yl, x, xn, yn);
! 2286: histeps_vertical(&xl, &yl, xn, yn, 0.0);
! 2287:
! 2288: free(gl);
! 2289: }
! 2290: /* CAC
! 2291: * Draw vertical line for the histeps routine.
! 2292: * Performs clipping.
! 2293: */
! 2294: static void histeps_vertical(xl, yl, x, y1, y2)
! 2295: int *xl, *yl; /* keeps track of "cursor" position */
! 2296: double x, y1, y2; /* coordinates of vertical line */
! 2297: {
! 2298: struct termentry *t = term;
! 2299: /* global x_min, x_max, y_min, y_max */
! 2300: int xm, y1m, y2m;
! 2301:
! 2302: if ((y1 < y_min && y2 < y_min) ||
! 2303: (y1 > y_max && y2 > y_max) ||
! 2304: x < x_min ||
! 2305: x > x_max)
! 2306: return;
! 2307:
! 2308: if (y1 < y_min)
! 2309: y1 = y_min;
! 2310: if (y1 > y_max)
! 2311: y1 = y_max;
! 2312: if (y2 < y_min)
! 2313: y2 = y_min;
! 2314: if (y2 > y_max)
! 2315: y2 = y_max;
! 2316:
! 2317: xm = map_x(x);
! 2318: y1m = map_y(y1);
! 2319: y2m = map_y(y2);
! 2320:
! 2321: if (y1m != *yl || xm != *xl)
! 2322: (*t->move) (xm, y1m);
! 2323: (*t->vector) (xm, y2m);
! 2324: *xl = xm;
! 2325: *yl = y2m;
! 2326:
! 2327: return;
! 2328: }
! 2329: /* CAC
! 2330: * Draw horizontal line for the histeps routine.
! 2331: * Performs clipping.
! 2332: */
! 2333: static void histeps_horizontal(xl, yl, x1, x2, y)
! 2334: int *xl, *yl; /* keeps track of "cursor" position */
! 2335: double x1, x2, y; /* coordinates of vertical line */
! 2336: {
! 2337: struct termentry *t = term;
! 2338: /* global x_min, x_max, y_min, y_max */
! 2339: int x1m, x2m, ym;
! 2340:
! 2341: if ((x1 < x_min && x2 < x_min) ||
! 2342: (x1 > x_max && x2 > x_max) ||
! 2343: y < y_min ||
! 2344: y > y_max)
! 2345: return;
! 2346:
! 2347: if (x1 < x_min)
! 2348: x1 = x_min;
! 2349: if (x1 > x_max)
! 2350: x1 = x_max;
! 2351: if (x2 < x_min)
! 2352: x2 = x_min;
! 2353: if (x2 > x_max)
! 2354: x2 = x_max;
! 2355:
! 2356: ym = map_y(y);
! 2357: x1m = map_x(x1);
! 2358: x2m = map_x(x2);
! 2359:
! 2360: if (x1m != *xl || ym != *yl)
! 2361: (*t->move) (x1m, ym);
! 2362: (*t->vector) (x2m, ym);
! 2363: *xl = x2m;
! 2364: *yl = ym;
! 2365:
! 2366: return;
! 2367: }
! 2368:
! 2369:
! 2370: /* plot_bars:
! 2371: * Plot the curves in ERRORBARS style
! 2372: * we just plot the bars; the points are plotted in plot_points
! 2373: */
! 2374: static void plot_bars(plot)
! 2375: struct curve_points *plot;
! 2376: {
! 2377: int i; /* point index */
! 2378: struct termentry *t = term;
! 2379: double x, y; /* position of the bar */
! 2380: double ylow, yhigh; /* the ends of the bars */
! 2381: double xlow, xhigh;
! 2382: double x1, y1, x2, y2, slope; /* parameters for polar error bars */
! 2383: unsigned int xM, ylowM, yhighM; /* the mapped version of above */
! 2384: unsigned int yM, xlowM, xhighM;
! 2385: TBOOLEAN low_inrange, high_inrange;
! 2386: int tic = ERRORBARTIC;
! 2387:
! 2388: /* Limitation: no boxes with x errorbars */
! 2389:
! 2390: if ((plot->plot_style == YERRORBARS) || (plot->plot_style == XYERRORBARS) ||
! 2391: (plot->plot_style == BOXERROR)) {
! 2392: /* Draw the vertical part of the bar */
! 2393: for (i = 0; i < plot->p_count; i++) {
! 2394: /* undefined points don't count */
! 2395: if (plot->points[i].type == UNDEFINED)
! 2396: continue;
! 2397:
! 2398: /* check to see if in xrange */
! 2399: x = plot->points[i].x;
! 2400: if (!inrange(x, x_min, x_max))
! 2401: continue;
! 2402: xM = map_x(x);
! 2403:
! 2404: /* check to see if in yrange */
! 2405: y = plot->points[i].y;
! 2406: if (!inrange(y, y_min, y_max))
! 2407: continue;
! 2408: yM = map_y(y);
! 2409:
! 2410: /* find low and high points of bar, and check yrange */
! 2411: yhigh = plot->points[i].yhigh;
! 2412: ylow = plot->points[i].ylow;
! 2413:
! 2414: high_inrange = inrange(yhigh, y_min, y_max);
! 2415: low_inrange = inrange(ylow, y_min, y_max);
! 2416:
! 2417: /* compute the plot position of yhigh */
! 2418: if (high_inrange)
! 2419: yhighM = map_y(yhigh);
! 2420: else if (samesign(yhigh - y_max, y_max - y_min))
! 2421: yhighM = map_y(y_max);
! 2422: else
! 2423: yhighM = map_y(y_min);
! 2424:
! 2425: /* compute the plot position of ylow */
! 2426: if (low_inrange)
! 2427: ylowM = map_y(ylow);
! 2428: else if (samesign(ylow - y_max, y_max - y_min))
! 2429: ylowM = map_y(y_max);
! 2430: else
! 2431: ylowM = map_y(y_min);
! 2432:
! 2433: if (!high_inrange && !low_inrange && ylowM == yhighM)
! 2434: /* both out of range on the same side */
! 2435: continue;
! 2436:
! 2437: /* find low and high points of bar, and check xrange */
! 2438: xhigh = plot->points[i].xhigh;
! 2439: xlow = plot->points[i].xlow;
! 2440:
! 2441: high_inrange = inrange(xhigh, x_min, x_max);
! 2442: low_inrange = inrange(xlow, x_min, x_max);
! 2443:
! 2444: /* compute the plot position of xhigh */
! 2445: if (high_inrange)
! 2446: xhighM = map_x(xhigh);
! 2447: else if (samesign(xhigh - x_max, x_max - x_min))
! 2448: xhighM = map_x(x_max);
! 2449: else
! 2450: xhighM = map_x(x_min);
! 2451:
! 2452: /* compute the plot position of xlow */
! 2453: if (low_inrange)
! 2454: xlowM = map_x(xlow);
! 2455: else if (samesign(xlow - x_max, x_max - x_min))
! 2456: xlowM = map_x(x_max);
! 2457: else
! 2458: xlowM = map_x(x_min);
! 2459:
! 2460: if (!high_inrange && !low_inrange && xlowM == xhighM)
! 2461: /* both out of range on the same side */
! 2462: continue;
! 2463:
! 2464: /* by here everything has been mapped */
! 2465: if (!polar) {
! 2466: /* HBB 981130: use Igor's routine *only* for polar errorbars */
! 2467: (*t->move) (xM, ylowM);
! 2468: /* draw the main bar */
! 2469: (*t->vector) (xM, yhighM);
! 2470: if (bar_size > 0.0) {
! 2471: /* draw the bottom tic */
! 2472: (*t->move) ((unsigned int) (xM - bar_size * tic), ylowM);
! 2473: (*t->vector) ((unsigned int) (xM + bar_size * tic), ylowM);
! 2474: /* draw the top tic */
! 2475: (*t->move) ((unsigned int) (xM - bar_size * tic), yhighM);
! 2476: (*t->vector) ((unsigned int) (xM + bar_size * tic), yhighM);
! 2477: }
! 2478: } else {
! 2479: /* HBB 981130: see above */
! 2480: /* The above has been replaced by Igor inorder to get errorbars
! 2481: coming out in polar mode AND to stop the bar from going
! 2482: through the symbol */
! 2483: if ((xhighM - xlowM) * (xhighM - xlowM) + (yhighM - ylowM) * (yhighM - ylowM)
! 2484: > pointsize * tic * pointsize * tic * 4.5) {
! 2485: /* Only plot the error bar if it is bigger than the
! 2486: * symbol */
! 2487: /* The factor of 4.5 should strictly be 4.0, but it looks
! 2488: * better to drop the error bar if it is only slightly
! 2489: * bigger than the symbol, Igor. */
! 2490: if (xlowM == xhighM) {
! 2491: (*t->move) (xM, ylowM);
! 2492: /* draw the main bar to the symbol end */
! 2493: (*t->vector) (xM, (unsigned int) (yM - pointsize * tic));
! 2494: (*t->move) (xM, (unsigned int) (yM + pointsize * tic));
! 2495: /* draw the other part of the main bar */
! 2496: (*t->vector) (xM, yhighM);
! 2497: } else {
! 2498: (*t->move) (xlowM, ylowM);
! 2499: /* draw the main bar in polar mode. Note that here
! 2500: * the bar is drawn through the symbol. I tried to
! 2501: * fix this, but got into trouble with the two bars
! 2502: * (on either side of symbol) not being perfectly
! 2503: * parallel due to mapping considerations. Igor
! 2504: */
! 2505: (*t->vector) (xhighM, yhighM);
! 2506: }
! 2507: if (bar_size > 0.0) {
! 2508: /* The following attempts to ensure that the tics
! 2509: * are perpendicular to the error bar, Igor. */
! 2510: /*perpendicular to the main bar */
! 2511: slope = (xlowM * 1.0 - xhighM * 1.0) / (yhighM * 1.0 - ylowM * 1.0 + 1e-10);
! 2512: x1 = xlowM + bar_size * tic / sqrt(1.0 + slope * slope);
! 2513: x2 = xlowM - bar_size * tic / sqrt(1.0 + slope * slope);
! 2514: y1 = slope * (x1 - xlowM) + ylowM;
! 2515: y2 = slope * (x2 - xlowM) + ylowM;
! 2516:
! 2517: /* draw the bottom tic */
! 2518: (*t->move) ((unsigned int) x1, (unsigned int) y1);
! 2519: (*t->vector) ((unsigned int) x2, (unsigned int) y2);
! 2520:
! 2521: x1 = xhighM + bar_size * tic / sqrt(1.0 + slope * slope);
! 2522: x2 = xhighM - bar_size * tic / sqrt(1.0 + slope * slope);
! 2523: y1 = slope * (x1 - xhighM) + yhighM;
! 2524: y2 = slope * (x2 - xhighM) + yhighM;
! 2525: /* draw the top tic */
! 2526: (*t->move) ((unsigned int) x1, (unsigned int) y1);
! 2527: (*t->vector) ((unsigned int) x2, (unsigned int) y2);
! 2528: } /* if error bar is bigger than symbol */
! 2529: }
! 2530: } /* HBB 981130: see above */
! 2531: } /* for loop */
! 2532: } /* if yerrorbars OR xyerrorbars */
! 2533: if ((plot->plot_style == XERRORBARS) || (plot->plot_style == XYERRORBARS)) {
! 2534:
! 2535: /* Draw the horizontal part of the bar */
! 2536: for (i = 0; i < plot->p_count; i++) {
! 2537: /* undefined points don't count */
! 2538: if (plot->points[i].type == UNDEFINED)
! 2539: continue;
! 2540:
! 2541: /* check to see if in yrange */
! 2542: y = plot->points[i].y;
! 2543: if (!inrange(y, y_min, y_max))
! 2544: continue;
! 2545: yM = map_y(y);
! 2546:
! 2547: /* find low and high points of bar, and check xrange */
! 2548: xhigh = plot->points[i].xhigh;
! 2549: xlow = plot->points[i].xlow;
! 2550:
! 2551: high_inrange = inrange(xhigh, x_min, x_max);
! 2552: low_inrange = inrange(xlow, x_min, x_max);
! 2553:
! 2554: /* compute the plot position of xhigh */
! 2555: if (high_inrange)
! 2556: xhighM = map_x(xhigh);
! 2557: else if (samesign(xhigh - x_max, x_max - x_min))
! 2558: xhighM = map_x(x_max);
! 2559: else
! 2560: xhighM = map_x(x_min);
! 2561:
! 2562: /* compute the plot position of xlow */
! 2563: if (low_inrange)
! 2564: xlowM = map_x(xlow);
! 2565: else if (samesign(xlow - x_max, x_max - x_min))
! 2566: xlowM = map_x(x_max);
! 2567: else
! 2568: xlowM = map_x(x_min);
! 2569:
! 2570: if (!high_inrange && !low_inrange && xlowM == xhighM)
! 2571: /* both out of range on the same side */
! 2572: continue;
! 2573:
! 2574: /* by here everything has been mapped */
! 2575: (*t->move) (xlowM, yM);
! 2576: (*t->vector) (xhighM, yM); /* draw the main bar */
! 2577: if (bar_size > 0.0) {
! 2578: (*t->move) (xlowM, (unsigned int) (yM - bar_size * tic)); /* draw the left tic */
! 2579: (*t->vector) (xlowM, (unsigned int) (yM + bar_size * tic));
! 2580: (*t->move) (xhighM, (unsigned int) (yM - bar_size * tic)); /* draw the right tic */
! 2581: (*t->vector) (xhighM, (unsigned int) (yM + bar_size * tic));
! 2582: }
! 2583: } /* for loop */
! 2584: } /* if xerrorbars OR xyerrorbars */
! 2585: }
! 2586:
! 2587: /* plot_boxes:
! 2588: * Plot the curves in BOXES style
! 2589: */
! 2590: static void plot_boxes(plot, xaxis_y)
! 2591: struct curve_points *plot;
! 2592: int xaxis_y;
! 2593: {
! 2594: int i; /* point index */
! 2595: int xl, xr, yt; /* point in terminal coordinates */
! 2596: double dxl, dxr, dyt;
! 2597: struct termentry *t = term;
! 2598: enum coord_type prev = UNDEFINED; /* type of previous point */
! 2599: TBOOLEAN boxxy = (plot->plot_style == BOXXYERROR);
! 2600:
! 2601: for (i = 0; i < plot->p_count; i++) {
! 2602:
! 2603: switch (plot->points[i].type) {
! 2604: case OUTRANGE:
! 2605: case INRANGE:{
! 2606: if (plot->points[i].z < 0.0) {
! 2607: /* need to auto-calc width */
! 2608: /* ASSERT(boxwidth <= 0.0); - else graphics.c
! 2609: * provides width */
! 2610:
! 2611: /* calculate width */
! 2612: if (prev != UNDEFINED)
! 2613: dxl = (plot->points[i - 1].x - plot->points[i].x) / 2.0;
! 2614: else
! 2615: dxl = 0.0;
! 2616:
! 2617: if (i < plot->p_count - 1) {
! 2618: if (plot->points[i + 1].type != UNDEFINED)
! 2619: dxr = (plot->points[i + 1].x - plot->points[i].x) / 2.0;
! 2620: else
! 2621: dxr = -dxl;
! 2622: } else {
! 2623: dxr = -dxl;
! 2624: }
! 2625:
! 2626: if (prev == UNDEFINED)
! 2627: dxl = -dxr;
! 2628:
! 2629: dxl = plot->points[i].x + dxl;
! 2630: dxr = plot->points[i].x + dxr;
! 2631: } else { /* z >= 0 */
! 2632: dxr = plot->points[i].xhigh;
! 2633: dxl = plot->points[i].xlow;
! 2634: }
! 2635:
! 2636: if (boxxy) {
! 2637: dyt = plot->points[i].yhigh;
! 2638: xaxis_y = map_y(plot->points[i].ylow);
! 2639: } else {
! 2640: dyt = plot->points[i].y;
! 2641: }
! 2642:
! 2643: /* clip to border */
! 2644: if ((y_min < y_max && dyt < y_min)
! 2645: || (y_max < y_min && dyt > y_min))
! 2646: dyt = y_min;
! 2647: if ((y_min < y_max && dyt > y_max)
! 2648: || (y_max < y_min && dyt < y_max))
! 2649: dyt = y_max;
! 2650: if ((x_min < x_max && dxr < x_min)
! 2651: || (x_max < x_min && dxr > x_min))
! 2652: dxr = x_min;
! 2653: if ((x_min < x_max && dxr > x_max)
! 2654: || (x_max < x_min && dxr < x_max))
! 2655: dxr = x_max;
! 2656: if ((x_min < x_max && dxl < x_min)
! 2657: || (x_max < x_min && dxl > x_min))
! 2658: dxl = x_min;
! 2659: if ((x_min < x_max && dxl > x_max)
! 2660: || (x_max < x_min && dxl < x_max))
! 2661: dxl = x_max;
! 2662:
! 2663: xl = map_x(dxl);
! 2664: xr = map_x(dxr);
! 2665: yt = map_y(dyt);
! 2666:
! 2667: (*t->move) (xl, xaxis_y);
! 2668: (*t->vector) (xl, yt);
! 2669: (*t->vector) (xr, yt);
! 2670: (*t->vector) (xr, xaxis_y);
! 2671: (*t->vector) (xl, xaxis_y);
! 2672: break;
! 2673: } /* case OUTRANGE, INRANGE */
! 2674:
! 2675: default: /* just a safety */
! 2676: case UNDEFINED:{
! 2677: break;
! 2678: }
! 2679:
! 2680: } /* switch point-type */
! 2681:
! 2682: prev = plot->points[i].type;
! 2683:
! 2684: } /*loop */
! 2685: }
! 2686:
! 2687:
! 2688:
! 2689: /* plot_points:
! 2690: * Plot the curves in POINTSTYLE style
! 2691: */
! 2692: static void plot_points(plot)
! 2693: struct curve_points *plot;
! 2694: {
! 2695: int i;
! 2696: int x, y;
! 2697: struct termentry *t = term;
! 2698:
! 2699: for (i = 0; i < plot->p_count; i++) {
! 2700: if (plot->points[i].type == INRANGE) {
! 2701: x = map_x(plot->points[i].x);
! 2702: y = map_y(plot->points[i].y);
! 2703: /* do clipping if necessary */
! 2704: if (!clip_points ||
! 2705: (x >= xleft + p_width && y >= ybot + p_height
! 2706: && x <= xright - p_width && y <= ytop - p_height))
! 2707: (*t->point) (x, y, plot->lp_properties.p_type);
! 2708: }
! 2709: }
! 2710: }
! 2711:
! 2712: /* plot_dots:
! 2713: * Plot the curves in DOTS style
! 2714: */
! 2715: static void plot_dots(plot)
! 2716: struct curve_points *plot;
! 2717: {
! 2718: int i;
! 2719: int x, y;
! 2720: struct termentry *t = term;
! 2721:
! 2722: for (i = 0; i < plot->p_count; i++) {
! 2723: if (plot->points[i].type == INRANGE) {
! 2724: x = map_x(plot->points[i].x);
! 2725: y = map_y(plot->points[i].y);
! 2726: /* point type -1 is a dot */
! 2727: (*t->point) (x, y, -1);
! 2728: }
! 2729: }
! 2730: }
! 2731:
! 2732: /* plot_vectors:
! 2733: * Plot the curves in VECTORS style
! 2734: */
! 2735: static void plot_vectors(plot)
! 2736: struct curve_points *plot;
! 2737: {
! 2738: int i;
! 2739: int x1, y1, x2, y2;
! 2740: struct termentry *t = term;
! 2741:
! 2742: for (i = 0; i < plot->p_count; i++) {
! 2743: if (plot->points[i].type == INRANGE) {
! 2744: x1 = map_x(plot->points[i].xlow);
! 2745: y1 = map_y(plot->points[i].ylow);
! 2746: x2 = map_x(plot->points[i].xhigh);
! 2747: y2 = map_y(plot->points[i].yhigh);
! 2748: (*t->arrow) (x1, y1, x2, y2, TRUE);
! 2749: }
! 2750: }
! 2751: }
! 2752:
! 2753:
! 2754: /* plot_f_bars() - finance bars */
! 2755: static void plot_f_bars(plot)
! 2756: struct curve_points *plot;
! 2757: {
! 2758: int i; /* point index */
! 2759: struct termentry *t = term;
! 2760: double x; /* position of the bar */
! 2761: double ylow, yhigh, yclose, yopen; /* the ends of the bars */
! 2762: unsigned int xM, ylowM, yhighM; /* the mapped version of above */
! 2763: TBOOLEAN low_inrange, high_inrange;
! 2764: int tic = ERRORBARTIC / 2;
! 2765:
! 2766: for (i = 0; i < plot->p_count; i++) {
! 2767: /* undefined points don't count */
! 2768: if (plot->points[i].type == UNDEFINED)
! 2769: continue;
! 2770:
! 2771: /* check to see if in xrange */
! 2772: x = plot->points[i].x;
! 2773: if (!inrange(x, x_min, x_max))
! 2774: continue;
! 2775: xM = map_x(x);
! 2776:
! 2777: /* find low and high points of bar, and check yrange */
! 2778: yhigh = plot->points[i].yhigh;
! 2779: ylow = plot->points[i].ylow;
! 2780: yclose = plot->points[i].z;
! 2781: yopen = plot->points[i].y;
! 2782:
! 2783: high_inrange = inrange(yhigh, y_min, y_max);
! 2784: low_inrange = inrange(ylow, y_min, y_max);
! 2785:
! 2786: /* compute the plot position of yhigh */
! 2787: if (high_inrange)
! 2788: yhighM = map_y(yhigh);
! 2789: else if (samesign(yhigh - y_max, y_max - y_min))
! 2790: yhighM = map_y(y_max);
! 2791: else
! 2792: yhighM = map_y(y_min);
! 2793:
! 2794: /* compute the plot position of ylow */
! 2795: if (low_inrange)
! 2796: ylowM = map_y(ylow);
! 2797: else if (samesign(ylow - y_max, y_max - y_min))
! 2798: ylowM = map_y(y_max);
! 2799: else
! 2800: ylowM = map_y(y_min);
! 2801:
! 2802: if (!high_inrange && !low_inrange && ylowM == yhighM)
! 2803: /* both out of range on the same side */
! 2804: continue;
! 2805:
! 2806: /* by here everything has been mapped */
! 2807: (*t->move) (xM, ylowM);
! 2808: (*t->vector) (xM, yhighM); /* draw the main bar */
! 2809: /* draw the open tic */
! 2810: (*t->move) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
! 2811: (*t->vector) (xM, map_y(yopen));
! 2812: /* draw the close tic */
! 2813: (*t->move) ((unsigned int) (xM + bar_size * tic), map_y(yclose));
! 2814: (*t->vector) (xM, map_y(yclose));
! 2815: }
! 2816: }
! 2817:
! 2818:
! 2819: /* plot_c_bars:
! 2820: * Plot the curves in CANDLESTICSK style
! 2821: * we just plot the bars; the points are not plotted
! 2822: */
! 2823: static void plot_c_bars(plot)
! 2824: struct curve_points *plot;
! 2825: {
! 2826: int i; /* point index */
! 2827: struct termentry *t = term;
! 2828: double x; /* position of the bar */
! 2829: double ylow, yhigh, yclose, yopen; /* the ends of the bars */
! 2830: unsigned int xM, ylowM, yhighM; /* the mapped version of above */
! 2831: TBOOLEAN low_inrange, high_inrange;
! 2832: int tic = ERRORBARTIC / 2;
! 2833:
! 2834: for (i = 0; i < plot->p_count; i++) {
! 2835: /* undefined points don't count */
! 2836: if (plot->points[i].type == UNDEFINED)
! 2837: continue;
! 2838:
! 2839: /* check to see if in xrange */
! 2840: x = plot->points[i].x;
! 2841: if (!inrange(x, x_min, x_max))
! 2842: continue;
! 2843: xM = map_x(x);
! 2844:
! 2845: /* find low and high points of bar, and check yrange */
! 2846: yhigh = plot->points[i].yhigh;
! 2847: ylow = plot->points[i].ylow;
! 2848: yclose = plot->points[i].z;
! 2849: yopen = plot->points[i].y;
! 2850:
! 2851: high_inrange = inrange(yhigh, y_min, y_max);
! 2852: low_inrange = inrange(ylow, y_min, y_max);
! 2853:
! 2854: /* compute the plot position of yhigh */
! 2855: if (high_inrange)
! 2856: yhighM = map_y(yhigh);
! 2857: else if (samesign(yhigh - y_max, y_max - y_min))
! 2858: yhighM = map_y(y_max);
! 2859: else
! 2860: yhighM = map_y(y_min);
! 2861:
! 2862: /* compute the plot position of ylow */
! 2863: if (low_inrange)
! 2864: ylowM = map_y(ylow);
! 2865: else if (samesign(ylow - y_max, y_max - y_min))
! 2866: ylowM = map_y(y_max);
! 2867: else
! 2868: ylowM = map_y(y_min);
! 2869:
! 2870: if (!high_inrange && !low_inrange && ylowM == yhighM)
! 2871: /* both out of range on the same side */
! 2872: continue;
! 2873:
! 2874: /* by here everything has been mapped */
! 2875: if (yopen <= yclose) {
! 2876: (*t->move) (xM, ylowM);
! 2877: (*t->vector) (xM, map_y(yopen)); /* draw the lower bar */
! 2878: /* draw the open tic */
! 2879: (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
! 2880: /* draw the open tic */
! 2881: (*t->vector) ((unsigned int) (xM + bar_size * tic), map_y(yopen));
! 2882: (*t->vector) ((unsigned int) (xM + bar_size * tic), map_y(yclose));
! 2883: (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yclose));
! 2884: /* draw the open tic */
! 2885: (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
! 2886: (*t->move) (xM, map_y(yclose)); /* draw the close tic */
! 2887: (*t->vector) (xM, yhighM);
! 2888: } else {
! 2889: (*t->move) (xM, ylowM);
! 2890: (*t->vector) (xM, yhighM);
! 2891: /* draw the open tic */
! 2892: (*t->move) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
! 2893: /* draw the open tic */
! 2894: (*t->vector) ((unsigned int) (xM + bar_size * tic), map_y(yopen));
! 2895: (*t->vector) ((unsigned int) (xM + bar_size * tic), map_y(yclose));
! 2896: (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yclose));
! 2897: /* draw the open tic */
! 2898: (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
! 2899: /* draw the close tic */
! 2900: (*t->move) ((unsigned int) (xM - bar_size * tic / 2), map_y(yclose));
! 2901: /* draw the open tic */
! 2902: (*t->vector) ((unsigned int) (xM - bar_size * tic / 2), map_y(yopen));
! 2903: /* draw the close tic */
! 2904: (*t->move) ((unsigned int) (xM + bar_size * tic / 2), map_y(yclose));
! 2905: /* draw the open tic */
! 2906: (*t->vector) ((unsigned int) (xM + bar_size * tic / 2), map_y(yopen));
! 2907: }
! 2908:
! 2909: }
! 2910: }
! 2911:
! 2912: /* single edge intersection algorithm */
! 2913: /* Given two points, one inside and one outside the plot, return
! 2914: * the point where an edge of the plot intersects the line segment defined
! 2915: * by the two points.
! 2916: */
! 2917: static void edge_intersect(points, i, ex, ey)
! 2918: struct coordinate GPHUGE *points; /* the points array */
! 2919: int i; /* line segment from point i-1 to point i */
! 2920: double *ex, *ey; /* the point where it crosses an edge */
! 2921: {
! 2922: /* global x_min, x_max, y_min, x_max */
! 2923: double ix = points[i - 1].x;
! 2924: double iy = points[i - 1].y;
! 2925: double ox = points[i].x;
! 2926: double oy = points[i].y;
! 2927: double x, y; /* possible intersection point */
! 2928:
! 2929: if (points[i].type == INRANGE) {
! 2930: /* swap points around so that ix/ix/iz are INRANGE and
! 2931: * ox/oy/oz are OUTRANGE
! 2932: */
! 2933: x = ix;
! 2934: ix = ox;
! 2935: ox = x;
! 2936: y = iy;
! 2937: iy = oy;
! 2938: oy = y;
! 2939: }
! 2940: /* nasty degenerate cases, effectively drawing to an infinity point (?)
! 2941: * cope with them here, so don't process them as a "real" OUTRANGE point
! 2942: *
! 2943: * If more than one coord is -VERYLARGE, then can't ratio the "infinities"
! 2944: * so drop out by returning the INRANGE point.
! 2945: *
! 2946: * Obviously, only need to test the OUTRANGE point (coordinates) */
! 2947: if (ox == -VERYLARGE || oy == -VERYLARGE) {
! 2948: *ex = ix;
! 2949: *ey = iy;
! 2950:
! 2951: if (ox == -VERYLARGE) {
! 2952: /* can't get a direction to draw line, so simply
! 2953: * return INRANGE point */
! 2954: if (oy == -VERYLARGE)
! 2955: return;
! 2956:
! 2957: *ex = x_min;
! 2958: return;
! 2959: }
! 2960: /* obviously oy is -VERYLARGE and ox != -VERYLARGE */
! 2961: *ey = y_min;
! 2962: return;
! 2963: }
! 2964: /*
! 2965: * Can't have case (ix == ox && iy == oy) as one point
! 2966: * is INRANGE and one point is OUTRANGE.
! 2967: */
! 2968: if (iy == oy) {
! 2969: /* horizontal line */
! 2970: /* assume inrange(iy, y_min, y_max) */
! 2971: *ey = iy; /* == oy */
! 2972:
! 2973: if (inrange(x_max, ix, ox))
! 2974: *ex = x_max;
! 2975: else if (inrange(x_min, ix, ox))
! 2976: *ex = x_min;
! 2977: else {
! 2978: graph_error("error in edge_intersect");
! 2979: }
! 2980: return;
! 2981: } else if (ix == ox) {
! 2982: /* vertical line */
! 2983: /* assume inrange(ix, x_min, x_max) */
! 2984: *ex = ix; /* == ox */
! 2985:
! 2986: if (inrange(y_max, iy, oy))
! 2987: *ey = y_max;
! 2988: else if (inrange(y_min, iy, oy))
! 2989: *ey = y_min;
! 2990: else {
! 2991: graph_error("error in edge_intersect");
! 2992: }
! 2993: return;
! 2994: }
! 2995: /* slanted line of some kind */
! 2996:
! 2997: /* does it intersect y_min edge */
! 2998: if (inrange(y_min, iy, oy) && y_min != iy && y_min != oy) {
! 2999: x = ix + (y_min - iy) * ((ox - ix) / (oy - iy));
! 3000: if (inrange(x, x_min, x_max)) {
! 3001: *ex = x;
! 3002: *ey = y_min;
! 3003: return; /* yes */
! 3004: }
! 3005: }
! 3006: /* does it intersect y_max edge */
! 3007: if (inrange(y_max, iy, oy) && y_max != iy && y_max != oy) {
! 3008: x = ix + (y_max - iy) * ((ox - ix) / (oy - iy));
! 3009: if (inrange(x, x_min, x_max)) {
! 3010: *ex = x;
! 3011: *ey = y_max;
! 3012: return; /* yes */
! 3013: }
! 3014: }
! 3015: /* does it intersect x_min edge */
! 3016: if (inrange(x_min, ix, ox) && x_min != ix && x_min != ox) {
! 3017: y = iy + (x_min - ix) * ((oy - iy) / (ox - ix));
! 3018: if (inrange(y, y_min, y_max)) {
! 3019: *ex = x_min;
! 3020: *ey = y;
! 3021: return;
! 3022: }
! 3023: }
! 3024: /* does it intersect x_max edge */
! 3025: if (inrange(x_max, ix, ox) && x_max != ix && x_max != ox) {
! 3026: y = iy + (x_max - ix) * ((oy - iy) / (ox - ix));
! 3027: if (inrange(y, y_min, y_max)) {
! 3028: *ex = x_max;
! 3029: *ey = y;
! 3030: return;
! 3031: }
! 3032: }
! 3033: /* If we reach here, the inrange point is on the edge, and
! 3034: * the line segment from the outrange point does not cross any
! 3035: * other edges to get there. In this case, we return the inrange
! 3036: * point as the 'edge' intersection point. This will basically draw
! 3037: * line.
! 3038: */
! 3039: *ex = ix;
! 3040: *ey = iy;
! 3041: return;
! 3042: }
! 3043:
! 3044: /* XXX - JG */
! 3045: /* single edge intersection algorithm for "steps" curves */
! 3046: /*
! 3047: * Given two points, one inside and one outside the plot, return
! 3048: * the point where an edge of the plot intersects the line segments
! 3049: * forming the step between the two points.
! 3050: *
! 3051: * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from
! 3052: * P1 to P2 is drawn as two line segments: (x1,y1)->(x2,y1) and
! 3053: * (x2,y1)->(x2,y2).
! 3054: */
! 3055: static void edge_intersect_steps(points, i, ex, ey)
! 3056: struct coordinate GPHUGE *points; /* the points array */
! 3057: int i; /* line segment from point i-1 to point i */
! 3058: double *ex, *ey; /* the point where it crosses an edge */
! 3059: {
! 3060: /* global x_min, x_max, y_min, x_max */
! 3061: double ax = points[i - 1].x;
! 3062: double ay = points[i - 1].y;
! 3063: double bx = points[i].x;
! 3064: double by = points[i].y;
! 3065:
! 3066: if (points[i].type == INRANGE) { /* from OUTRANGE to INRANG */
! 3067: if (inrange(ay, y_min, y_max)) {
! 3068: *ey = ay;
! 3069: if (ax > x_max)
! 3070: *ex = x_max;
! 3071: else /* x < x_min */
! 3072: *ex = x_min;
! 3073: } else {
! 3074: *ex = bx;
! 3075: if (ay > y_max)
! 3076: *ey = y_max;
! 3077: else /* y < y_min */
! 3078: *ey = y_min;
! 3079: }
! 3080: } else { /* from INRANGE to OUTRANGE */
! 3081: if (inrange(bx, x_min, x_max)) {
! 3082: *ex = bx;
! 3083: if (by > y_max)
! 3084: *ey = y_max;
! 3085: else /* y < y_min */
! 3086: *ey = y_min;
! 3087: } else {
! 3088: *ey = ay;
! 3089: if (bx > x_max)
! 3090: *ex = x_max;
! 3091: else /* x < x_min */
! 3092: *ex = x_min;
! 3093: }
! 3094: }
! 3095: return;
! 3096: }
! 3097:
! 3098: /* XXX - HOE */
! 3099: /* single edge intersection algorithm for "fsteps" curves */
! 3100: /* fsteps means step on forward y-value.
! 3101: * Given two points, one inside and one outside the plot, return
! 3102: * the point where an edge of the plot intersects the line segments
! 3103: * forming the step between the two points.
! 3104: *
! 3105: * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from
! 3106: * P1 to P2 is drawn as two line segments: (x1,y1)->(x1,y2) and
! 3107: * (x1,y2)->(x2,y2).
! 3108: */
! 3109: static void edge_intersect_fsteps(points, i, ex, ey)
! 3110: struct coordinate GPHUGE *points; /* the points array */
! 3111: int i; /* line segment from point i-1 to point i */
! 3112: double *ex, *ey; /* the point where it crosses an edge */
! 3113: {
! 3114: /* global x_min, x_max, y_min, x_max */
! 3115: double ax = points[i - 1].x;
! 3116: double ay = points[i - 1].y;
! 3117: double bx = points[i].x;
! 3118: double by = points[i].y;
! 3119:
! 3120: if (points[i].type == INRANGE) { /* from OUTRANGE to INRANG */
! 3121: if (inrange(ax, x_min, x_max)) {
! 3122: *ex = ax;
! 3123: if (ay > y_max)
! 3124: *ey = y_max;
! 3125: else /* y < y_min */
! 3126: *ey = y_min;
! 3127: } else {
! 3128: *ey = by;
! 3129: if (bx > x_max)
! 3130: *ex = x_max;
! 3131: else /* x < x_min */
! 3132: *ex = x_min;
! 3133: }
! 3134: } else { /* from INRANGE to OUTRANGE */
! 3135: if (inrange(by, y_min, y_max)) {
! 3136: *ey = by;
! 3137: if (bx > x_max)
! 3138: *ex = x_max;
! 3139: else /* x < x_min */
! 3140: *ex = x_min;
! 3141: } else {
! 3142: *ex = ax;
! 3143: if (by > y_max)
! 3144: *ey = y_max;
! 3145: else /* y < y_min */
! 3146: *ey = y_min;
! 3147: }
! 3148: }
! 3149: return;
! 3150: }
! 3151:
! 3152: /* XXX - JG */
! 3153: /* double edge intersection algorithm for "steps" plot */
! 3154: /* Given two points, both outside the plot, return the points where an
! 3155: * edge of the plot intersects the line segments forming a step
! 3156: * by the two points. There may be zero, one, two, or an infinite number
! 3157: * of intersection points. (One means an intersection at a corner, infinite
! 3158: * means overlaying the edge itself). We return FALSE when there is nothing
! 3159: * to draw (zero intersections), and TRUE when there is something to
! 3160: * draw (the one-point case is a degenerate of the two-point case and we do
! 3161: * not distinguish it - we draw it anyway).
! 3162: *
! 3163: * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from
! 3164: * P1 to P2 is drawn as two line segments: (x1,y1)->(x2,y1) and
! 3165: * (x2,y1)->(x2,y2).
! 3166: */
! 3167: static TBOOLEAN /* any intersection? */
! 3168: two_edge_intersect_steps(points, i, lx, ly)
! 3169: struct coordinate GPHUGE *points; /* the points array */
! 3170: int i; /* line segment from point i-1 to point i */
! 3171: double *lx, *ly; /* lx[2], ly[2]: points where it crosses edges */
! 3172: {
! 3173: /* global x_min, x_max, y_min, x_max */
! 3174: double ax = points[i - 1].x;
! 3175: double ay = points[i - 1].y;
! 3176: double bx = points[i].x;
! 3177: double by = points[i].y;
! 3178:
! 3179: if (GPMAX(ax, bx) < x_min || GPMIN(ax, bx) > x_max ||
! 3180: GPMAX(ay, by) < y_min || GPMIN(ay, by) > y_max ||
! 3181: ((ay > y_max || ay < y_min) &&
! 3182: (bx > x_max || bx < x_min))) {
! 3183: return (FALSE);
! 3184: } else if (inrange(ay, y_min, y_max) && inrange(bx, x_min, x_max)) { /* corner of step inside plotspace */
! 3185: *ly++ = ay;
! 3186: if (ax < x_min)
! 3187: *lx++ = x_min;
! 3188: else
! 3189: *lx++ = x_max;
! 3190:
! 3191: *lx++ = bx;
! 3192: if (by < y_min)
! 3193: *ly++ = y_min;
! 3194: else
! 3195: *ly++ = y_max;
! 3196:
! 3197: return (TRUE);
! 3198: } else if (inrange(ay, y_min, y_max)) { /* cross plotspace in x-direction */
! 3199: *lx++ = x_min;
! 3200: *ly++ = ay;
! 3201: *lx++ = x_max;
! 3202: *ly++ = ay;
! 3203: return (TRUE);
! 3204: } else if (inrange(ax, x_min, x_max)) { /* cross plotspace in y-direction */
! 3205: *lx++ = bx;
! 3206: *ly++ = y_min;
! 3207: *lx++ = bx;
! 3208: *ly++ = y_max;
! 3209: return (TRUE);
! 3210: } else
! 3211: return (FALSE);
! 3212: }
! 3213:
! 3214: /* XXX - HOE */
! 3215: /* double edge intersection algorithm for "fsteps" plot */
! 3216: /* Given two points, both outside the plot, return the points where an
! 3217: * edge of the plot intersects the line segments forming a step
! 3218: * by the two points. There may be zero, one, two, or an infinite number
! 3219: * of intersection points. (One means an intersection at a corner, infinite
! 3220: * means overlaying the edge itself). We return FALSE when there is nothing
! 3221: * to draw (zero intersections), and TRUE when there is something to
! 3222: * draw (the one-point case is a degenerate of the two-point case and we do
! 3223: * not distinguish it - we draw it anyway).
! 3224: *
! 3225: * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from
! 3226: * P1 to P2 is drawn as two line segments: (x1,y1)->(x1,y2) and
! 3227: * (x1,y2)->(x2,y2).
! 3228: */
! 3229: static TBOOLEAN /* any intersection? */
! 3230: two_edge_intersect_fsteps(points, i, lx, ly)
! 3231: struct coordinate GPHUGE *points; /* the points array */
! 3232: int i; /* line segment from point i-1 to point i */
! 3233: double *lx, *ly; /* lx[2], ly[2]: points where it crosses edges */
! 3234: {
! 3235: /* global x_min, x_max, y_min, x_max */
! 3236: double ax = points[i - 1].x;
! 3237: double ay = points[i - 1].y;
! 3238: double bx = points[i].x;
! 3239: double by = points[i].y;
! 3240:
! 3241: if (GPMAX(ax, bx) < x_min || GPMIN(ax, bx) > x_max ||
! 3242: GPMAX(ay, by) < y_min || GPMIN(ay, by) > y_max ||
! 3243: ((by > y_max || by < y_min) &&
! 3244: (ax > x_max || ax < x_min))) {
! 3245: return (FALSE);
! 3246: } else if (inrange(by, y_min, y_max) && inrange(ax, x_min, x_max)) { /* corner of step inside plotspace */
! 3247: *lx++ = ax;
! 3248: if (ay < y_min)
! 3249: *ly++ = y_min;
! 3250: else
! 3251: *ly++ = y_max;
! 3252:
! 3253: *ly = by;
! 3254: if (bx < x_min)
! 3255: *lx = x_min;
! 3256: else
! 3257: *lx = x_max;
! 3258:
! 3259: return (TRUE);
! 3260: } else if (inrange(by, y_min, y_max)) { /* cross plotspace in x-direction */
! 3261: *lx++ = x_min;
! 3262: *ly++ = by;
! 3263: *lx = x_max;
! 3264: *ly = by;
! 3265: return (TRUE);
! 3266: } else if (inrange(ax, x_min, x_max)) { /* cross plotspace in y-direction */
! 3267: *lx++ = ax;
! 3268: *ly++ = y_min;
! 3269: *lx = ax;
! 3270: *ly = y_max;
! 3271: return (TRUE);
! 3272: } else
! 3273: return (FALSE);
! 3274: }
! 3275:
! 3276: /* double edge intersection algorithm */
! 3277: /* Given two points, both outside the plot, return
! 3278: * the points where an edge of the plot intersects the line segment defined
! 3279: * by the two points. There may be zero, one, two, or an infinite number
! 3280: * of intersection points. (One means an intersection at a corner, infinite
! 3281: * means overlaying the edge itself). We return FALSE when there is nothing
! 3282: * to draw (zero intersections), and TRUE when there is something to
! 3283: * draw (the one-point case is a degenerate of the two-point case and we do
! 3284: * not distinguish it - we draw it anyway).
! 3285: */
! 3286: static TBOOLEAN /* any intersection? */
! 3287: two_edge_intersect(points, i, lx, ly)
! 3288: struct coordinate GPHUGE *points; /* the points array */
! 3289: int i; /* line segment from point i-1 to point i */
! 3290: double *lx, *ly; /* lx[2], ly[2]: points where it crosses edges */
! 3291: {
! 3292: /* global x_min, x_max, y_min, x_max */
! 3293: int count;
! 3294: double ix = points[i - 1].x;
! 3295: double iy = points[i - 1].y;
! 3296: double ox = points[i].x;
! 3297: double oy = points[i].y;
! 3298: double t[4];
! 3299: double swap;
! 3300: double t_min, t_max;
! 3301: #if 0
! 3302: fprintf(stderr, "\ntwo_edge_intersect (%g, %g) and (%g, %g) : ",
! 3303: points[i - 1].x, points[i - 1].y,
! 3304: points[i].x, points[i].y);
! 3305: #endif
! 3306: /* nasty degenerate cases, effectively drawing to an infinity point (?)
! 3307: cope with them here, so don't process them as a "real" OUTRANGE point
! 3308:
! 3309: If more than one coord is -VERYLARGE, then can't ratio the "infinities"
! 3310: so drop out by returning FALSE */
! 3311:
! 3312: count = 0;
! 3313: if (ix == -VERYLARGE)
! 3314: count++;
! 3315: if (ox == -VERYLARGE)
! 3316: count++;
! 3317: if (iy == -VERYLARGE)
! 3318: count++;
! 3319: if (oy == -VERYLARGE)
! 3320: count++;
! 3321:
! 3322: /* either doesn't pass through graph area *or*
! 3323: can't ratio infinities to get a direction to draw line, so simply return(FALSE) */
! 3324: if (count > 1) {
! 3325: #if 0
! 3326: fprintf(stderr, "\tA\n");
! 3327: #endif
! 3328: return (FALSE);
! 3329: }
! 3330: if (ox == -VERYLARGE || ix == -VERYLARGE) {
! 3331: if (ix == -VERYLARGE) {
! 3332: /* swap points so ix/iy don't have a -VERYLARGE component */
! 3333: swap = ix;
! 3334: ix = ox;
! 3335: ox = swap;
! 3336: swap = iy;
! 3337: iy = oy;
! 3338: oy = swap;
! 3339: }
! 3340: /* check actually passes through the graph area */
! 3341: if (ix > x_max && inrange(iy, y_min, y_max)) {
! 3342: lx[0] = x_min;
! 3343: ly[0] = iy;
! 3344:
! 3345: lx[1] = x_max;
! 3346: ly[1] = iy;
! 3347: #if 0
! 3348: fprintf(stderr, "(%g %g) -> (%g %g)",
! 3349: lx[0], ly[0], lx[1], ly[1]);
! 3350: #endif
! 3351: return (TRUE);
! 3352: } else {
! 3353: #if 0
! 3354: fprintf(stderr, "\tB\n");
! 3355: #endif
! 3356: return (FALSE);
! 3357: }
! 3358: }
! 3359: if (oy == -VERYLARGE || iy == -VERYLARGE) {
! 3360: if (iy == -VERYLARGE) {
! 3361: /* swap points so ix/iy don't have a -VERYLARGE component */
! 3362: swap = ix;
! 3363: ix = ox;
! 3364: ox = swap;
! 3365: swap = iy;
! 3366: iy = oy;
! 3367: oy = swap;
! 3368: }
! 3369: /* check actually passes through the graph area */
! 3370: if (iy > y_max && inrange(ix, x_min, x_max)) {
! 3371: lx[0] = ix;
! 3372: ly[0] = y_min;
! 3373:
! 3374: lx[1] = ix;
! 3375: ly[1] = y_max;
! 3376: #if 0
! 3377: fprintf(stderr, "(%g %g) -> (%g %g)",
! 3378: lx[0], ly[0], lx[1], ly[1]);
! 3379: #endif
! 3380: return (TRUE);
! 3381: } else {
! 3382: #if 0
! 3383: fprintf(stderr, "\tC\n");
! 3384: #endif
! 3385: return (FALSE);
! 3386: }
! 3387: }
! 3388: /*
! 3389: * Special horizontal/vertical, etc. cases are checked and remaining
! 3390: * slant lines are checked separately.
! 3391: *
! 3392: * The slant line intersections are solved using the parametric form
! 3393: * of the equation for a line, since if we test x/y min/max planes explicitly
! 3394: * then e.g. a line passing through a corner point (x_min,y_min)
! 3395: * actually intersects 2 planes and hence further tests would be required
! 3396: * to anticipate this and similar situations.
! 3397: */
! 3398:
! 3399: /*
! 3400: * Can have case (ix == ox && iy == oy) as both points OUTRANGE
! 3401: */
! 3402: if (ix == ox && iy == oy) {
! 3403: /* but as only define single outrange point, can't intersect graph area */
! 3404: return (FALSE);
! 3405: }
! 3406: if (ix == ox) {
! 3407: /* line parallel to y axis */
! 3408:
! 3409: /* x coord must be in range, and line must span both y_min and y_max */
! 3410: /* note that spanning y_min implies spanning y_max, as both points OUTRANGE */
! 3411: if (!inrange(ix, x_min, x_max)) {
! 3412: return (FALSE);
! 3413: }
! 3414: if (inrange(y_min, iy, oy)) {
! 3415: lx[0] = ix;
! 3416: ly[0] = y_min;
! 3417:
! 3418: lx[1] = ix;
! 3419: ly[1] = y_max;
! 3420: #if 0
! 3421: fprintf(stderr, "(%g %g) -> (%g %g)",
! 3422: lx[0], ly[0], lx[1], ly[1]);
! 3423: #endif
! 3424: return (TRUE);
! 3425: } else
! 3426: return (FALSE);
! 3427: }
! 3428: if (iy == oy) {
! 3429: /* already checked case (ix == ox && iy == oy) */
! 3430:
! 3431: /* line parallel to x axis */
! 3432: /* y coord must be in range, and line must span both x_min and x_max */
! 3433: /* note that spanning x_min implies spanning x_max, as both points OUTRANGE */
! 3434: if (!inrange(iy, y_min, y_max)) {
! 3435: return (FALSE);
! 3436: }
! 3437: if (inrange(x_min, ix, ox)) {
! 3438: lx[0] = x_min;
! 3439: ly[0] = iy;
! 3440:
! 3441: lx[1] = x_max;
! 3442: ly[1] = iy;
! 3443: #if 0
! 3444: fprintf(stderr, "(%g %g) -> (%g %g)",
! 3445: lx[0], ly[0], lx[1], ly[1]);
! 3446: #endif
! 3447: return (TRUE);
! 3448: } else
! 3449: return (FALSE);
! 3450: }
! 3451: /* nasty 2D slanted line in an xy plane */
! 3452:
! 3453: /*
! 3454: Solve parametric equation
! 3455:
! 3456: (ix, iy) + t (diff_x, diff_y)
! 3457:
! 3458: where 0.0 <= t <= 1.0 and
! 3459:
! 3460: diff_x = (ox - ix);
! 3461: diff_y = (oy - iy);
! 3462: */
! 3463:
! 3464: t[0] = (x_min - ix) / (ox - ix);
! 3465: t[1] = (x_max - ix) / (ox - ix);
! 3466:
! 3467: if (t[0] > t[1]) {
! 3468: swap = t[0];
! 3469: t[0] = t[1];
! 3470: t[1] = swap;
! 3471: }
! 3472: t[2] = (y_min - iy) / (oy - iy);
! 3473: t[3] = (y_max - iy) / (oy - iy);
! 3474:
! 3475: if (t[2] > t[3]) {
! 3476: swap = t[2];
! 3477: t[2] = t[3];
! 3478: t[3] = swap;
! 3479: }
! 3480: t_min = GPMAX(GPMAX(t[0], t[2]), 0.0);
! 3481: t_max = GPMIN(GPMIN(t[1], t[3]), 1.0);
! 3482:
! 3483: if (t_min > t_max)
! 3484: return (FALSE);
! 3485:
! 3486: lx[0] = ix + t_min * (ox - ix);
! 3487: ly[0] = iy + t_min * (oy - iy);
! 3488:
! 3489: lx[1] = ix + t_max * (ox - ix);
! 3490: ly[1] = iy + t_max * (oy - iy);
! 3491:
! 3492: /*
! 3493: * Can only have 0 or 2 intersection points -- only need test one coord
! 3494: */
! 3495: if (inrange(lx[0], x_min, x_max) &&
! 3496: inrange(ly[0], y_min, y_max)) {
! 3497: #if 0
! 3498: fprintf(stderr, "(%g %g) -> (%g %g)",
! 3499: lx[0], ly[0], lx[1], ly[1]);
! 3500: #endif
! 3501: return (TRUE);
! 3502: }
! 3503: return (FALSE);
! 3504: }
! 3505:
! 3506: /* justify ticplace to a proper date-time value */
! 3507: double time_tic_just(level, ticplace)
! 3508: int level;
! 3509: double ticplace;
! 3510: {
! 3511: struct tm tm;
! 3512:
! 3513: if (level <= 0) {
! 3514: return (ticplace);
! 3515: }
! 3516: ggmtime(&tm, (double) ticplace);
! 3517: if (level > 0) { /* units of minutes */
! 3518: if (tm.tm_sec > 50)
! 3519: tm.tm_min++;
! 3520: tm.tm_sec = 0;
! 3521: }
! 3522: if (level > 1) { /* units of hours */
! 3523: if (tm.tm_min > 50)
! 3524: tm.tm_hour++;
! 3525: tm.tm_min = 0;
! 3526: }
! 3527: if (level > 2) { /* units of days */
! 3528: if (tm.tm_hour > 14) {
! 3529: tm.tm_hour = 0;
! 3530: tm.tm_mday = 0;
! 3531: tm.tm_yday++;
! 3532: ggmtime(&tm, (double) gtimegm(&tm));
! 3533: } else if (tm.tm_hour > 7) {
! 3534: tm.tm_hour = 12;
! 3535: } else if (tm.tm_hour > 3) {
! 3536: tm.tm_hour = 6;
! 3537: } else {
! 3538: tm.tm_hour = 0;
! 3539: }
! 3540: }
! 3541: /* skip it, I have not bothered with weekday so far */
! 3542: if (level > 4) { /* units of month */
! 3543: if (tm.tm_mday > 25) {
! 3544: tm.tm_mon++;
! 3545: if (tm.tm_mon > 11) {
! 3546: tm.tm_year++;
! 3547: tm.tm_mon = 0;
! 3548: }
! 3549: }
! 3550: tm.tm_mday = 1;
! 3551: }
! 3552: if (level > 5) {
! 3553: if (tm.tm_mon >= 7)
! 3554: tm.tm_year++;
! 3555: tm.tm_mon = 0;
! 3556: }
! 3557: ticplace = (double) gtimegm(&tm);
! 3558: ggmtime(&tm, (double) gtimegm(&tm));
! 3559: return (ticplace);
! 3560: }
! 3561:
! 3562: /* make smalltics for time-axis */
! 3563: double make_ltic(tlevel, incr)
! 3564: int tlevel;
! 3565: double incr;
! 3566: {
! 3567: double tinc;
! 3568: tinc = 0;
! 3569: if (tlevel < 0)
! 3570: tlevel = 0;
! 3571: switch (tlevel) {
! 3572: case 0:
! 3573: case 1:{
! 3574: if (incr >= 20)
! 3575: tinc = 10;
! 3576: if (incr >= 60)
! 3577: tinc = 30;
! 3578: if (incr >= 2 * 60)
! 3579: tinc = 60;
! 3580: if (incr >= 5 * 60)
! 3581: tinc = 2 * 60;
! 3582: if (incr >= 10 * 60)
! 3583: tinc = 5 * 60;
! 3584: if (incr >= 20 * 60)
! 3585: tinc = 10 * 60;
! 3586: break;
! 3587: }
! 3588: case 2:{
! 3589: if (incr >= 20 * 60)
! 3590: tinc = 10 * 60;
! 3591: if (incr >= 3600)
! 3592: tinc = 30 * 60;
! 3593: if (incr >= 2 * 3600)
! 3594: tinc = 3600;
! 3595: if (incr >= 5 * 3600)
! 3596: tinc = 2 * 3600;
! 3597: if (incr >= 10 * 3600)
! 3598: tinc = 5 * 3600;
! 3599: if (incr >= 20 * 3600)
! 3600: tinc = 10 * 3600;
! 3601: break;
! 3602: }
! 3603: case 3:{
! 3604: if (incr > 2 * 3600)
! 3605: tinc = 3600;
! 3606: if (incr > 4 * 3600)
! 3607: tinc = 2 * 3600;
! 3608: if (incr > 7 * 3600)
! 3609: tinc = 3 * 3600;
! 3610: if (incr > 13 * 3600)
! 3611: tinc = 6 * 3600;
! 3612: if (incr > DAY_SEC)
! 3613: tinc = 12 * 3600;
! 3614: if (incr > 2 * DAY_SEC)
! 3615: tinc = DAY_SEC;
! 3616: break;
! 3617: }
! 3618: case 4:{ /* week: tic per day */
! 3619: if (incr > 2 * DAY_SEC)
! 3620: tinc = DAY_SEC;
! 3621: if (incr > 7 * DAY_SEC)
! 3622: tinc = 7 * DAY_SEC;
! 3623: break;
! 3624: }
! 3625: case 5:{ /* month */
! 3626: if (incr > 2 * DAY_SEC)
! 3627: tinc = DAY_SEC;
! 3628: if (incr > 15 * DAY_SEC)
! 3629: tinc = 10 * DAY_SEC;
! 3630: if (incr > 2 * MON_SEC)
! 3631: tinc = MON_SEC;
! 3632: if (incr > 6 * MON_SEC)
! 3633: tinc = 3 * MON_SEC;
! 3634: if (incr > 2 * YEAR_SEC)
! 3635: tinc = YEAR_SEC;
! 3636: break;
! 3637: }
! 3638: case 6:{ /* year */
! 3639: if (incr > 2 * MON_SEC)
! 3640: tinc = MON_SEC;
! 3641: if (incr > 6 * MON_SEC)
! 3642: tinc = 3 * MON_SEC;
! 3643: if (incr > 2 * YEAR_SEC)
! 3644: tinc = YEAR_SEC;
! 3645: if (incr > 10 * YEAR_SEC)
! 3646: tinc = 5 * YEAR_SEC;
! 3647: if (incr > 50 * YEAR_SEC)
! 3648: tinc = 10 * YEAR_SEC;
! 3649: if (incr > 100 * YEAR_SEC)
! 3650: tinc = 20 * YEAR_SEC;
! 3651: if (incr > 200 * YEAR_SEC)
! 3652: tinc = 50 * YEAR_SEC;
! 3653: if (incr > 300 * YEAR_SEC)
! 3654: tinc = 100 * YEAR_SEC;
! 3655: break;
! 3656: }
! 3657: }
! 3658: return (tinc);
! 3659: }
! 3660:
! 3661:
! 3662: void write_multiline(x, y, text, hor, vert, angle, font)
! 3663: unsigned int x, y;
! 3664: char *text;
! 3665: enum JUSTIFY hor; /* horizontal ... */
! 3666: int vert; /* ... and vertical just - text in hor direction despite angle */
! 3667: int angle; /* assume term has already been set for this */
! 3668: char *font; /* NULL or "" means use default */
! 3669: {
! 3670: /* assumes we are free to mangle the text */
! 3671: register struct termentry *t = term;
! 3672: char *p;
! 3673: if (vert != JUST_TOP) {
! 3674: /* count lines and adjust y */
! 3675: int lines = 0; /* number of linefeeds - one fewer than lines */
! 3676: for (p = text; *p; ++p)
! 3677: if (*p == '\n')
! 3678: ++lines;
! 3679: if (angle)
! 3680: x -= (vert * lines * t->v_char) / 2;
! 3681: else
! 3682: y += (vert * lines * t->v_char) / 2;
! 3683: }
! 3684: if (font && *font)
! 3685: (*t->set_font) (font);
! 3686:
! 3687:
! 3688: for (;;) { /* we will explicitly break out */
! 3689:
! 3690: if ((p = strchr(text, '\n')) != NULL)
! 3691: *p = 0; /* terminate the string */
! 3692:
! 3693: if ((*t->justify_text) (hor)) {
! 3694: (*t->put_text) (x, y, text);
! 3695: } else {
! 3696: int fix = hor * (t->h_char) * strlen(text) / 2;
! 3697: if (angle)
! 3698: (*t->put_text) (x, y - fix, text);
! 3699: else
! 3700: (*t->put_text) (x - fix, y, text);
! 3701: }
! 3702: if (angle)
! 3703: x += t->v_char;
! 3704: else
! 3705: y -= t->v_char;
! 3706:
! 3707: if (!p)
! 3708: break;
! 3709:
! 3710: text = p + 1;
! 3711: } /* unconditional branch back to the for(;;) - just a goto ! */
! 3712:
! 3713: if (font && *font)
! 3714: (*t->set_font) (default_font);
! 3715: }
! 3716:
! 3717: /* display a x-axis ticmark - called by gen_ticks */
! 3718: /* also uses global tic_start, tic_direction, tic_text and tic_just */
! 3719: static void xtick2d_callback(axis, place, text, grid)
! 3720: int axis;
! 3721: double place;
! 3722: char *text;
! 3723: struct lp_style_type grid; /* linetype or -2 for no grid */
! 3724: {
! 3725: register struct termentry *t = term;
! 3726: /* minitick if text is NULL - beware - h_tic is unsigned */
! 3727: int ticsize = tic_direction * (int) (t->v_tic) * (text ? ticscale : miniticscale);
! 3728: unsigned int x = map_x(place);
! 3729:
! 3730: if (grid.l_type > -2) {
! 3731: term_apply_lp_properties(&grid);
! 3732: if (polar_grid_angle) {
! 3733: double x = place, y = 0, s = sin(0.1), c = cos(0.1);
! 3734: int i;
! 3735: int ogx = map_x(x);
! 3736: int ogy = map_y(0);
! 3737: int tmpgx, tmpgy, gx, gy;
! 3738:
! 3739: if (place > largest_polar_circle)
! 3740: largest_polar_circle = place;
! 3741: else if (-place > largest_polar_circle)
! 3742: largest_polar_circle = -place;
! 3743: for (i = 1; i <= 63 /* 2pi/0.1 */ ; ++i) {
! 3744: {
! 3745: /* cos(t+dt) = cos(t)cos(dt)-sin(t)cos(dt) */
! 3746: double tx = x * c - y * s;
! 3747: /* sin(t+dt) = sin(t)cos(dt)+cos(t)sin(dt) */
! 3748: y = y * c + x * s;
! 3749: x = tx;
! 3750: }
! 3751: tmpgx = gx = map_x(x);
! 3752: tmpgy = gy = map_y(y);
! 3753: if (clip_line(&ogx, &ogy, &tmpgx, &tmpgy)) {
! 3754: (*t->move) ((unsigned int) ogx, (unsigned int) ogy);
! 3755: (*t->vector) ((unsigned int) tmpgx, (unsigned int) tmpgy);
! 3756: }
! 3757: ogx = gx;
! 3758: ogy = gy;
! 3759: }
! 3760: } else {
! 3761: if (lkey && key_yt > ybot && x < key_xr && x > key_xl) {
! 3762: if (key_yb > ybot) {
! 3763: (*t->move) (x, ybot);
! 3764: (*t->vector) (x, key_yb);
! 3765: }
! 3766: if (key_yt < ytop) {
! 3767: (*t->move) (x, key_yt);
! 3768: (*t->vector) (x, ytop);
! 3769: }
! 3770: } else {
! 3771: (*t->move) (x, ybot);
! 3772: (*t->vector) (x, ytop);
! 3773: }
! 3774: }
! 3775: term_apply_lp_properties(&border_lp); /* border linetype */
! 3776: }
! 3777: /* we precomputed tic posn and text posn in global vars */
! 3778:
! 3779: (*t->move) (x, tic_start);
! 3780: (*t->vector) (x, tic_start + ticsize);
! 3781:
! 3782: if (tic_mirror >= 0) {
! 3783: (*t->move) (x, tic_mirror);
! 3784: (*t->vector) (x, tic_mirror - ticsize);
! 3785: }
! 3786: if (text)
! 3787: write_multiline(x, tic_text, text, tic_hjust, tic_vjust, rotate_tics, NULL);
! 3788: }
! 3789:
! 3790: /* display a y-axis ticmark - called by gen_ticks */
! 3791: /* also uses global tic_start, tic_direction, tic_text and tic_just */
! 3792: static void ytick2d_callback(axis, place, text, grid)
! 3793: int axis;
! 3794: double place;
! 3795: char *text;
! 3796: struct lp_style_type grid; /* linetype or -2 */
! 3797: {
! 3798: register struct termentry *t = term;
! 3799: /* minitick if text is NULL - v_tic is unsigned */
! 3800: int ticsize = tic_direction * (int) (t->h_tic) * (text ? ticscale : miniticscale);
! 3801: unsigned int y = map_y(place);
! 3802: if (grid.l_type > -2) {
! 3803: term_apply_lp_properties(&grid);
! 3804: if (polar_grid_angle) {
! 3805: double x = 0, y = place, s = sin(0.1), c = cos(0.1);
! 3806: int i;
! 3807: if (place > largest_polar_circle)
! 3808: largest_polar_circle = place;
! 3809: else if (-place > largest_polar_circle)
! 3810: largest_polar_circle = -place;
! 3811: clip_move(map_x(x), map_y(y));
! 3812: for (i = 1; i <= 63 /* 2pi/0.1 */ ; ++i) {
! 3813: {
! 3814: /* cos(t+dt) = cos(t)cos(dt)-sin(t)cos(dt) */
! 3815: double tx = x * c - y * s;
! 3816: /* sin(t+dt) = sin(t)cos(dt)+cos(t)sin(dt) */
! 3817: y = y * c + x * s;
! 3818: x = tx;
! 3819: }
! 3820: clip_vector(map_x(x), map_y(y));
! 3821: }
! 3822: } else {
! 3823: if (lkey && y < key_yt && y > key_yb && key_xl < xright /* catch TOUT */ ) {
! 3824: if (key_xl > xleft) {
! 3825: (*t->move) (xleft, y);
! 3826: (*t->vector) (key_xl, y);
! 3827: }
! 3828: if (key_xr < xright) {
! 3829: (*t->move) (key_xr, y);
! 3830: (*t->vector) (xright, y);
! 3831: }
! 3832: } else {
! 3833: (*t->move) (xleft, y);
! 3834: (*t->vector) (xright, y);
! 3835: }
! 3836: }
! 3837: term_apply_lp_properties(&border_lp); /* border linetype */
! 3838: }
! 3839: /* we precomputed tic posn and text posn */
! 3840:
! 3841: (*t->move) (tic_start, y);
! 3842: (*t->vector) (tic_start + ticsize, y);
! 3843:
! 3844: if (tic_mirror >= 0) {
! 3845: (*t->move) (tic_mirror, y);
! 3846: (*t->vector) (tic_mirror - ticsize, y);
! 3847: }
! 3848: if (text)
! 3849: write_multiline(tic_text, y, text, tic_hjust, tic_vjust, rotate_tics, NULL);
! 3850: }
! 3851:
! 3852: int label_width(str, lines)
! 3853: char *str;
! 3854: int *lines;
! 3855: {
! 3856: char lab[MAX_LINE_LEN + 1], *s, *e;
! 3857: int mlen, len, l;
! 3858:
! 3859: l = mlen = len = 0;
! 3860: sprintf(lab, "%s\n", str);
! 3861: s = lab;
! 3862: while ((e = (char *) strchr(s, '\n')) != NULL) { /* HBB 980308: quiet BC-3.1 warning */
! 3863: *e = '\0';
! 3864: len = strlen(s); /* = e-s ? */
! 3865: if (len > mlen)
! 3866: mlen = len;
! 3867: if (len || l)
! 3868: l++;
! 3869: s = ++e;
! 3870: }
! 3871: /* lines = NULL => not interested - div */
! 3872: if (lines)
! 3873: *lines = l;
! 3874: return (mlen);
! 3875: }
! 3876:
! 3877:
! 3878: void setup_tics(axis, ticdef, format, max)
! 3879: int axis;
! 3880: struct ticdef *ticdef;
! 3881: char *format;
! 3882: int max; /* approx max number of slots available */
! 3883: {
! 3884: double tic = 0; /* HBB: shut up gcc -Wall */
! 3885:
! 3886: int fixmin = (auto_array[axis] & 1) != 0;
! 3887: int fixmax = (auto_array[axis] & 2) != 0;
! 3888:
! 3889: if (ticdef->type == TIC_SERIES) {
! 3890: ticstep[axis] = tic = ticdef->def.series.incr;
! 3891: fixmin &= (ticdef->def.series.start == -VERYLARGE);
! 3892: fixmax &= (ticdef->def.series.end == VERYLARGE);
! 3893: } else if (ticdef->type == TIC_COMPUTED) {
! 3894: ticstep[axis] = tic = make_tics(axis, max);
! 3895: } else {
! 3896: fixmin = fixmax = 0; /* user-defined, day or month */
! 3897: }
! 3898:
! 3899: if (fixmin) {
! 3900: if (min_array[axis] < max_array[axis])
! 3901: min_array[axis] = tic * floor(min_array[axis] / tic);
! 3902: else
! 3903: min_array[axis] = tic * ceil(min_array[axis] / tic);
! 3904: }
! 3905: if (fixmax) {
! 3906: if (min_array[axis] < max_array[axis])
! 3907: max_array[axis] = tic * ceil(max_array[axis] / tic);
! 3908: else
! 3909: max_array[axis] = tic * floor(max_array[axis] / tic);
! 3910: }
! 3911: if (datatype[axis] == TIME && format_is_numeric[axis])
! 3912: /* invent one for them */
! 3913: timetic_format(axis, min_array[axis], max_array[axis]);
! 3914: else
! 3915: strcpy(ticfmt[axis], format);
! 3916: }
! 3917:
! 3918: /*{{{ mant_exp - split into mantissa and/or exponent */
! 3919: static void mant_exp(log_base, x, scientific, m, p)
! 3920: double log_base, x;
! 3921: int scientific; /* round to power of 3 */
! 3922: double *m;
! 3923: int *p; /* results */
! 3924: {
! 3925: int sign = 1;
! 3926: double l10;
! 3927: int power;
! 3928: /*{{{ check 0 */
! 3929: if (x == 0) {
! 3930: if (m)
! 3931: *m = 0;
! 3932: if (p)
! 3933: *p = 0;
! 3934: return;
! 3935: }
! 3936: /*}}} */
! 3937: /*{{{ check -ve */
! 3938: if (x < 0) {
! 3939: sign = (-1);
! 3940: x = (-x);
! 3941: }
! 3942: /*}}} */
! 3943:
! 3944: l10 = log10(x) / log_base;
! 3945: power = floor(l10);
! 3946: if (scientific) {
! 3947: power = 3 * floor(power / 3.0);
! 3948: }
! 3949: if (m)
! 3950: *m = sign * pow(10.0, (l10 - power) * log_base);
! 3951: if (p)
! 3952: *p = power;
! 3953: }
! 3954: /*}}} */
! 3955:
! 3956: /*
! 3957: * Kludge alert!!
! 3958: * Workaround until we have a better solution ...
! 3959: * Note: this assumes that all calls to sprintf in gprintf have
! 3960: * exactly three args. Lars
! 3961: */
! 3962: #ifdef HAVE_SNPRINTF
! 3963: # define sprintf(str,fmt,arg) \
! 3964: if (snprintf((str),count,(fmt),(arg)) > count) \
! 3965: fprintf (stderr,"%s:%d: Warning: too many digits for format\n",__FILE__,__LINE__)
! 3966: #endif
! 3967:
! 3968: /*{{{ gprintf */
! 3969: /* extended snprintf */
! 3970: static void gprintf(dest, count, format, log_base, x)
! 3971: char *dest, *format;
! 3972: size_t count;
! 3973: double log_base, x; /* we print one number in a number of different formats */
! 3974: {
! 3975: #define LOC_PI 3.14159265358979323846 /* local definition of PI */
! 3976: char temp[MAX_LINE_LEN];
! 3977: char *t;
! 3978:
! 3979: for (;;) {
! 3980: /*{{{ copy to dest until % */
! 3981: while (*format != '%')
! 3982: if (!(*dest++ = *format++))
! 3983: return; /* end of format */
! 3984: /*}}} */
! 3985:
! 3986: /*{{{ check for %% */
! 3987: if (format[1] == '%') {
! 3988: *dest++ = '%';
! 3989: format += 2;
! 3990: continue;
! 3991: }
! 3992: /*}}} */
! 3993:
! 3994: /*{{{ copy format part to temp, excluding conversion character */
! 3995: t = temp;
! 3996: *t++ = '%';
! 3997: /* dont put isdigit first since sideeffect in macro is bad */
! 3998: while (*++format == '.' || isdigit((int) *format) ||
! 3999: *format == '-' || *format == '+' || *format == ' ')
! 4000: *t++ = *format;
! 4001: /*}}} */
! 4002:
! 4003: /*{{{ convert conversion character */
! 4004: switch (*format) {
! 4005: /*{{{ x and o */
! 4006: case 'x':
! 4007: case 'X':
! 4008: case 'o':
! 4009: case 'O':
! 4010: t[0] = *format++;
! 4011: t[1] = 0;
! 4012: sprintf(dest, temp, (int) x);
! 4013: dest += strlen(dest);
! 4014: break;
! 4015: /*}}} */
! 4016: /*{{{ e, f and g */
! 4017: case 'e':
! 4018: case 'E':
! 4019: case 'f':
! 4020: case 'F':
! 4021: case 'g':
! 4022: case 'G':
! 4023: t[0] = *format++;
! 4024: t[1] = 0;
! 4025: sprintf(dest, temp, x);
! 4026: dest += strlen(dest);
! 4027: break;
! 4028: /*}}} */
! 4029: /*{{{ l */
! 4030: case 'l':
! 4031: {
! 4032: double mantissa;
! 4033: mant_exp(log_base, x, 0, &mantissa, NULL);
! 4034: t[0] = 'f';
! 4035: t[1] = 0;
! 4036: sprintf(dest, temp, mantissa);
! 4037: dest += strlen(dest);
! 4038: ++format;
! 4039: break;
! 4040: }
! 4041: /*}}} */
! 4042: /*{{{ t */
! 4043: case 't':
! 4044: {
! 4045: double mantissa;
! 4046: mant_exp(1.0, x, 0, &mantissa, NULL);
! 4047: t[0] = 'f';
! 4048: t[1] = 0;
! 4049: sprintf(dest, temp, mantissa);
! 4050: dest += strlen(dest);
! 4051: ++format;
! 4052: break;
! 4053: }
! 4054: /*}}} */
! 4055: /*{{{ s */
! 4056: case 's':
! 4057: {
! 4058: double mantissa;
! 4059: mant_exp(1.0, x, 1, &mantissa, NULL);
! 4060: t[0] = 'f';
! 4061: t[1] = 0;
! 4062: sprintf(dest, temp, mantissa);
! 4063: dest += strlen(dest);
! 4064: ++format;
! 4065: break;
! 4066: }
! 4067: /*}}} */
! 4068: /*{{{ L */
! 4069: case 'L':
! 4070: {
! 4071: int power;
! 4072: mant_exp(log_base, x, 0, NULL, &power);
! 4073: t[0] = 'd';
! 4074: t[1] = 0;
! 4075: sprintf(dest, temp, power);
! 4076: dest += strlen(dest);
! 4077: ++format;
! 4078: break;
! 4079: }
! 4080: /*}}} */
! 4081: /*{{{ T */
! 4082: case 'T':
! 4083: {
! 4084: int power;
! 4085: mant_exp(1.0, x, 0, NULL, &power);
! 4086: t[0] = 'd';
! 4087: t[1] = 0;
! 4088: sprintf(dest, temp, power);
! 4089: dest += strlen(dest);
! 4090: ++format;
! 4091: break;
! 4092: }
! 4093: /*}}} */
! 4094: /*{{{ S */
! 4095: case 'S':
! 4096: {
! 4097: int power;
! 4098: mant_exp(1.0, x, 1, NULL, &power);
! 4099: t[0] = 'd';
! 4100: t[1] = 0;
! 4101: sprintf(dest, temp, power);
! 4102: dest += strlen(dest);
! 4103: ++format;
! 4104: break;
! 4105: }
! 4106: /*}}} */
! 4107: /*{{{ c */
! 4108: case 'c':
! 4109: {
! 4110: int power;
! 4111: mant_exp(1.0, x, 1, NULL, &power);
! 4112: t[0] = 'c';
! 4113: t[1] = 0;
! 4114: power = power / 3 + 6; /* -18 -> 0, 0 -> 6, +18 -> 12, ... */
! 4115: if (power >= 0 && power <= 12) {
! 4116: sprintf(dest, temp, "afpnum kMGTPE"[power]);
! 4117: } else {
! 4118: /* please extend the range ! */
! 4119: /* name power name power
! 4120: -------------------------
! 4121: atto -18 Exa 18
! 4122: femto -15 Peta 15
! 4123: pico -12 Tera 12
! 4124: nano -9 Giga 9
! 4125: micro -6 Mega 6
! 4126: milli -3 kilo 3 */
! 4127:
! 4128: /* for the moment, print e+21 for example */
! 4129: sprintf(dest, "e%+02d", (power - 6) * 3);
! 4130: }
! 4131:
! 4132: dest += strlen(dest);
! 4133: ++format;
! 4134: break;
! 4135: }
! 4136: case 'P':
! 4137: {
! 4138: t[0] = 'f';
! 4139: t[1] = 0;
! 4140: sprintf(dest, temp, x / LOC_PI);
! 4141: dest += strlen(dest);
! 4142: ++format;
! 4143: break;
! 4144: }
! 4145: /*}}} */
! 4146: default:
! 4147: int_error("Bad format character", NO_CARET);
! 4148: }
! 4149: /*}}} */
! 4150: }
! 4151: }
! 4152: #undef LOC_PI /* local definition of PI */
! 4153: /*}}} */
! 4154: #ifdef HAVE_SNPRINTF
! 4155: # undef sprintf
! 4156: #endif
! 4157:
! 4158:
! 4159:
! 4160: /*{{{ gen_tics */
! 4161: /* uses global arrays ticstep[], ticfmt[], min_array[], max_array[],
! 4162: * auto_array[], log_array[], log_base_array[]
! 4163: * we use any of GRID_X/Y/X2/Y2 and _MX/_MX2/etc - caller is expected
! 4164: * to clear the irrelevent fields from global grid bitmask
! 4165: * note this is also called from graph3d, so we need GRID_Z too
! 4166: */
! 4167:
! 4168: void gen_tics(axis, def, grid, minitics, minifreq, callback)
! 4169: int axis; /* FIRST_X_AXIS, etc */
! 4170: struct ticdef *def; /* tic defn */
! 4171: int grid; /* GRID_X | GRID_MX etc */
! 4172: int minitics; /* minitics - off/default/auto/explicit */
! 4173: double minifreq; /* frequency */
! 4174: tic_callback callback; /* fn to call to actually do the work */
! 4175: {
! 4176: /* seperate main-tic part of grid */
! 4177: struct lp_style_type lgrd, mgrd;
! 4178:
! 4179: memcpy(&lgrd, &grid_lp, sizeof(struct lp_style_type));
! 4180: memcpy(&mgrd, &mgrid_lp, sizeof(struct lp_style_type));
! 4181: lgrd.l_type = (grid & (GRID_X | GRID_Y | GRID_X2 | GRID_Y2 | GRID_Z)) ? grid_lp.l_type : -2;
! 4182: mgrd.l_type = (grid & (GRID_MX | GRID_MY | GRID_MX2 | GRID_MY2 | GRID_MZ)) ? mgrid_lp.l_type : -2;
! 4183:
! 4184: if (def->type == TIC_USER) { /* special case */
! 4185: /*{{{ do user tics then return */
! 4186: struct ticmark *mark = def->def.user;
! 4187: double uncertain = (max_array[axis] - min_array[axis]) / 10;
! 4188: double ticmin = min_array[axis] - SIGNIF * uncertain;
! 4189: double internal_max = max_array[axis] + SIGNIF * uncertain;
! 4190: double log_base = log_array[axis] ? log10(base_array[axis]) : 1.0;
! 4191:
! 4192: /* polar labels always +ve, and if rmin has been set, they are
! 4193: * relative to rmin. position is as user specified, but must
! 4194: * be translated. I dont think it will work at all for
! 4195: * log scale, so I shan't worry about it !
! 4196: */
! 4197: double polar_shift = (polar && !(autoscale_r & 1)) ? rmin : 0;
! 4198:
! 4199: for (mark = def->def.user; mark; mark = mark->next) {
! 4200: char label[64];
! 4201: double internal = log_array[axis] ? log(mark->position) / log_base_array[axis] : mark->position;
! 4202:
! 4203: internal -= polar_shift;
! 4204:
! 4205: if (!inrange(internal, ticmin, internal_max))
! 4206: continue;
! 4207:
! 4208: if (datatype[axis] == TIME)
! 4209: gstrftime(label, 24, mark->label ? mark->label : ticfmt[axis], mark->position);
! 4210: else
! 4211: gprintf(label, sizeof(label), mark->label ? mark->label : ticfmt[axis], log_base, mark->position);
! 4212:
! 4213: (*callback) (axis, internal, label, lgrd);
! 4214: }
! 4215:
! 4216: return; /* NO MINITICS FOR USER-DEF TICS */
! 4217: /*}}} */
! 4218: }
! 4219: /* series-tics
! 4220: * need to distinguish user co-ords from internal co-ords.
! 4221: * - for logscale, internal = log(user), else internal = user
! 4222: *
! 4223: * The minitics are a bit of a drag - we need to distinuish
! 4224: * the cases step>1 from step == 1.
! 4225: * If step = 1, we are looking at 1,10,100,1000 for example, so
! 4226: * minitics are 2,5,8, ... - done in user co-ordinates
! 4227: * If step>1, we are looking at 1,1e6,1e12 for example, so
! 4228: * minitics are 10,100,1000,... - done in internal co-ords
! 4229: */
! 4230:
! 4231: {
! 4232: double tic; /* loop counter */
! 4233: double internal; /* in internal co-ords */
! 4234: double user; /* in user co-ords */
! 4235: double start, step, end;
! 4236: double lmin = min_array[axis], lmax = max_array[axis];
! 4237: double internal_min, internal_max; /* to allow for rounding errors */
! 4238: double ministart = 0, ministep = 1, miniend = 1; /* internal or user - depends on step */
! 4239: int anyticput = 0; /* for detection of infinite loop */
! 4240:
! 4241: /* gprintf uses log10() of base - log_base_array is log() */
! 4242: double log_base = log_array[axis] ? log10(base_array[axis]) : 1.0;
! 4243:
! 4244: if (lmax < lmin) {
! 4245: /* hmm - they have set reversed range for some reason */
! 4246: double temp = lmin;
! 4247: lmin = lmax;
! 4248: lmax = temp;
! 4249: }
! 4250: /*{{{ choose start, step and end */
! 4251: switch (def->type) {
! 4252: case TIC_SERIES:
! 4253: if (log_array[axis]) {
! 4254: /* we can tolerate start <= 0 if step and end > 0 */
! 4255: if (def->def.series.end <= 0 ||
! 4256: def->def.series.incr <= 0)
! 4257: return; /* just quietly ignore */
! 4258: step = log(def->def.series.incr) / log_base_array[axis];
! 4259: end = log(def->def.series.end) / log_base_array[axis];
! 4260: start = def->def.series.start > 0 ?
! 4261: log(def->def.series.start) / log_base_array[axis] :
! 4262: step;
! 4263: } else {
! 4264: start = def->def.series.start;
! 4265: step = def->def.series.incr;
! 4266: end = def->def.series.end;
! 4267: if (start == -VERYLARGE)
! 4268: start = step * floor(lmin / step);
! 4269: if (end == VERYLARGE)
! 4270: end = step * ceil(lmax / step);
! 4271: }
! 4272: break;
! 4273: case TIC_COMPUTED:
! 4274: /* round to multiple of step */
! 4275: start = ticstep[axis] * floor(lmin / ticstep[axis]);
! 4276: step = ticstep[axis];
! 4277: end = ticstep[axis] * ceil(lmax / ticstep[axis]);
! 4278: break;
! 4279: case TIC_MONTH:
! 4280: start = floor(lmin);
! 4281: end = ceil(lmax);
! 4282: step = floor((end - start) / 12);
! 4283: if (step < 1)
! 4284: step = 1;
! 4285: break;
! 4286: case TIC_DAY:
! 4287: start = floor(lmin);
! 4288: end = ceil(lmax);
! 4289: step = floor((end - start) / 14);
! 4290: if (step < 1)
! 4291: step = 1;
! 4292: break;
! 4293: default:
! 4294: graph_error("Internal error : unknown tic type");
! 4295: return; /* avoid gcc -Wall warning about start */
! 4296: }
! 4297: /*}}} */
! 4298:
! 4299: /*{{{ ensure ascending order */
! 4300: if (end < start) {
! 4301: double temp;
! 4302: temp = end;
! 4303: end = start;
! 4304: start = temp;
! 4305: }
! 4306: step = fabs(step);
! 4307: /*}}} */
! 4308:
! 4309: if (minitics) {
! 4310: /*{{{ figure out ministart, ministep, miniend */
! 4311: if (minitics == MINI_USER) {
! 4312: /* they have said what they want */
! 4313: if (minifreq <= 0)
! 4314: minitics = 0; /* not much else we can do */
! 4315: else if (log_array[axis]) {
! 4316: ministart = ministep = step / minifreq * base_array[axis];
! 4317: miniend = step * base_array[axis];
! 4318: } else {
! 4319: ministart = ministep = step / minifreq;
! 4320: miniend = step;
! 4321: }
! 4322: } else if (log_array[axis]) {
! 4323: if (step > 1.5) { /* beware rounding errors */
! 4324: /*{{{ 10,100,1000 case */
! 4325: /* no more than five minitics */
! 4326: ministart = ministep = (int) (0.2 * step);
! 4327: if (ministep < 1)
! 4328: ministart = ministep = 1;
! 4329: miniend = step;
! 4330: /*}}} */
! 4331: } else {
! 4332: /*{{{ 2,5,8 case */
! 4333: miniend = base_array[axis];
! 4334: if (end - start >= 10)
! 4335: minitics = 0; /* none */
! 4336: else if (end - start >= 5) {
! 4337: ministart = 2;
! 4338: ministep = 3;
! 4339: } else {
! 4340: ministart = 2;
! 4341: ministep = 1;
! 4342: }
! 4343: /*}}} */
! 4344: }
! 4345: } else if (datatype[axis] == TIME) {
! 4346: ministart = ministep = make_ltic(timelevel[axis], step);
! 4347: miniend = step * 0.9;
! 4348: } else if (minitics == MINI_AUTO) {
! 4349: ministart = ministep = 0.1 * step;
! 4350: miniend = step;
! 4351: } else
! 4352: minitics = 0;
! 4353:
! 4354: if (ministep <= 0)
! 4355: minitics = 0; /* dont get stuck in infinite loop */
! 4356: /*}}} */
! 4357: }
! 4358: /*{{{ a few tweaks and checks */
! 4359: /* watch rounding errors */
! 4360: end += SIGNIF * step;
! 4361: internal_max = lmax + step * SIGNIF;
! 4362: internal_min = lmin - step * SIGNIF;
! 4363:
! 4364: if (step == 0)
! 4365: return; /* just quietly ignore them ! */
! 4366: /*}}} */
! 4367:
! 4368: for (tic = start; tic <= end; tic += step) {
! 4369: if (anyticput == 2) /* See below... */
! 4370: break;
! 4371: if (anyticput && (fabs(tic - start) < DBL_EPSILON)) {
! 4372: /* step is too small.. */
! 4373: anyticput = 2; /* Don't try again. */
! 4374: tic = end; /* Put end tic. */
! 4375: } else
! 4376: anyticput = 1;
! 4377:
! 4378: /*{{{ calc internal and user co-ords */
! 4379: if (!log_array[axis]) {
! 4380: internal = datatype[axis] == TIME ? time_tic_just(timelevel[axis], tic) : tic;
! 4381: user = CheckZero(internal, step);
! 4382: } else {
! 4383: /* log scale => dont need to worry about zero ? */
! 4384: internal = tic;
! 4385: user = pow(base_array[axis], internal);
! 4386: }
! 4387: /*}}} */
! 4388: if (internal > internal_max)
! 4389: break; /* gone too far - end of series = VERYLARGE perhaps */
! 4390: if (internal >= internal_min) {
! 4391: /* continue; *//* maybe minitics!!!. user series starts below min ? */
! 4392:
! 4393: /*{{{ draw tick via callback */
! 4394: switch (def->type) {
! 4395: case TIC_DAY:{
! 4396: int d = (long) floor(user + 0.5) % 7;
! 4397: if (d < 0)
! 4398: d += 7;
! 4399: (*callback) (axis, internal, abbrev_day_names[d], lgrd);
! 4400: break;
! 4401: }
! 4402: case TIC_MONTH:{
! 4403: int m = (long) floor(user - 1) % 12;
! 4404: if (m < 0)
! 4405: m += 12;
! 4406: (*callback) (axis, internal, abbrev_month_names[m], lgrd);
! 4407: break;
! 4408: }
! 4409: default:{ /* comp or series */
! 4410: char label[64];
! 4411: if (datatype[axis] == TIME) {
! 4412: /* If they are doing polar time plot, good luck to them */
! 4413: gstrftime(label, 24, ticfmt[axis], (double) user);
! 4414: } else if (polar) {
! 4415: /* if rmin is set, we stored internally with r-rmin */
! 4416: #if 0 /* Igor's polar-grid patch */
! 4417: double r = fabs(user) + (autoscale_r & 1 ? 0 : rmin);
! 4418: #else
! 4419: /* Igor removed fabs to allow -ve labels */
! 4420: double r = user + (autoscale_r & 1 ? 0 : rmin);
! 4421: #endif
! 4422: gprintf(label, sizeof(label), ticfmt[axis], log_base, r);
! 4423: } else {
! 4424: gprintf(label, sizeof(label), ticfmt[axis], log_base, user);
! 4425: }
! 4426: (*callback) (axis, internal, label, lgrd);
! 4427: }
! 4428: }
! 4429: /*}}} */
! 4430:
! 4431: }
! 4432: if (minitics) {
! 4433: /*{{{ process minitics */
! 4434: double mplace, mtic;
! 4435: for (mplace = ministart; mplace < miniend; mplace += ministep) {
! 4436: if (datatype[axis] == TIME)
! 4437: mtic = time_tic_just(timelevel[axis] - 1, internal + mplace);
! 4438: else
! 4439: mtic = internal + (log_array[axis] && step <= 1.5 ? log(mplace) / log_base_array[axis] : mplace);
! 4440: if (inrange(mtic, internal_min, internal_max) &&
! 4441: inrange(mtic, start - step * SIGNIF, end + step * SIGNIF))
! 4442: (*callback) (axis, mtic, NULL, mgrd);
! 4443: }
! 4444: /*}}} */
! 4445: }
! 4446: }
! 4447: }
! 4448: }
! 4449: /*}}} */
! 4450:
! 4451: /*{{{ map_position */
! 4452: static void map_position(pos, x, y, what)
! 4453: struct position *pos;
! 4454: unsigned int *x, *y;
! 4455: char *what;
! 4456: {
! 4457: switch (pos->scalex) {
! 4458: case first_axes:
! 4459: {
! 4460: double xx = LogScale(pos->x, log_array[FIRST_X_AXIS], log_base_array[FIRST_X_AXIS], what, "x");
! 4461: *x = xleft + (xx - min_array[FIRST_X_AXIS]) * scale[FIRST_X_AXIS] + 0.5;
! 4462: break;
! 4463: }
! 4464: case second_axes:
! 4465: {
! 4466: double xx = LogScale(pos->x, log_array[SECOND_X_AXIS], log_base_array[SECOND_X_AXIS], what, "x");
! 4467: *x = xleft + (xx - min_array[SECOND_X_AXIS]) * scale[SECOND_X_AXIS] + 0.5;
! 4468: break;
! 4469: }
! 4470: case graph:
! 4471: {
! 4472: *x = xleft + pos->x * (xright - xleft) + 0.5;
! 4473: break;
! 4474: }
! 4475: case screen:
! 4476: {
! 4477: register struct termentry *t = term;
! 4478: *x = pos->x * (t->xmax) + 0.5;
! 4479: break;
! 4480: }
! 4481: }
! 4482: switch (pos->scaley) {
! 4483: case first_axes:
! 4484: {
! 4485: double yy = LogScale(pos->y, log_array[FIRST_Y_AXIS], log_base_array[FIRST_Y_AXIS], what, "y");
! 4486: *y = ybot + (yy - min_array[FIRST_Y_AXIS]) * scale[FIRST_Y_AXIS] + 0.5;
! 4487: return;
! 4488: }
! 4489: case second_axes:
! 4490: {
! 4491: double yy = LogScale(pos->y, log_array[SECOND_Y_AXIS], log_base_array[SECOND_Y_AXIS], what, "y");
! 4492: *y = ybot + (yy - min_array[SECOND_Y_AXIS]) * scale[SECOND_Y_AXIS] + 0.5;
! 4493: return;
! 4494: }
! 4495: case graph:
! 4496: {
! 4497: *y = ybot + pos->y * (ytop - ybot) + 0.5;
! 4498: return;
! 4499: }
! 4500: case screen:
! 4501: {
! 4502: register struct termentry *t = term;
! 4503: *y = pos->y * (t->ymax) + 0.5;
! 4504: return;
! 4505: }
! 4506: }
! 4507: }
! 4508: /*}}} */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>