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

Annotation of OpenXM_contrib/gnuplot/graphics.c, Revision 1.1

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

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