[BACK]Return to graphics.c CVS log [TXT][DIR] Up to [local] / OpenXM_contrib / gnuplot

Annotation of OpenXM_contrib/gnuplot/graphics.c, Revision 1.1.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>