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