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