[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.2

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>