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

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

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