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