2fbe2ca368136a2584ddb9efc70252f25a4879bc
[gnuplot] / src / graphics.c
1 #ifndef lint
2 static char *RCSid() { return RCSid("$Id: graphics.c,v 1.194.2.43 2009/07/05 06:16:17 sfeam Exp $"); }
3 #endif
4
5 /* GNUPLOT - graphics.c */
6
7 /*[
8  * Copyright 1986 - 1993, 1998, 2004   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 /* Daniel Sebald: added plot_image_or_update_axes() routine for images.
38  * (5 November 2003)
39  */
40
41 #include "graphics.h"
42
43 #include "color.h"
44 #include "pm3d.h"
45 #include "plot.h"
46
47 #include "alloc.h"
48 #include "axis.h"
49 #include "command.h"
50 #include "gp_time.h"
51 #include "gadgets.h"
52 /* FIXME HBB 20010822: this breaks the plan of disentangling graphics
53  * and plot2d, because each #include's the other's header: */
54 #include "plot2d.h"             /* for boxwidth */
55 #include "term_api.h"
56 #include "util.h"
57
58
59 /* Externally visible/modifiable status variables */
60
61 /* 'set offset' --- artificial buffer zone between coordinate axes and
62  * the area actually covered by the data */
63 double loff = 0.0;
64 double roff = 0.0;
65 double toff = 0.0;
66 double boff = 0.0;
67
68 /* set bars */
69 double bar_size = 1.0;
70
71 /* key placement is calculated in boundary, so we need file-wide variables
72  * To simplify adjustments to the key, we set all these once [depends on
73  * key->reverse] and use them throughout.
74  */
75
76 /*{{{  local and global variables */
77 static int key_sample_width;    /* width of line sample */
78 static int key_sample_left;     /* offset from x for left of line sample */
79 static int key_sample_right;    /* offset from x for right of line sample */
80 static int key_point_offset;    /* offset from x for point sample */
81 static int key_text_left;       /* offset from x for left-justified text */
82 static int key_text_right;      /* offset from x for right-justified text */
83 static int key_size_left;       /* size of left bit of key (text or sample, depends on key->reverse) */
84 static int key_size_right;      /* size of right part of key (including padding) */
85 static int max_ptitl_len = 0;   /* max length of plot-titles (keys) */
86 static double ktitl_lines = 0;  /* no lines in key->title (key header) */
87 static int ptitl_cnt;           /* count keys with len > 0  */
88 static int key_rows, key_col_wth, yl_ref;
89 static struct clipbox keybox;   /* boundaries for key field */
90
91 /* set by tic_callback - how large to draw polar radii */
92 static double largest_polar_circle;
93
94 static int xlablin, x2lablin, ylablin, y2lablin, titlelin, xticlin, x2ticlin;
95
96 static int key_entry_height;    /* bigger of t->v_size, pointsize*t->v_tick */
97 static int p_width, p_height;   /* pointsize * { t->h_tic | t->v_tic } */
98
99
100 /* there are several things on right of plot - key, y2tics and y2label
101  * when working out boundary, save posn of y2label for later...
102  * Same goes for x2label.
103  * key posn is also stored in keybox.xl, and tics go at plot_bounds.xright
104  */
105 static int ylabel_x, y2label_x, xlabel_y, x2label_y, title_y, time_y, time_x;
106 static int ylabel_y, y2label_y, xtic_y, x2tic_y, ytic_x, y2tic_x;
107 /*}}} */
108
109 #ifdef EAM_HISTOGRAMS
110 /* Status information for stacked histogram plots */
111 static struct coordinate GPHUGE *stackheight = NULL;    /* top of previous row */
112 static int stack_count;                                 /* points actually used */
113 static void place_histogram_titles __PROTO((void));
114 #endif
115
116 /*{{{  static fns and local macros */
117 static void plot_border __PROTO((void));
118 static void plot_impulses __PROTO((struct curve_points * plot, int yaxis_x, int xaxis_y));
119 static void plot_lines __PROTO((struct curve_points * plot));
120 static void plot_points __PROTO((struct curve_points * plot));
121 static void plot_dots __PROTO((struct curve_points * plot));
122 static void plot_bars __PROTO((struct curve_points * plot));
123 static void plot_boxes __PROTO((struct curve_points * plot, int xaxis_y));
124 static void plot_filledcurves __PROTO((struct curve_points * plot));
125 static void finish_filled_curve __PROTO((int, gpiPoint *, struct curve_points *));
126 static void plot_betweencurves __PROTO((struct curve_points * plot));
127 static void fill_missing_corners __PROTO((gpiPoint *corners, int *points, int exit, int reentry, int updown, int leftright));
128 static void fill_between __PROTO((double, double, double, double, double, double, struct curve_points *));
129 static TBOOLEAN bound_intersect __PROTO((struct coordinate GPHUGE * points, int i, double *ex, double *ey, filledcurves_opts *filledcurves_options));
130 static gpiPoint *fill_corners __PROTO((int, unsigned int, unsigned int, unsigned int, unsigned int));
131 static void plot_vectors __PROTO((struct curve_points * plot));
132 static void plot_f_bars __PROTO((struct curve_points * plot));
133 static void plot_c_bars __PROTO((struct curve_points * plot));
134
135 static void place_labels __PROTO((struct text_label * listhead, int layer, TBOOLEAN clip));
136 static void place_arrows __PROTO((int layer));
137 static void place_grid __PROTO((void));
138
139 static int edge_intersect __PROTO((struct coordinate GPHUGE * points, int i, double *ex, double *ey));
140 static TBOOLEAN two_edge_intersect __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));
141 static TBOOLEAN two_edge_intersect_steps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));
142
143 static void plot_steps __PROTO((struct curve_points * plot));   /* JG */
144 static void plot_fsteps __PROTO((struct curve_points * plot));  /* HOE */
145 static void plot_histeps __PROTO((struct curve_points * plot)); /* CAC */
146 static void histeps_horizontal __PROTO((int *xl, int *yl, double x1, double x2, double y));     /* CAC */
147 static void histeps_vertical __PROTO((int *xl, int *yl, double x, double y1, double y2));       /* CAC */
148 static void edge_intersect_steps __PROTO((struct coordinate GPHUGE * points, int i, double *ex, double *ey));   /* JG */
149 static void edge_intersect_fsteps __PROTO((struct coordinate GPHUGE * points, int i, double *ex, double *ey));  /* HOE */
150 static TBOOLEAN two_edge_intersect_steps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));   /* JG */
151 static TBOOLEAN two_edge_intersect_fsteps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));
152
153 static void boundary __PROTO((struct curve_points * plots, int count));
154
155 /* HBB 20010118: these should be static, but can't --- HP-UX assembler bug */
156 void ytick2d_callback __PROTO((AXIS_INDEX, double place, char *text, struct lp_style_type grid));
157 void xtick2d_callback __PROTO((AXIS_INDEX, double place, char *text, struct lp_style_type grid));
158 int histeps_compare __PROTO((SORTFUNC_ARGS p1, SORTFUNC_ARGS p2));
159
160 static void get_arrow __PROTO((struct arrow_def* arrow, int* sx, int* sy, int* ex, int* ey));
161 static void map_position_double __PROTO((struct position* pos, double* x, double* y, const char* what));
162
163 static int find_maxl_keys __PROTO((struct curve_points *plots, int count, int *kcnt));
164
165 static void do_key_sample __PROTO((struct curve_points *this_plot, legend_key *key,
166                                    char *title,  struct termentry *t, int xl, int yl));
167
168 static TBOOLEAN check_for_variable_color __PROTO((struct curve_points *plot, struct coordinate *point));
169
170 static int style_from_fill __PROTO((struct fill_style_type *));
171
172 /* for plotting error bars
173  * half the width of error bar tic mark
174  */
175 #define ERRORBARTIC GPMAX((t->h_tic/2),1)
176
177 /* For tracking exit and re-entry of bounding curves that extend out of plot */
178 /* these must match the bit values returned by clip_point(). */
179 #define LEFT_EDGE       1
180 #define RIGHT_EDGE      2
181 #define BOTTOM_EDGE     4
182 #define TOP_EDGE        8
183
184 #define clip_fill       ((plot->filledcurves_options.closeto == FILLEDCURVES_CLOSED) || clip_lines2)
185
186 /*
187  * The Amiga SAS/C 6.2 compiler moans about macro envocations causing
188  * multiple calls to functions. I converted these macros to inline
189  * functions coping with the problem without losing speed.
190  * If your compiler supports __inline, you should add it to the
191  * #ifdef directive
192  * (MGR, 1993)
193  */
194
195 #ifdef AMIGA_SC_6_1
196 GP_INLINE static TBOOLEAN
197 i_inrange(int z, int min, int max)
198 {
199     return ((min < max)
200             ? ((z >= min) && (z <= max))
201             : ((z >= max) && (z <= min)));
202 }
203
204 GP_INLINE static double
205 f_max(double a, double b)
206 {
207     return (GPMAX(a, b));
208 }
209
210 GP_INLINE static double
211 f_min(double a, double b)
212 {
213     return (GPMIN(a, b));
214 }
215
216 #else
217 #define f_max(a,b) GPMAX((a),(b))
218 #define f_min(a,b) GPMIN((a),(b))
219 #define i_inrange(z,a,b) inrange((z),(a),(b))
220 #endif
221
222 /* True if a and b have the same sign or zero (positive or negative) */
223 #define samesign(a,b) ((a) * (b) >= 0)
224 /*}}} */
225
226 /*{{{  more variables */
227
228 /* we make a local copy of the 'key' variable so that if something
229  * goes wrong, we can switch it off temporarily
230  */
231
232 static TBOOLEAN lkey;
233
234 /*}}} */
235
236 static int
237 find_maxl_keys(struct curve_points *plots, int count, int *kcnt)
238 {
239     int mlen, len, curve, cnt;
240     struct curve_points *this_plot;
241
242     mlen = cnt = 0;
243     this_plot = plots;
244     for (curve = 0; curve < count; this_plot = this_plot->next, curve++) {
245         if (this_plot->title && !this_plot->title_is_suppressed) {
246             ignore_enhanced(this_plot->title_no_enhanced);
247             len = estimate_strlen(this_plot->title);
248             if (len != 0) {
249                 cnt++;
250                 if (len > mlen)
251                     mlen = len;
252             }
253             ignore_enhanced(FALSE);
254         }
255 #ifdef EAM_HISTOGRAMS
256         /* Check for new histogram here and save space for divider */
257         if (this_plot->plot_style == HISTOGRAMS
258         &&  this_plot->histogram_sequence == 0 && cnt > 1)
259             cnt++;
260         /* Check for column-stacked histogram with key entries */
261         if (this_plot->plot_style == HISTOGRAMS &&  this_plot->labels) {
262             text_label *key_entry = this_plot->labels->next;
263             for (; key_entry; key_entry=key_entry->next) {
264                 cnt++;
265                 len = key_entry->text ? estimate_strlen(key_entry->text) : 0;
266                 if (len > mlen)
267                     mlen = len;
268             }
269         }
270 #endif
271     }
272
273     if (kcnt != NULL)
274         *kcnt = cnt;
275     return (mlen);
276 }
277
278
279 /*{{{  boundary() */
280 /* borders of plotting area
281  * computed once on every call to do_plot
282  *
283  * The order in which things is done is getting pretty critical:
284  *  plot_bounds.ytop depends on title, x2label, ylabels (if no rotated text)
285  *  plot_bounds.ybot depends on key, if "under"
286  *  once we have these, we can setup the y1 and y2 tics and the
287  *  only then can we calculate plot_bounds.xleft and plot_bounds.xright
288  *  plot_bounds.xright depends also on key RIGHT
289  *  then we can do x and x2 tics
290  *
291  * For set size ratio ..., everything depends on everything else...
292  * not really a lot we can do about that, so we lose if the plot has to
293  * be reduced vertically. But the chances are the
294  * change will not be very big, so the number of tics will not
295  * change dramatically.
296  *
297  * Margin computation redone by Dick Crawford (rccrawford@lanl.gov) 4/98
298  */
299
300 static void
301 boundary(struct curve_points *plots, int count)
302 {
303     int yticlin = 0, y2ticlin = 0, timelin = 0;
304     legend_key *key = &keyT;
305
306     struct termentry *t = term;
307     int key_h, key_w;
308     /* FIXME HBB 20000506: this line is the reason for the 'D0,1;D1,0'
309      * bug in the HPGL terminal: we actually carry out the switch of
310      * text orientation, just for finding out if the terminal can do
311      * that. *But* we're not in graphical mode, yet, so this call
312      * yields undesirable results */
313     int can_rotate = (*t->text_angle) (TEXT_VERTICAL);
314
315     int xtic_textheight;        /* height of xtic labels */
316     int x2tic_textheight;       /* height of x2tic labels */
317     int title_textheight;       /* height of title */
318     int xlabel_textheight;      /* height of xlabel */
319     int x2label_textheight;     /* height of x2label */
320     int timetop_textheight;     /* height of timestamp (if at top) */
321     int timebot_textheight;     /* height of timestamp (if at bottom) */
322     int ylabel_textheight;      /* height of (unrotated) ylabel */
323     int y2label_textheight;     /* height of (unrotated) y2label */
324     int ylabel_textwidth;       /* width of (rotated) ylabel */
325     int y2label_textwidth;      /* width of (rotated) y2label */
326     int timelabel_textwidth;    /* width of timestamp */
327     int ytic_textwidth;         /* width of ytic labels */
328     int y2tic_textwidth;        /* width of y2tic labels */
329     int x2tic_height;           /* 0 for tic_in or no x2tics, ticscale*v_tic otherwise */
330     int xtic_height;
331     int ytic_width;
332     int y2tic_width;
333
334     int key_cols = 1;           /* # columns of keys */
335
336     /* figure out which rotatable items are to be rotated
337      * (ylabel and y2label are rotated if possible) */
338     int vertical_timelabel = can_rotate ? timelabel_rotate : 0;
339     int vertical_xtics  = can_rotate ? axis_array[FIRST_X_AXIS].tic_rotate : 0;
340     int vertical_x2tics = can_rotate ? axis_array[SECOND_X_AXIS].tic_rotate : 0;
341     int vertical_ytics  = can_rotate ? axis_array[FIRST_Y_AXIS].tic_rotate : 0;
342     int vertical_y2tics = can_rotate ? axis_array[SECOND_Y_AXIS].tic_rotate : 0;
343
344     TBOOLEAN shift_labels_to_border = FALSE;
345
346     lkey = key->visible;        /* but we may have to disable it later */
347
348     xticlin = ylablin = y2lablin = xlablin = x2lablin = titlelin = 0;
349
350     /*{{{  count lines in labels and tics */
351     if (title.text)
352         label_width(title.text, &titlelin);
353     if (axis_array[FIRST_X_AXIS].label.text)
354         label_width(axis_array[FIRST_X_AXIS].label.text, &xlablin);
355
356     /* This should go *inside* label_width(), but it messes up the key title */
357     /* Imperfect check for subscripts or superscripts */
358     if ((term->flags & TERM_ENHANCED_TEXT) && axis_array[FIRST_X_AXIS].label.text
359         && strpbrk(axis_array[FIRST_X_AXIS].label.text, "_^"))
360             xlablin++;
361
362     if (axis_array[SECOND_X_AXIS].label.text)
363         label_width(axis_array[SECOND_X_AXIS].label.text, &x2lablin);
364     if (axis_array[FIRST_Y_AXIS].label.text)
365         label_width(axis_array[FIRST_Y_AXIS].label.text, &ylablin);
366     if (axis_array[SECOND_Y_AXIS].label.text)
367         label_width(axis_array[SECOND_Y_AXIS].label.text, &y2lablin);
368
369     if (axis_array[FIRST_X_AXIS].ticmode) {
370         label_width(axis_array[FIRST_X_AXIS].formatstring, &xticlin);
371         /* Reserve room for user tic labels even if format of autoticks is "" */
372         if (xticlin == 0 && axis_array[FIRST_X_AXIS].ticdef.def.user)
373             xticlin = 1;
374     }
375
376     if (axis_array[SECOND_X_AXIS].ticmode)
377         label_width(axis_array[SECOND_X_AXIS].formatstring, &x2ticlin);
378     if (axis_array[FIRST_Y_AXIS].ticmode)
379         label_width(axis_array[FIRST_Y_AXIS].formatstring, &yticlin);
380     if (axis_array[SECOND_Y_AXIS].ticmode)
381         label_width(axis_array[SECOND_Y_AXIS].formatstring, &y2ticlin);
382     if (timelabel.text)
383         label_width(timelabel.text, &timelin);
384     /*}}} */
385
386     /*{{{  preliminary plot_bounds.ytop  calculation */
387
388     /*     first compute heights of things to be written in the margin */
389
390     /* title */
391     if (titlelin) {
392         double tmpx, tmpy;
393         map_position_r(&(title.offset), &tmpx, &tmpy, "boundary");
394         title_textheight = (int) ((titlelin + 1) * (t->v_char) + tmpy);
395     } else
396         title_textheight = 0;
397
398     /* x2label */
399     if (x2lablin) {
400         double tmpx, tmpy;
401         map_position_r(&(axis_array[SECOND_X_AXIS].label.offset),
402                        &tmpx, &tmpy, "boundary");
403         x2label_textheight = (int) (x2lablin * t->v_char + tmpy);
404         if (!axis_array[SECOND_X_AXIS].ticmode)
405             x2label_textheight += 0.5 * t->v_char;
406     } else
407         x2label_textheight = 0;
408
409     /* tic labels */
410     if (axis_array[SECOND_X_AXIS].ticmode & TICS_ON_BORDER) {
411         /* ought to consider tics on axes if axis near border */
412         if (vertical_x2tics) {
413             /* guess at tic length, since we don't know it yet
414                --- we'll fix it after the tic labels have been created */
415             x2tic_textheight = (int) (5 * t->h_char);
416         } else
417             x2tic_textheight = (int) (x2ticlin * t->v_char);
418     } else
419         x2tic_textheight = 0;
420
421     /* tics */
422     if (!axis_array[SECOND_X_AXIS].tic_in
423         && ((axis_array[SECOND_X_AXIS].ticmode & TICS_ON_BORDER)
424             || ((axis_array[FIRST_X_AXIS].ticmode & TICS_MIRROR)
425                 && (axis_array[FIRST_X_AXIS].ticmode & TICS_ON_BORDER))))
426         x2tic_height = (int) (t->v_tic * axis_array[SECOND_X_AXIS].ticscale);
427     else
428         x2tic_height = 0;
429
430     /* timestamp */
431     if (timelabel.text && !timelabel_bottom) {
432         double tmpx, tmpy;
433         map_position_r(&(timelabel.offset), &tmpx, &tmpy, "boundary");
434         timetop_textheight = (int) ((timelin + 2) * t->v_char + tmpy);
435     } else
436         timetop_textheight = 0;
437
438     /* horizontal ylabel */
439     if (axis_array[FIRST_Y_AXIS].label.text && !can_rotate) {
440         double tmpx, tmpy;
441         map_position_r(&(axis_array[FIRST_Y_AXIS].label.offset),
442                        &tmpx, &tmpy, "boundary");
443         ylabel_textheight = (int) (ylablin * t->v_char + tmpy);
444     } else
445         ylabel_textheight = 0;
446
447     /* horizontal y2label */
448     if (axis_array[SECOND_Y_AXIS].label.text && !can_rotate) {
449         double tmpx, tmpy;
450         map_position_r(&(axis_array[SECOND_Y_AXIS].label.offset),
451                        &tmpx, &tmpy, "boundary");
452         y2label_textheight = (int) (y2lablin * t->v_char + tmpy);
453     } else
454         y2label_textheight = 0;
455
456     /* compute plot_bounds.ytop from the various components
457      *     unless tmargin is explicitly specified  */
458
459     /* HBB 20010118: fix round-off bug */
460     plot_bounds.ytop = (int) (0.5 + (ysize + yoffset) * t->ymax);
461
462     if (tmargin.scalex == screen) {
463         /* Specified as absolute position on the canvas */
464         plot_bounds.ytop = tmargin.x * (float)t->ymax;
465     } else if (tmargin.x >=0) {
466         /* Specified in terms of character height */
467         plot_bounds.ytop -= tmargin.x * (float)t->v_char + 0.5;
468     } else {
469         /* Auto-calculation of space required */
470         int top_margin = x2label_textheight + title_textheight;
471
472         if (timetop_textheight + ylabel_textheight > top_margin)
473             top_margin = timetop_textheight + ylabel_textheight;
474         if (y2label_textheight > top_margin)
475             top_margin = y2label_textheight;
476
477         top_margin += x2tic_height + x2tic_textheight;
478         /* x2tic_height and x2tic_textheight are computed as only the
479          *     relevant heights, but they nonetheless need a blank
480          *     space above them  */
481         if (top_margin > x2tic_height)
482             top_margin += (int) t->v_char;
483
484         plot_bounds.ytop -= top_margin;
485         if (plot_bounds.ytop == (int) (0.5 + (ysize + yoffset) * t->ymax)) {
486             /* make room for the end of rotated ytics or y2tics */
487             plot_bounds.ytop -= (int) (t->h_char * 2);
488         }
489     }
490
491     /*  end of preliminary plot_bounds.ytop calculation }}} */
492
493
494     /*{{{  preliminary plot_bounds.xleft, needed for "under" */
495     if (lmargin.scalex == screen)
496         plot_bounds.xleft = lmargin.x * (float)t->xmax;
497     else
498         plot_bounds.xleft = xoffset * t->xmax
499                           + t->h_char * (lmargin.x >= 0 ? lmargin.x : 2);
500     /*}}} */
501
502
503     /*{{{  tentative plot_bounds.xright, needed for "under" */
504     if (rmargin.scalex == screen)
505         plot_bounds.xright = rmargin.x * (float)t->xmax;
506     else
507         plot_bounds.xright = (xsize + xoffset) * t->xmax
508                            - t->h_char * (rmargin.x >= 0 ? rmargin.x : 2);
509     /*}}} */
510
511
512 #define COLORBOX_SCALE 0.125
513 #define WIDEST_COLORBOX_TICTEXT 3
514     /* Make room for the color box if anything in the graph uses a palette. */
515     set_plot_with_palette(0, MODE_PLOT); /* EAM FIXME - 1st parameter is a dummy */
516     if (rmargin.scalex != screen) {
517         if (is_plot_with_palette() && (color_box.where != SMCOLOR_BOX_NO)
518         && (color_box.where != SMCOLOR_BOX_USER)) {
519             plot_bounds.xright -= (int) (plot_bounds.xright-plot_bounds.xleft)*COLORBOX_SCALE;
520             plot_bounds.xright -= (int) ((t->h_char) * WIDEST_COLORBOX_TICTEXT);
521         }
522     }
523
524     /*{{{  preliminary plot_bounds.ybot calculation
525      *     first compute heights of labels and tics */
526
527     /* tic labels */
528     shift_labels_to_border = FALSE;
529     if (axis_array[FIRST_X_AXIS].ticmode & TICS_ON_AXIS) {
530         /* FIXME: This test for how close the axis is to the border does not match */
531         /*        the tests in axis_output_tics(), and assumes FIRST_Y_AXIS.       */
532         if (!inrange(0.0, axis_array[FIRST_Y_AXIS].min, axis_array[FIRST_Y_AXIS].max))
533             shift_labels_to_border = TRUE;
534         if (0.05 > fabs( axis_array[FIRST_Y_AXIS].min
535                 / (axis_array[FIRST_Y_AXIS].max - axis_array[FIRST_Y_AXIS].min)))
536             shift_labels_to_border = TRUE;
537     }
538     if ((axis_array[FIRST_X_AXIS].ticmode & TICS_ON_BORDER)
539     ||  shift_labels_to_border) {
540         /* ought to consider tics on axes if axis near border */
541         if (vertical_xtics) {
542             /* guess at tic length, since we don't know it yet */
543             xtic_textheight = (int) (t->h_char * 5);
544         } else
545             xtic_textheight = (int) (t->v_char * (xticlin + 1));
546     } else
547         xtic_textheight =  0;
548
549     /* tics */
550     if (!axis_array[FIRST_X_AXIS].tic_in
551         && ((axis_array[FIRST_X_AXIS].ticmode & TICS_ON_BORDER)
552             || ((axis_array[SECOND_X_AXIS].ticmode & TICS_MIRROR)
553                 && (axis_array[SECOND_X_AXIS].ticmode & TICS_ON_BORDER))))
554         xtic_height = (int) (t->v_tic * axis_array[FIRST_X_AXIS].ticscale);
555     else
556         xtic_height = 0;
557
558     /* xlabel */
559     if (xlablin) {
560         double tmpx, tmpy;
561         map_position_r(&(axis_array[FIRST_X_AXIS].label.offset),
562                        &tmpx, &tmpy, "boundary");
563         /* offset is subtracted because if > 0, the margin is smaller */
564         /* textheight is inflated by 0.2 to allow descenders to clear bottom of canvas */
565         xlabel_textheight = (((float)xlablin + 0.2) * t->v_char - tmpy);
566         if (!axis_array[FIRST_X_AXIS].ticmode)
567             xlabel_textheight += 0.5 * t->v_char;
568     } else
569         xlabel_textheight = 0;
570
571     /* timestamp */
572     if (timelabel.text && timelabel_bottom) {
573         /* && !vertical_timelabel)
574          * DBT 11-18-98 resize plot for vertical timelabels too !
575          */
576         double tmpx, tmpy;
577         map_position_r(&(timelabel.offset), &tmpx, &tmpy, "boundary");
578         /* offset is subtracted because if . 0, the margin is smaller */
579         timebot_textheight = (int) (timelin * t->v_char - tmpy);
580     } else
581         timebot_textheight = 0;
582
583     /* compute plot_bounds.ybot from the various components
584      *     unless bmargin is explicitly specified  */
585
586     plot_bounds.ybot = yoffset * (float)t->ymax + 0.5;
587
588     if (bmargin.scalex == screen) {
589         /* Absolute position for bottom of plot */
590         plot_bounds.ybot = bmargin.x * (float)t->ymax;
591     } else if (bmargin.x >= 0) {
592         /* Position based on specified character height */
593         plot_bounds.ybot += bmargin.x * (float)t->v_char + 0.5;
594     } else {
595         plot_bounds.ybot += xtic_height + xtic_textheight;
596         if (xlabel_textheight > 0)
597             plot_bounds.ybot += xlabel_textheight;
598         if (timebot_textheight > 0)
599             plot_bounds.ybot += timebot_textheight;
600         /* HBB 19990616: round to nearest integer, required to escape
601          * floating point inaccuracies */
602         if (plot_bounds.ybot == (int) (0.5 + t->ymax * yoffset)) {
603             /* make room for the end of rotated ytics or y2tics */
604             plot_bounds.ybot += (int) (t->h_char * 2);
605         }
606     }
607
608     /*  end of preliminary plot_bounds.ybot calculation }}} */
609
610     /* EAM FIXME - 
611      * I don't understand why this is necessary, but it is.
612      * Didn't we already do this at line 488ff, and then add colorbox? */
613     if (lmargin.scalex != screen)
614         plot_bounds.xleft = xoffset * t->xmax
615                           + t->h_char * (lmargin.x >= 0 ? lmargin.x : 2);
616     if (rmargin.scalex != screen)
617         plot_bounds.xright = (xsize + xoffset) * t->xmax
618                            - t->h_char * (rmargin.x >= 0 ? rmargin.x : 2);
619
620     if (lkey) {
621         TBOOLEAN key_panic = FALSE;
622         /*{{{  essential key features */
623
624         p_width = pointsize * t->h_tic;
625         p_height = pointsize * t->v_tic;
626
627         if (key->swidth >= 0)
628             key_sample_width = key->swidth * t->h_char + p_width;
629         else
630             key_sample_width = 0;
631
632         key_entry_height = p_height * 1.25 * key->vert_factor;
633         if (key_entry_height < t->v_char)
634             key_entry_height = t->v_char * key->vert_factor;
635         /* HBB 20020122: safeguard to prevent division by zero later */
636         if (key_entry_height == 0)
637             key_entry_height = 1;
638
639         /* Count max_len key and number keys with len > 0 */
640         max_ptitl_len = find_maxl_keys(plots, count, &ptitl_cnt);
641
642         /* Key title length and height */
643         if (key->title) {
644             int ytlen, ytheight;
645             ytlen = label_width(key->title, &ytheight);
646             ytlen -= key->swidth + 2;
647             if (ytlen > max_ptitl_len)
648                 max_ptitl_len = ytlen;
649             ktitl_lines = (int)ytheight;
650         }
651
652         if (key->reverse) {
653             key_sample_left = -key_sample_width;
654             key_sample_right = 0;
655             /* if key width is being used, adjust right-justified text */
656             key_text_left = t->h_char;
657             key_text_right = t->h_char * (max_ptitl_len + 1 + key->width_fix);
658             key_size_left = t->h_char - key_sample_left; /* sample left is -ve */
659             key_size_right = key_text_right;
660         } else {
661             key_sample_left = 0;
662             key_sample_right = key_sample_width;
663             /* if key width is being used, adjust left-justified text */
664             key_text_left = -(int) (t->h_char
665                                     * (max_ptitl_len + 1 + key->width_fix));
666             key_text_right = -(int) t->h_char;
667             key_size_left = -key_text_left;
668             key_size_right = key_sample_right + t->h_char;
669         }
670         key_point_offset = (key_sample_left + key_sample_right) / 2;
671
672         /* advance width for cols */
673         key_col_wth = key_size_left + key_size_right;
674
675         key_rows = ptitl_cnt;
676         key_cols = 1;
677
678         /* calculate rows and cols for key */
679
680         if (key->stack_dir == GPKEY_HORIZONTAL) {
681             /* maximise no cols, limited by label-length */
682             key_cols = (int) (plot_bounds.xright - plot_bounds.xleft) / key_col_wth;
683             /* EAM Dec 2004 - Rather than turn off the key, try to squeeze */
684             if (key_cols == 0) {
685                 key_cols = 1;
686                 key_panic = TRUE;
687                 key_col_wth = (plot_bounds.xright - plot_bounds.xleft);
688             }
689             key_rows = (int) (ptitl_cnt + key_cols - 1) / key_cols;
690             /* now calculate actual no cols depending on no rows */
691             key_cols = (key_rows == 0) ? 1
692                 : (int) (ptitl_cnt + key_rows - 1) / key_rows;
693             if (key_cols == 0) {
694                 key_cols = 1;
695                 key_panic = TRUE;
696             }
697         } else {
698             /* maximise no rows, limited by plot_bounds.ytop-plot_bounds.ybot */
699             int i = (int) (plot_bounds.ytop - plot_bounds.ybot - key->height_fix * t->v_char
700                            - (ktitl_lines + 1) * t->v_char)
701                 / key_entry_height;
702
703             if (i == 0) {
704                 i = 1;
705                 key_panic = TRUE;
706             }
707             if (ptitl_cnt > i) {
708                 key_cols = (int) (ptitl_cnt + i - 1) / i;
709                 /* now calculate actual no rows depending on no cols */
710                 if (key_cols == 0) {
711                     key_cols = 1;
712                     key_panic = TRUE;
713                 }
714                 key_rows = (int) (ptitl_cnt + key_cols - 1) / key_cols;
715             }
716         }
717
718         /* warn if we had to punt on key size calculations */
719         if (key_panic)
720             int_warn(NO_CARET, "Warning - difficulty fitting plot titles into key");
721
722         /* adjust for outside key, leave manually set margins alone */
723         if ((key->region == GPKEY_AUTO_EXTERIOR_LRTBC && (key->vpos != JUST_CENTRE || key->hpos != CENTRE))
724             || key->region == GPKEY_AUTO_EXTERIOR_MARGIN) {
725             if (key->margin == GPKEY_BMARGIN && bmargin.x < 0) {
726                 plot_bounds.ybot += key_entry_height * key_rows
727                     + (int) (t->v_char * (ktitl_lines + 1));
728                 plot_bounds.ybot += (int) (key->height_fix * t->v_char);
729             } else if (key->margin == GPKEY_TMARGIN && tmargin.x < 0) {
730                 plot_bounds.ytop -= key_entry_height * key_rows
731                     + (int) (t->v_char * (ktitl_lines + 1));
732                 plot_bounds.ytop += (int) (key->height_fix * t->v_char);
733             } else if (key->margin == GPKEY_LMARGIN && lmargin.x < 0) {
734                 plot_bounds.xleft += key_col_wth * key_cols;
735             } else if (key->margin == GPKEY_RMARGIN && rmargin.x < 0) {
736                 plot_bounds.xright -= key_col_wth * key_cols;
737             }
738         }
739         /*}}} */
740     }
741
742     /*{{{  set up y and y2 tics */
743     setup_tics(FIRST_Y_AXIS, 20);
744     setup_tics(SECOND_Y_AXIS, 20);
745     /*}}} */
746
747     /* Adjust color axis limits if necessary. */
748     if (is_plot_with_palette()) {
749         set_cbminmax();
750         axis_checked_extend_empty_range(COLOR_AXIS, "All points of color axis undefined.");
751         setup_tics(COLOR_AXIS, 20);
752     }
753
754     /*{{{  recompute plot_bounds.xleft based on widths of ytics, ylabel etc
755        unless it has been explicitly set by lmargin */
756
757     /* tic labels */
758     shift_labels_to_border = FALSE;
759     if (axis_array[FIRST_Y_AXIS].ticmode & TICS_ON_AXIS) {
760         /* FIXME: This test for how close the axis is to the border does not match */
761         /*        the tests in axis_output_tics(), and assumes FIRST_X_AXIS.       */
762         if (!inrange(0.0, axis_array[FIRST_X_AXIS].min, axis_array[FIRST_X_AXIS].max))
763             shift_labels_to_border = TRUE;
764         if (0.1 > fabs( axis_array[FIRST_X_AXIS].min
765                /  (axis_array[FIRST_X_AXIS].max - axis_array[FIRST_X_AXIS].min)))
766             shift_labels_to_border = TRUE;
767     }
768     
769     if ((axis_array[FIRST_Y_AXIS].ticmode & TICS_ON_BORDER)
770     ||  shift_labels_to_border) {
771         if (vertical_ytics)
772             /* HBB: we will later add some white space as part of this, so
773              * reserve two more rows (one above, one below the text ...).
774              * Same will be done to similar calc.'s elsewhere */
775             ytic_textwidth = (int) (t->v_char * (yticlin + 2));
776         else {
777             widest_tic_strlen = 0;      /* reset the global variable ... */
778             /* get gen_tics to call widest_tic_callback with all labels
779              * the latter sets widest_tic_strlen to the length of the widest
780              * one ought to consider tics on axis if axis near border...
781              */
782             gen_tics(FIRST_Y_AXIS, /* 0, */ widest_tic_callback);
783
784             ytic_textwidth = (int) (t->h_char * (widest_tic_strlen + 2));
785         }
786     } else {
787         ytic_textwidth = 0;
788     }
789
790     /* tics */
791     if (!axis_array[FIRST_Y_AXIS].tic_in
792         && ((axis_array[FIRST_Y_AXIS].ticmode & TICS_ON_BORDER)
793             || ((axis_array[SECOND_Y_AXIS].ticmode & TICS_MIRROR)
794                 && (axis_array[SECOND_Y_AXIS].ticmode & TICS_ON_BORDER))))
795         ytic_width = (int) (t->h_tic * axis_array[FIRST_Y_AXIS].ticscale);
796     else
797         ytic_width = 0;
798
799     /* ylabel */
800     if (axis_array[FIRST_Y_AXIS].label.text && can_rotate) {
801         double tmpx, tmpy;
802         map_position_r(&(axis_array[FIRST_Y_AXIS].label.offset),
803                        &tmpx, &tmpy, "boundary");
804         ylabel_textwidth = (int) (ylablin * (t->v_char) - tmpx);
805         if (!axis_array[FIRST_Y_AXIS].ticmode)
806             ylabel_textwidth += 0.5 * t->v_char;
807     } else
808         /* this should get large for NEGATIVE ylabel.xoffsets  DBT 11-5-98 */
809         ylabel_textwidth = 0;
810
811     /* timestamp */
812     if (timelabel.text && vertical_timelabel) {
813         double tmpx, tmpy;
814         map_position_r(&(timelabel.offset), &tmpx, &tmpy, "boundary");
815         timelabel_textwidth = (int) ((timelin + 1.5) * t->v_char - tmpx);
816     } else
817         timelabel_textwidth = 0;
818
819     if (lmargin.x < 0) {        
820         /* Auto-calculation */
821         double tmpx, tmpy;
822
823         plot_bounds.xleft += (timelabel_textwidth > ylabel_textwidth
824                   ? timelabel_textwidth : ylabel_textwidth)
825             + ytic_width + ytic_textwidth;
826
827         /* make sure plot_bounds.xleft is wide enough for a negatively
828          * x-offset horizontal timestamp
829          */
830         map_position_r(&(timelabel.offset), &tmpx, &tmpy, "boundary");
831         if (!vertical_timelabel
832             && plot_bounds.xleft - ytic_width - ytic_textwidth < -(int) (tmpx))
833             plot_bounds.xleft = ytic_width + ytic_textwidth - (int) (tmpx);
834         /* EAM FIXME: Skip if "lmargin at screen ..."? */
835         if (plot_bounds.xleft == (int) (0.5 + t->xmax * xoffset)) {
836             /* make room for end of xtic or x2tic label */
837             plot_bounds.xleft += (int) (t->h_char * 2);
838         }
839         /* DBT 12-3-98  extra margin just in case */
840         plot_bounds.xleft += 0.5 * t->h_char;
841     }
842     /* Note: we took care of explicit 'set lmargin foo' at line 492 */
843
844     /*  end of plot_bounds.xleft calculation }}} */
845
846     /*{{{  recompute plot_bounds.xright based on widest y2tic. y2labels, key "outside"
847        unless it has been explicitly set by rmargin */
848
849     /* tic labels */
850     if (axis_array[SECOND_Y_AXIS].ticmode & TICS_ON_BORDER) {
851         if (vertical_y2tics)
852             y2tic_textwidth = (int) (t->v_char * (y2ticlin + 2));
853         else {
854             widest_tic_strlen = 0;      /* reset the global variable ... */
855             /* get gen_tics to call widest_tic_callback with all labels
856              * the latter sets widest_tic_strlen to the length of the widest
857              * one ought to consider tics on axis if axis near border...
858              */
859             gen_tics(SECOND_Y_AXIS, /* 0, */ widest_tic_callback);
860
861             y2tic_textwidth = (int) (t->h_char * (widest_tic_strlen + 2));
862         }
863     } else {
864         y2tic_textwidth = 0;
865     }
866
867     /* tics */
868     if (!axis_array[SECOND_Y_AXIS].tic_in
869         && ((axis_array[SECOND_Y_AXIS].ticmode & TICS_ON_BORDER)
870             || ((axis_array[FIRST_Y_AXIS].ticmode & TICS_MIRROR)
871                 && (axis_array[FIRST_Y_AXIS].ticmode & TICS_ON_BORDER))))
872         y2tic_width = (int) (t->h_tic * axis_array[SECOND_Y_AXIS].ticscale);
873     else
874         y2tic_width = 0;
875
876     /* y2label */
877     if (can_rotate && axis_array[SECOND_Y_AXIS].label.text) {
878         double tmpx, tmpy;
879         map_position_r(&(axis_array[SECOND_Y_AXIS].label.offset),
880                        &tmpx, &tmpy, "boundary");
881         y2label_textwidth = (int) (y2lablin * t->v_char + tmpx);
882         if (!axis_array[SECOND_Y_AXIS].ticmode)
883             y2label_textwidth += 0.5 * t->v_char;
884     } else
885         y2label_textwidth = 0;
886
887     /* Make room for the color box if needed. */
888     if (rmargin.scalex != screen) {
889         if (is_plot_with_palette() && is_plot_with_colorbox()
890         && (color_box.where != SMCOLOR_BOX_NO) && (color_box.where != SMCOLOR_BOX_USER)) {
891             plot_bounds.xright -= (int) (plot_bounds.xright-plot_bounds.xleft)*COLORBOX_SCALE;
892             plot_bounds.xright -= (int) ((t->h_char) * WIDEST_COLORBOX_TICTEXT);
893         }
894
895         if (rmargin.x < 0) {
896             /* plot_bounds.xright -= y2label_textwidth + y2tic_width + y2tic_textwidth; */
897             plot_bounds.xright -= y2tic_width + y2tic_textwidth;
898             if (y2label_textwidth > 0)
899                 plot_bounds.xright -= y2label_textwidth;
900
901             if (plot_bounds.xright == (int) (0.5 + t->xmax * (xsize + xoffset))) {
902                 /* make room for end of xtic or x2tic label */
903                 plot_bounds.xright -= (int) (t->h_char * 2);
904             }
905             /* DBT 12-3-98  extra margin just in case */
906             plot_bounds.xright -= 0.5 * t->h_char;
907         }
908         /* Note: we took care of explicit 'set rmargin foo' at line 502 */
909     }
910
911     /*  end of plot_bounds.xright calculation }}} */
912
913
914     /*{{{  set up x and x2 tics */
915     /* we should base the guide on the width of the xtics, but we cannot
916      * use widest_tics until tics are set up. Bit of a downer - let us
917      * assume tics are 5 characters wide
918      */
919     /* HBB 20001205: moved this block to before aspect_ratio is
920      * applied: setup_tics may extend the ranges, which would distort
921      * the aspect ratio */
922
923     setup_tics(FIRST_X_AXIS, 20);
924     setup_tics(SECOND_X_AXIS, 20);
925
926
927     /* Modify the bounding box to fit the aspect ratio, if any was
928      * given. */
929     if (aspect_ratio != 0.0) {
930         double current_aspect_ratio;
931
932         if (aspect_ratio < 0
933             && (X_AXIS.max - X_AXIS.min) != 0.0
934             ) {
935             current_aspect_ratio = - aspect_ratio
936                 * fabs((Y_AXIS.max - Y_AXIS.min) / (X_AXIS.max - X_AXIS.min));
937         } else
938             current_aspect_ratio = aspect_ratio;
939
940         /* Set aspect ratio if valid and sensible */
941         /* EAM Mar 2008 - fixed borders take precedence over centering */
942         if (current_aspect_ratio >= 0.01 && current_aspect_ratio <= 100.0) {
943             double current = ((double) (plot_bounds.ytop - plot_bounds.ybot)) 
944                            / (plot_bounds.xright - plot_bounds.xleft);
945             double required = (current_aspect_ratio * t->v_tic) / t->h_tic;
946
947             if (current > required) {
948                 /* too tall */
949                 int old_height = plot_bounds.ytop - plot_bounds.ybot;
950                 int new_height = required * (plot_bounds.xright - plot_bounds.xleft);
951                 if (bmargin.scalex == screen)
952                     plot_bounds.ytop = plot_bounds.ybot + new_height;
953                 else if (tmargin.scalex == screen)
954                     plot_bounds.ybot = plot_bounds.ytop - new_height;
955                 else {
956                     plot_bounds.ybot += (old_height - new_height) / 2;
957                     plot_bounds.ytop -= (old_height - new_height) / 2;
958                 }
959
960             } else {
961                 int old_width = plot_bounds.xright - plot_bounds.xleft;
962                 int new_width = (plot_bounds.ytop - plot_bounds.ybot) / required;
963                 if (lmargin.scalex == screen)
964                     plot_bounds.xright = plot_bounds.xleft + new_width;
965                 else if (rmargin.scalex == screen)
966                     plot_bounds.xleft = plot_bounds.xright - new_width;
967                 else {
968                     plot_bounds.xleft += (old_width - new_width) / 2;
969                     plot_bounds.xright -= (old_width - new_width) / 2;
970                 }
971             }
972         }
973     }
974
975     /*  adjust top and bottom margins for tic label rotation */
976
977     if (tmargin.x < 0
978         && axis_array[SECOND_X_AXIS].ticmode & TICS_ON_BORDER
979         && vertical_x2tics) {
980         double projection = sin((double)axis_array[SECOND_X_AXIS].tic_rotate*DEG2RAD);
981         widest_tic_strlen = 0;          /* reset the global variable ... */
982         gen_tics(SECOND_X_AXIS, /* 0, */ widest_tic_callback);
983         plot_bounds.ytop += x2tic_textheight;
984         /* Now compute a new one and use that instead: */
985         if (projection > 0.0)
986             x2tic_textheight = (int) (t->h_char * (widest_tic_strlen)) * projection;
987         else
988             x2tic_textheight = t->v_char;
989         plot_bounds.ytop -= x2tic_textheight;
990     }
991     if (bmargin.x < 0
992         && axis_array[FIRST_X_AXIS].ticmode & TICS_ON_BORDER
993         && vertical_xtics) {
994         double projection;
995         if (axis_array[FIRST_X_AXIS].tic_rotate == 90)
996             projection = 1.0;
997         else
998             projection = -sin((double)axis_array[FIRST_X_AXIS].tic_rotate*DEG2RAD);
999         widest_tic_strlen = 0;          /* reset the global variable ... */
1000         gen_tics(FIRST_X_AXIS, /* 0, */ widest_tic_callback);
1001         plot_bounds.ybot -= xtic_textheight;
1002         if (projection > 0.0)
1003             xtic_textheight = (int) (t->h_char * widest_tic_strlen) * projection
1004                             + t->v_char;
1005         plot_bounds.ybot += xtic_textheight;
1006     }
1007
1008     /* EAM - FIXME
1009      * Notwithstanding all these fancy calculations, plot_bounds.ytop must always be above plot_bounds.ybot
1010      */
1011     if (plot_bounds.ytop < plot_bounds.ybot) {
1012         int i = plot_bounds.ytop;
1013
1014         plot_bounds.ytop = plot_bounds.ybot;
1015         plot_bounds.ybot = i;
1016         FPRINTF((stderr,"boundary: Big problems! plot_bounds.ybot > plot_bounds.ytop\n"));
1017     }
1018
1019     /*  compute coordinates for axis labels, title et al
1020      *     (some of these may not be used) */
1021
1022     x2label_y = plot_bounds.ytop + x2tic_height + x2tic_textheight + x2label_textheight;
1023     if (x2tic_textheight && (title_textheight || x2label_textheight))
1024         x2label_y += t->v_char;
1025
1026     title_y = x2label_y + title_textheight;
1027
1028     ylabel_y = plot_bounds.ytop + x2tic_height + x2tic_textheight + ylabel_textheight;
1029
1030     y2label_y = plot_bounds.ytop + x2tic_height + x2tic_textheight + y2label_textheight;
1031
1032     /* Shift upward by 0.2 line to allow for descenders in xlabel text */
1033     xlabel_y = plot_bounds.ybot - xtic_height - xtic_textheight - xlabel_textheight
1034         + ((float)xlablin+0.2) * t->v_char;
1035     ylabel_x = plot_bounds.xleft - ytic_width - ytic_textwidth;
1036     if (axis_array[FIRST_Y_AXIS].label.text && can_rotate)
1037         ylabel_x -= ylabel_textwidth;
1038
1039     y2label_x = plot_bounds.xright + y2tic_width + y2tic_textwidth;
1040     if (axis_array[SECOND_Y_AXIS].label.text && can_rotate)
1041         y2label_x += y2label_textwidth - y2lablin * t->v_char;
1042
1043     if (vertical_timelabel) {
1044         if (timelabel_bottom)
1045             time_y = xlabel_y - timebot_textheight + xlabel_textheight;
1046         else {
1047             time_y = title_y + timetop_textheight - title_textheight
1048                 - x2label_textheight;
1049         }
1050     } else {
1051         if (timelabel_bottom)
1052             time_y = plot_bounds.ybot - xtic_height - xtic_textheight - xlabel_textheight
1053                 - timebot_textheight + t->v_char;
1054         else if (ylabel_textheight > 0)
1055             time_y = ylabel_y + timetop_textheight;
1056         else
1057             time_y = plot_bounds.ytop + x2tic_height + x2tic_textheight
1058                 + timetop_textheight + (int) t->h_char;
1059     }
1060     if (vertical_timelabel)
1061         time_x = plot_bounds.xleft - ytic_width - ytic_textwidth - timelabel_textwidth;
1062     else {
1063         double tmpx, tmpy;
1064         map_position_r(&(timelabel.offset), &tmpx, &tmpy, "boundary");
1065         time_x = plot_bounds.xleft - ytic_width - ytic_textwidth + (int) (tmpx);
1066     }
1067
1068     xtic_y = plot_bounds.ybot - xtic_height
1069         - (int) (vertical_xtics ? t->h_char : t->v_char);
1070
1071     x2tic_y = plot_bounds.ytop + x2tic_height
1072         + (vertical_x2tics ? (int) t->h_char : x2tic_textheight);
1073
1074     ytic_x = plot_bounds.xleft - ytic_width
1075         - (vertical_ytics
1076            ? (ytic_textwidth - (int) t->v_char)
1077            : (int) t->h_char);
1078
1079     y2tic_x = plot_bounds.xright + y2tic_width
1080         + (int) (vertical_y2tics ? t->v_char : t->h_char);
1081
1082     /* restore text to horizontal [we tested rotation above] */
1083     (void) (*t->text_angle) (0);
1084
1085     /* needed for map_position() below */
1086     AXIS_SETSCALE(FIRST_Y_AXIS, plot_bounds.ybot, plot_bounds.ytop);
1087     AXIS_SETSCALE(SECOND_Y_AXIS, plot_bounds.ybot, plot_bounds.ytop);
1088     AXIS_SETSCALE(FIRST_X_AXIS, plot_bounds.xleft, plot_bounds.xright);
1089     AXIS_SETSCALE(SECOND_X_AXIS, plot_bounds.xleft, plot_bounds.xright);
1090     /* HBB 20020122: moved here from do_plot, because map_position
1091      * needs these, too */
1092     axis_set_graphical_range(FIRST_X_AXIS, plot_bounds.xleft, plot_bounds.xright);
1093     axis_set_graphical_range(FIRST_Y_AXIS, plot_bounds.ybot, plot_bounds.ytop);
1094     axis_set_graphical_range(SECOND_X_AXIS, plot_bounds.xleft, plot_bounds.xright);
1095     axis_set_graphical_range(SECOND_Y_AXIS, plot_bounds.ybot, plot_bounds.ytop);
1096
1097 #define ALIGN_BORDERS 1
1098     /* Calculate space for keys (do_plot will use these to position key). */
1099     key_w = key_col_wth * key_cols;
1100     key_h = (ktitl_lines) * t->v_char + key_rows * key_entry_height;
1101     key_h += (int) (key->height_fix * t->v_char);
1102     if (key->region == GPKEY_AUTO_INTERIOR_LRTBC
1103         || (key->region == GPKEY_AUTO_EXTERIOR_LRTBC && key->vpos == JUST_CENTRE && key->hpos == CENTRE)) {
1104         if (key->vpos == JUST_TOP) {
1105             keybox.yt = plot_bounds.ytop - t->v_tic;
1106             keybox.yb = keybox.yt - key_h;
1107         } else if (key->vpos == JUST_BOT) {
1108             keybox.yb = plot_bounds.ybot + t->v_tic;
1109             keybox.yt = keybox.yb + key_h;
1110         } else /* (key->vpos == JUST_CENTRE) */ {
1111             int key_box_half = key_h / 2;
1112             keybox.yb = (plot_bounds.ybot + plot_bounds.ytop) / 2 - key_box_half;
1113             keybox.yt = (plot_bounds.ybot + plot_bounds.ytop) / 2 + key_box_half;
1114         }
1115         if (key->hpos == LEFT) {
1116             keybox.xl = plot_bounds.xleft + t->h_char;
1117             keybox.xr = keybox.xl + key_w;
1118         } else if (key->hpos == RIGHT) {
1119             keybox.xr = plot_bounds.xright - t->h_char;
1120             keybox.xl = keybox.xr - key_w;
1121         } else /* (key->hpos == CENTER) */ {
1122             int key_box_half = key_w / 2;
1123             keybox.xl = (plot_bounds.xright + plot_bounds.xleft) / 2 - key_box_half;
1124             keybox.xr = (plot_bounds.xright + plot_bounds.xleft) / 2 + key_box_half;
1125         }
1126     } else if (key->region == GPKEY_AUTO_EXTERIOR_LRTBC || key->region == GPKEY_AUTO_EXTERIOR_MARGIN) {
1127
1128         /* Vertical alignment */
1129         if (key->margin == GPKEY_TMARGIN) {
1130             /* align top first since tmargin may be manual */
1131             keybox.yt = (ysize + yoffset) * t->ymax - t->v_tic;
1132             keybox.yb = keybox.yt - key_h;
1133         } else if (key->margin == GPKEY_BMARGIN) {
1134             /* align bottom first since bmargin may be manual */
1135             keybox.yb = yoffset * t->ymax + t->v_tic;
1136             keybox.yt = keybox.yb + key_h;
1137         } else {
1138             if (key->vpos == JUST_TOP) {
1139                 /* align top first since tmargin may be manual */
1140 #if ALIGN_BORDERS
1141                 keybox.yt = plot_bounds.ytop;
1142 #else
1143                 keybox.yt = (ysize + yoffset) * t->ymax - t->v_tic;
1144 #endif
1145                 keybox.yb = keybox.yt - key_h;
1146             } else if (key->vpos == CENTRE) {
1147                 int key_box_half = key_h / 2;
1148                 keybox.yb = (plot_bounds.ybot + plot_bounds.ytop) / 2 - key_box_half;
1149                 keybox.yt = (plot_bounds.ybot + plot_bounds.ytop) / 2 + key_box_half;
1150             } else {
1151                 /* align bottom first since bmargin may be manual */
1152 #if ALIGN_BORDERS
1153                 keybox.yb = plot_bounds.ybot;
1154 #else
1155                 keybox.yb = yoffset * t->ymax + t->v_tic;
1156 #endif
1157                 keybox.yt = keybox.yb + key_h;
1158             }
1159         }
1160
1161         /* Horizontal alignment */
1162         if (key->margin == GPKEY_LMARGIN) {
1163             /* align left first since lmargin may be manual */
1164             keybox.xl = xoffset * t->xmax + t->h_char;
1165             keybox.xr = keybox.xl + key_w;
1166         } else if (key->margin == GPKEY_RMARGIN) {
1167             /* align right first since rmargin may be manual */
1168             keybox.xr = (xsize + xoffset) * t->xmax - t->h_char;
1169             keybox.xl = keybox.xr - key_w;
1170         } else {
1171             if (key->hpos == LEFT) {
1172                 /* align left first since lmargin may be manual */
1173 #if ALIGN_BORDERS
1174                 keybox.xl = plot_bounds.xleft;
1175 #else
1176                 keybox.xl = xoffset * t->xmax + t->h_char;
1177 #endif
1178                 keybox.xr = keybox.xl + key_w;
1179             } else if (key->hpos == CENTRE) {
1180                 int key_box_half = key_w / 2;
1181                 keybox.xl = (plot_bounds.xright + plot_bounds.xleft) / 2 - key_box_half;
1182                 keybox.xr = (plot_bounds.xright + plot_bounds.xleft) / 2 + key_box_half;
1183             } else {
1184                 /* align right first since rmargin may be manual */
1185 #if ALIGN_BORDERS
1186                 keybox.xr = plot_bounds.xright;
1187 #else
1188                 keybox.xr = (xsize + xoffset) * t->xmax - t->h_char;
1189 #endif
1190                 keybox.xl = keybox.xr - key_w;
1191             }
1192         }
1193
1194     } else {
1195         int x, y;
1196
1197         map_position(&key->user_pos, &x, &y, "key");
1198 #if 0
1199 /* FIXME!!!
1200 ** pm 22.1.2002: if key->user_pos.scalex or scaley == first_axes or second_axes,
1201 ** then the graph scaling is not yet known and the box is positioned incorrectly;
1202 ** you must do "replot" to avoid the wrong plot ... bad luck if output does not
1203 ** go to screen */
1204 #define OK fprintf(stderr,"Line %i of %s is OK\n",__LINE__,__FILE__)
1205         OK;
1206         fprintf(stderr,"\tHELE: user pos: x=%i y=%i\n",key->user_pos.x,key->user_pos.y);
1207         fprintf(stderr,"\tHELE: user pos: x=%i y=%i\n",x,y);
1208 #endif
1209         /* Here top, bottom, left, right refer to the alignment with respect to point. */
1210         keybox.xl = x;
1211         if (key->hpos == CENTRE)
1212             keybox.xl -= key_w/2;
1213         else if (key->hpos == RIGHT)
1214             keybox.xl -= key_w;
1215         keybox.xr = keybox.xl + key_w;
1216         keybox.yt = y;
1217         if (key->vpos == JUST_CENTRE)
1218             keybox.yt += key_h/2;
1219         else if (key->vpos == JUST_BOT)
1220             keybox.yt += key_h;
1221         keybox.yb = keybox.yt - key_h;
1222     }
1223     /*}}} */
1224
1225     /* Set default clipping to the plot boundary */
1226     clip_area = &plot_bounds;
1227
1228 }
1229
1230 /*}}} */
1231
1232
1233 static void
1234 get_arrow(
1235     struct arrow_def *arrow,
1236     int* sx, int* sy,
1237     int* ex, int* ey)
1238 {
1239     double sx_d, sy_d, ex_d, ey_d;
1240     map_position_double(&arrow->start, &sx_d, &sy_d, "arrow");
1241     *sx = (int)(sx_d);
1242     *sy = (int)(sy_d);
1243     if (arrow->relative) {
1244         /* different coordinate systems:
1245          * add the values in the drivers coordinate system.
1246          * For log scale: relative coordinate is factor */
1247         map_position_r(&arrow->end, &ex_d, &ey_d, "arrow");
1248         *ex = (int)(ex_d + sx_d);
1249         *ey = (int)(ey_d + sy_d);
1250     } else {
1251         map_position_double(&arrow->end, &ex_d, &ey_d, "arrow");
1252         *ex = (int)(ex_d);
1253         *ey = (int)(ey_d);
1254     }
1255 }
1256
1257 /* FIXME HBB 20020225: this is shared with graph3d.c, so it shouldn't
1258  * be in this module */
1259 void
1260 apply_head_properties(struct arrow_style_type *arrow_properties)
1261 {
1262     curr_arrow_headfilled = arrow_properties->head_filled;
1263     curr_arrow_headlength = 0;
1264     if (arrow_properties->head_length > 0) {
1265         /* set head length+angle for term->arrow */
1266         int itmp, x1, x2;
1267         struct position headsize = {0,0,0,0.,0.,0.};
1268
1269         headsize.x = arrow_properties->head_length;
1270         headsize.scalex = arrow_properties->head_lengthunit;
1271
1272         headsize.y = 1.0; /* any value, just avoid log y */
1273         map_position(&headsize, &x2, &itmp, "arrow");
1274
1275         headsize.x = 0; /* measure length from zero */
1276         map_position(&headsize, &x1, &itmp, "arrow");
1277
1278         curr_arrow_headangle = arrow_properties->head_angle;
1279         curr_arrow_headbackangle = arrow_properties->head_backangle;
1280         curr_arrow_headlength = x2 - x1;
1281     }
1282 }
1283
1284 static void
1285 place_grid()
1286 {
1287     struct termentry *t = term;
1288
1289     term_apply_lp_properties(&border_lp);       /* border linetype */
1290     largest_polar_circle = 0;
1291
1292     /* select first mapping */
1293     x_axis = FIRST_X_AXIS;
1294     y_axis = FIRST_Y_AXIS;
1295
1296     /* label first y axis tics */
1297     axis_output_tics(FIRST_Y_AXIS, &ytic_x, FIRST_X_AXIS,
1298                      /* (GRID_Y | GRID_MY), */ ytick2d_callback);
1299     /* label first x axis tics */
1300     axis_output_tics(FIRST_X_AXIS, &xtic_y, FIRST_Y_AXIS,
1301                      /* (GRID_X | GRID_MX), */ xtick2d_callback);
1302
1303     /* select second mapping */
1304     x_axis = SECOND_X_AXIS;
1305     y_axis = SECOND_Y_AXIS;
1306
1307     axis_output_tics(SECOND_Y_AXIS, &y2tic_x, SECOND_X_AXIS,
1308                      /* (GRID_Y2 | GRID_MY2), */ ytick2d_callback);
1309     axis_output_tics(SECOND_X_AXIS, &x2tic_y, SECOND_Y_AXIS,
1310                      /* (GRID_X2 | GRID_MX2), */ xtick2d_callback);
1311
1312
1313     /* select first mapping */
1314     x_axis = FIRST_X_AXIS;
1315     y_axis = FIRST_Y_AXIS;
1316
1317 /* RADIAL LINES FOR POLAR GRID */
1318
1319     /* note that draw_clip_line takes unsigneds, but (fortunately)
1320      * clip_line takes signeds
1321      */
1322     if (polar_grid_angle) {
1323         double theta = 0;
1324         int ox = map_x(0);
1325         int oy = map_y(0);
1326         term_apply_lp_properties(&grid_lp);
1327         for (theta = 0; theta < 6.29; theta += polar_grid_angle) {
1328             /* copy ox in case it gets moved (but it shouldn't) */
1329             int oox = ox;
1330             int ooy = oy;
1331             int x = map_x(largest_polar_circle * cos(theta));
1332             int y = map_y(largest_polar_circle * sin(theta));
1333             if (clip_line(&oox, &ooy, &x, &y)) {
1334                 (*t->move) ((unsigned int) oox, (unsigned int) ooy);
1335                 (*t->vector) ((unsigned int) x, (unsigned int) y);
1336             }
1337         }
1338         draw_clip_line(ox, oy, map_x(largest_polar_circle * cos(theta)), map_y(largest_polar_circle * sin(theta)));
1339     }
1340 }
1341
1342 static void
1343 place_arrows(int layer)
1344 {
1345     struct arrow_def *this_arrow;
1346     BoundingBox *clip_save = clip_area;
1347
1348     /* Allow arrows to run off the plot, so long as they are still on the canvas */
1349     if (term->flags & TERM_CAN_CLIP)
1350       clip_area = NULL;
1351     else
1352       clip_area = &canvas;
1353
1354     for (this_arrow = first_arrow;
1355          this_arrow != NULL;
1356          this_arrow = this_arrow->next) {
1357         int sx, sy, ex, ey;
1358         
1359         if (this_arrow->arrow_properties.layer != layer)
1360             continue;
1361         get_arrow(this_arrow, &sx, &sy, &ex, &ey);
1362
1363         term_apply_lp_properties(&(this_arrow->arrow_properties.lp_properties));
1364         apply_head_properties(&(this_arrow->arrow_properties));
1365         draw_clip_arrow(sx, sy, ex, ey, this_arrow->arrow_properties.head);
1366     }
1367     term_apply_lp_properties(&border_lp);
1368     clip_area = clip_save;
1369 }
1370
1371 static void
1372 place_labels(struct text_label *listhead, int layer, TBOOLEAN clip)
1373 {
1374     struct text_label *this_label;
1375     int x, y;
1376
1377     if (term->pointsize)
1378         (*term->pointsize)(pointsize);
1379
1380     for (this_label = listhead; this_label != NULL; this_label = this_label->next) {
1381
1382         if (this_label->layer != layer)
1383             continue;
1384
1385         if (layer == LAYER_PLOTLABELS) {
1386             x = map_x(this_label->place.x);
1387             y = map_y(this_label->place.y);
1388         } else
1389             map_position(&this_label->place, &x, &y, "label");
1390
1391         if (clip) {
1392             if (this_label->place.scalex == first_axes)
1393                 if (!(inrange(this_label->place.x, axis_array[FIRST_X_AXIS].min, axis_array[FIRST_X_AXIS].max)))
1394                     continue;
1395             if (this_label->place.scalex == second_axes)
1396                 if (!(inrange(this_label->place.x, axis_array[SECOND_X_AXIS].min, axis_array[SECOND_X_AXIS].max)))
1397                     continue;
1398             if (this_label->place.scaley == first_axes)
1399                 if (!(inrange(this_label->place.y, axis_array[FIRST_Y_AXIS].min, axis_array[FIRST_Y_AXIS].max)))
1400                     continue;
1401             if (this_label->place.scaley == second_axes)
1402                 if (!(inrange(this_label->place.y, axis_array[SECOND_Y_AXIS].min, axis_array[SECOND_Y_AXIS].max)))
1403                     continue;
1404
1405         }
1406
1407         write_label(x, y, this_label);
1408     }
1409 }
1410
1411 #ifdef EAM_OBJECTS
1412 void
1413 place_rectangles(struct object *listhead, int layer, int dimensions, BoundingBox *clip_area)
1414 {
1415     t_object *this_object;
1416     t_rectangle *this_rect;
1417     double x1, y1, x2, y2;
1418     int x, y;
1419     unsigned int w, h;
1420     int style;
1421
1422     for (this_object = listhead; this_object != NULL; this_object = this_object->next) {
1423         struct lp_style_type lpstyle;
1424         struct fill_style_type *fillstyle;
1425         TBOOLEAN clip_x = FALSE;
1426         TBOOLEAN clip_y = FALSE;
1427     
1428         if (this_object->object_type == OBJ_RECTANGLE)
1429             this_rect = &this_object->o.rectangle;
1430         else
1431             continue;
1432
1433         if (this_object->layer != layer)
1434             continue;
1435
1436         if (this_rect->type == 1) {
1437             double width, height;
1438
1439             if (dimensions == 2 || this_rect->center.scalex == screen) {
1440                 map_position_double(&this_rect->center, &x1, &y1, "rect");
1441                 map_position_r(&this_rect->extent, &width, &height, "rect");
1442             } else if (splot_map) {
1443                 int junkw, junkh;
1444                 map3d_position_double(&this_rect->center, &x1, &y1, "rect");
1445                 map3d_position_r(&this_rect->extent, &junkw, &junkh, "rect");
1446                 width = junkw;
1447                 height = junkh;
1448             } else
1449                 continue;
1450
1451             x1 -= width/2;
1452             y1 -= height/2;
1453             x2 = x1 + width;
1454             y2 = y1 + height;
1455             w = width;
1456             h = height;
1457             if (this_rect->extent.scalex == first_axes
1458             ||  this_rect->extent.scalex == second_axes)
1459                 clip_x = TRUE;
1460             if (this_rect->extent.scaley == first_axes
1461             ||  this_rect->extent.scaley == second_axes)
1462                 clip_y = TRUE;
1463
1464         } else {
1465             if ((dimensions == 2)
1466             ||  (this_rect->bl.scalex == screen && this_rect->tr.scalex == screen)) {
1467                 map_position_double(&this_rect->bl, &x1, &y1, "rect");
1468                 map_position_double(&this_rect->tr, &x2, &y2, "rect");
1469             } else if (splot_map) {
1470                 map3d_position_double(&this_rect->bl, &x1, &y1, "rect");
1471                 map3d_position_double(&this_rect->tr, &x2, &y2, "rect");
1472             } else
1473                 continue;
1474
1475             if (x1 > x2) {double t=x1; x1=x2; x2=t;}
1476             if (y1 > y2) {double t=y1; y1=y2; y2=t;}
1477             if (this_rect->bl.scalex == first_axes
1478             ||  this_rect->bl.scalex == second_axes)
1479                 clip_x = TRUE;
1480             if (this_rect->bl.scaley == first_axes
1481             ||  this_rect->bl.scaley == second_axes)
1482                 clip_y = TRUE;
1483         }
1484
1485         /* FIXME - Should there be a generic clip_rectangle() routine?  */
1486         /* Clip to the graph boundaries, but only if the rectangle      */
1487         /* itself was specified in plot coords.                         */
1488         if (clip_area) {
1489             if (clip_x && x1 < clip_area->xleft)
1490                 x1 = clip_area->xleft;
1491             if (clip_x && x2 > clip_area->xright)
1492                 x2 = clip_area->xright;
1493             if (clip_y && y1 < clip_area->ybot)
1494                 y1 = clip_area->ybot;
1495             if (clip_y && y2 > clip_area->ytop)
1496                 y2 = clip_area->ytop;
1497             if (x1 > x2 || y1 > y2)
1498                 continue;
1499         }
1500
1501         w = x2 - x1;
1502         h = y2 - y1;
1503         x = x1;
1504         y = y1;
1505
1506         if (w == 0 || h == 0)
1507             continue;
1508
1509         if (this_object->lp_properties.l_type == LT_DEFAULT)
1510             lpstyle = default_rectangle.lp_properties;
1511         else
1512             lpstyle = this_object->lp_properties;
1513         if (lpstyle.l_width > 0)
1514             lpstyle.l_width = this_object->lp_properties.l_width;
1515         
1516         if (this_object->fillstyle.fillstyle == FS_DEFAULT)
1517             fillstyle = &default_rectangle.fillstyle;
1518         else
1519             fillstyle = &this_object->fillstyle;
1520
1521         term_apply_lp_properties(&lpstyle);
1522         style = style_from_fill(fillstyle);
1523
1524         if (style != FS_EMPTY) {
1525             if (lpstyle.use_palette && term->filled_polygon) {
1526                 (*term->filled_polygon)(4, fill_corners(style,x,y,w,h));
1527             } else if (term->fillbox)
1528                 (*term->fillbox) (style, x, y, w, h);
1529         }
1530
1531         if (fillstyle->border_linetype != LT_NODRAW
1532         &&  fillstyle->border_linetype != LT_UNDEFINED) {
1533             (*term->linetype)(fillstyle->border_linetype);
1534             (*term->move)   (x, y);
1535             (*term->vector) (x, y+h);
1536             (*term->vector) (x+w, y+h);
1537             (*term->vector) (x+w, y);
1538             (*term->vector) (x, y);
1539         }
1540     }
1541 }
1542 #endif
1543
1544 void
1545 do_plot(struct curve_points *plots, int pcount)
1546 {
1547     struct termentry *t = term;
1548     int curve;
1549     struct curve_points *this_plot = NULL;
1550     int xl = 0, yl = 0; /* avoid gcc -Wall warning */
1551     int key_count = 0;
1552     legend_key *key = &keyT;
1553
1554     x_axis = FIRST_X_AXIS;
1555     y_axis = FIRST_Y_AXIS;
1556
1557     /* Apply the desired viewport offsets. */
1558     if (Y_AXIS.min < Y_AXIS.max) {
1559         Y_AXIS.min -= boff;
1560         Y_AXIS.max += toff;
1561     } else {
1562         Y_AXIS.max -= boff;
1563         Y_AXIS.min += toff;
1564     }
1565     if (X_AXIS.min < X_AXIS.max) {
1566         X_AXIS.min -= loff;
1567         X_AXIS.max += roff;
1568     } else {
1569         X_AXIS.max -= loff;
1570         X_AXIS.min += roff;
1571     }
1572
1573     /*
1574      * In the beginning, this "empty range" test was for exact
1575      * equality, eg y_min == y_max , but that caused an infinite loop
1576      * once.  Then the test was changed to check for being within the
1577      * 'zero' threshold, fabs(y_max - y_min) < zero) , but that
1578      * prevented plotting data with ranges below 'zero'.  Now it's an
1579      * absolute equality test again, since
1580      * axis_checked_extend_empty_range() should have widened empty
1581      * ranges before we get here.  */
1582     if (X_AXIS.min == X_AXIS.max)
1583         int_error(NO_CARET, "x_min should not equal x_max!");
1584     if (Y_AXIS.min == Y_AXIS.max)
1585         int_error(NO_CARET, "y_min should not equal y_max!");
1586
1587     /* EAM June 2003 - Although the comment below implies that font dimensions
1588      * are known after term_initialise(), this is not true at least for the X11
1589      * driver.  X11 fonts are not set until an actual display window is
1590      * opened, and that happens in term->graphics(), which is called from
1591      * term_start_plot().
1592      */
1593     term_initialise();          /* may set xmax/ymax */
1594     term_start_plot();
1595
1596     /* compute boundary for plot (plot_bounds.xleft, plot_bounds.xright, plot_bounds.ytop, plot_bounds.ybot)
1597      * also calculates tics, since xtics depend on plot_bounds.xleft
1598      * but plot_bounds.xleft depends on ytics. Boundary calculations depend
1599      * on term->v_char etc, so terminal must be initialised first.
1600      */
1601     boundary(plots, pcount);
1602
1603     /* Make palette */
1604     if (is_plot_with_palette())
1605         make_palette();
1606
1607     /* Give a chance for rectangles to be behind everything else */
1608     place_rectangles(first_object, -1, 2, NULL);
1609
1610     screen_ok = FALSE;
1611
1612     /* Sync point for epslatex text positioning */
1613     if (term->layer)
1614         (term->layer)(TERM_LAYER_BACKTEXT);
1615
1616     /* DRAW TICS AND GRID */
1617     if (grid_layer == 0 || grid_layer == -1)
1618         place_grid();
1619
1620     /* DRAW AXES */
1621     /* after grid so that axes linetypes are on top */
1622     x_axis = FIRST_X_AXIS;
1623     y_axis = FIRST_Y_AXIS;      /* chose scaling */
1624
1625     axis_draw_2d_zeroaxis(FIRST_X_AXIS,FIRST_Y_AXIS);
1626     axis_draw_2d_zeroaxis(FIRST_Y_AXIS,FIRST_X_AXIS);
1627
1628     x_axis = SECOND_X_AXIS;
1629     y_axis = SECOND_Y_AXIS;     /* chose scaling */
1630
1631     axis_draw_2d_zeroaxis(SECOND_X_AXIS,SECOND_Y_AXIS);
1632     axis_draw_2d_zeroaxis(SECOND_Y_AXIS,SECOND_X_AXIS);
1633
1634     /* DRAW PLOT BORDER */
1635     if (draw_border)
1636         plot_border();
1637
1638     /* YLABEL */
1639     if (axis_array[FIRST_Y_AXIS].label.text) {
1640         ignore_enhanced(axis_array[FIRST_Y_AXIS].label.noenhanced);
1641         apply_pm3dcolor(&(axis_array[FIRST_Y_AXIS].label.textcolor),t);
1642         /* we worked out x-posn in boundary() */
1643         if ((*t->text_angle) (axis_array[FIRST_Y_AXIS].label.rotate)) {
1644             double tmpx, tmpy;
1645             unsigned int x, y;
1646             map_position_r(&(axis_array[FIRST_Y_AXIS].label.offset),
1647                            &tmpx, &tmpy, "doplot");
1648
1649             x = ylabel_x + (t->v_char / 2);
1650             y = (plot_bounds.ytop + plot_bounds.ybot) / 2 + tmpy;
1651
1652             write_multiline(x, y, axis_array[FIRST_Y_AXIS].label.text,
1653                             CENTRE, JUST_TOP, axis_array[FIRST_Y_AXIS].label.rotate,
1654                             axis_array[FIRST_Y_AXIS].label.font);
1655             (*t->text_angle) (0);
1656         } else {
1657             /* really bottom just, but we know number of lines
1658                so we need to adjust x-posn by one line */
1659             unsigned int x = ylabel_x;
1660             unsigned int y = ylabel_y;
1661
1662             write_multiline(x, y, axis_array[FIRST_Y_AXIS].label.text,
1663                             LEFT, JUST_TOP, 0,
1664                             axis_array[FIRST_Y_AXIS].label.font);
1665         }
1666         reset_textcolor(&(axis_array[FIRST_Y_AXIS].label.textcolor),t);
1667         ignore_enhanced(FALSE);
1668     }
1669
1670     /* Y2LABEL */
1671     if (axis_array[SECOND_Y_AXIS].label.text) {
1672         ignore_enhanced(axis_array[SECOND_Y_AXIS].label.noenhanced);
1673         apply_pm3dcolor(&(axis_array[SECOND_Y_AXIS].label.textcolor),t);
1674         /* we worked out coordinates in boundary() */
1675         if ((*t->text_angle) (axis_array[SECOND_Y_AXIS].label.rotate)) {
1676             double tmpx, tmpy;
1677             unsigned int x, y;
1678             map_position_r(&(axis_array[SECOND_Y_AXIS].label.offset),
1679                            &tmpx, &tmpy, "doplot");
1680             x = y2label_x + (t->v_char / 2) - 1;
1681             y = (plot_bounds.ytop + plot_bounds.ybot) / 2 + tmpy;
1682
1683             write_multiline(x, y, axis_array[SECOND_Y_AXIS].label.text,
1684                             CENTRE, JUST_TOP, 
1685                             axis_array[SECOND_Y_AXIS].label.rotate,
1686                             axis_array[SECOND_Y_AXIS].label.font);
1687             (*t->text_angle) (0);
1688         } else {
1689             /* really bottom just, but we know number of lines */
1690             unsigned int x = y2label_x;
1691             unsigned int y = y2label_y;
1692
1693             write_multiline(x, y, axis_array[SECOND_Y_AXIS].label.text,
1694                             RIGHT, JUST_TOP, 0,
1695                             axis_array[SECOND_Y_AXIS].label.font);
1696         }
1697         reset_textcolor(&(axis_array[SECOND_Y_AXIS].label.textcolor),t);
1698         ignore_enhanced(FALSE);
1699     }
1700
1701     /* XLABEL */
1702     if (axis_array[FIRST_X_AXIS].label.text) {
1703         double tmpx, tmpy;
1704         unsigned int x, y;
1705         map_position_r(&(axis_array[FIRST_X_AXIS].label.offset),
1706                        &tmpx, &tmpy, "doplot");
1707
1708         x = (plot_bounds.xright + plot_bounds.xleft) / 2 +  tmpx;
1709         y = xlabel_y - t->v_char / 2;   /* HBB */
1710
1711         ignore_enhanced(axis_array[FIRST_X_AXIS].label.noenhanced);
1712         apply_pm3dcolor(&(axis_array[FIRST_X_AXIS].label.textcolor), t);
1713         write_multiline(x, y, axis_array[FIRST_X_AXIS].label.text,
1714                         JUST_CENTRE, JUST_TOP, 0,
1715                         axis_array[FIRST_X_AXIS].label.font);
1716         reset_textcolor(&(axis_array[FIRST_X_AXIS].label.textcolor), t);
1717         ignore_enhanced(FALSE);
1718     }
1719
1720     /* PLACE TITLE */
1721     if (title.text) {
1722         double tmpx, tmpy;
1723         unsigned int x, y;
1724         map_position_r(&(title.offset), &tmpx, &tmpy, "doplot");
1725         /* we worked out y-coordinate in boundary() */
1726         x = (plot_bounds.xleft + plot_bounds.xright) / 2 + tmpx;
1727         y = title_y - t->v_char / 2;
1728
1729         ignore_enhanced(title.noenhanced);
1730         apply_pm3dcolor(&(title.textcolor), t);
1731         write_multiline(x, y, title.text, CENTRE, JUST_TOP, 0, title.font);
1732         reset_textcolor(&(title.textcolor), t);
1733         ignore_enhanced(FALSE);
1734     }
1735
1736     /* X2LABEL */
1737     if (axis_array[SECOND_X_AXIS].label.text) {
1738         double tmpx, tmpy;
1739         unsigned int x, y;
1740         map_position_r(&(axis_array[SECOND_X_AXIS].label.offset),
1741                        &tmpx, &tmpy, "doplot");
1742         /* we worked out y-coordinate in boundary() */
1743         x = (plot_bounds.xright + plot_bounds.xleft) / 2 + tmpx;
1744         y = x2label_y - t->v_char / 2 - 1;
1745         ignore_enhanced(axis_array[SECOND_X_AXIS].label.noenhanced);
1746         apply_pm3dcolor(&(axis_array[SECOND_X_AXIS].label.textcolor),t);
1747         write_multiline(x, y, axis_array[SECOND_X_AXIS].label.text, CENTRE,
1748                         JUST_TOP, 0, axis_array[SECOND_X_AXIS].label.font);
1749         reset_textcolor(&(axis_array[SECOND_X_AXIS].label.textcolor),t);
1750         ignore_enhanced(FALSE);
1751     }
1752
1753     /* PLACE TIMEDATE */
1754     if (timelabel.text) {
1755         /* we worked out coordinates in boundary() */
1756         char *str;
1757         time_t now;
1758         unsigned int x = time_x;
1759         unsigned int y = time_y;
1760         time(&now);
1761         /* there is probably no way to find out in advance how many
1762          * chars strftime() writes */
1763         str = gp_alloc(MAX_LINE_LEN + 1, "timelabel.text");
1764         strftime(str, MAX_LINE_LEN, timelabel.text, localtime(&now));
1765
1766         if (timelabel_rotate && (*t->text_angle) (TEXT_VERTICAL)) {
1767             x += t->v_char / 2; /* HBB */
1768             if (timelabel_bottom)
1769                 write_multiline(x, y, str, LEFT, JUST_TOP, TEXT_VERTICAL, timelabel.font);
1770             else
1771                 write_multiline(x, y, str, RIGHT, JUST_TOP, TEXT_VERTICAL, timelabel.font);
1772             (*t->text_angle) (0);
1773         } else {
1774             y -= t->v_char / 2; /* HBB */
1775             if (timelabel_bottom)
1776                 write_multiline(x, y, str, LEFT, JUST_BOT, 0, timelabel.font);
1777             else
1778                 write_multiline(x, y, str, LEFT, JUST_TOP, 0, timelabel.font);
1779         }
1780         free(str);
1781     }
1782
1783     /* Add back colorbox if appropriate */
1784     if (is_plot_with_palette() && is_plot_with_colorbox() && term->set_color
1785         && color_box.layer == LAYER_BACK)
1786             draw_color_smooth_box(MODE_PLOT);
1787
1788     /* And rectangles */
1789     place_rectangles( first_object, 0, 2, clip_area );
1790
1791     /* PLACE LABELS */
1792     place_labels( first_label, 0, FALSE );
1793
1794     /* PLACE ARROWS */
1795     place_arrows( 0 );
1796
1797     /* Sync point for epslatex text positioning */
1798     if (term->layer)
1799         (term->layer)(TERM_LAYER_FRONTTEXT);
1800
1801     /* WORK OUT KEY SETTINGS AND DO KEY TITLE / BOX */
1802     if (lkey) {                 /* may have been cancelled if something went wrong */
1803         /* just use keybox.xl etc worked out in boundary() */
1804         xl = keybox.xl + key_size_left;
1805         yl = keybox.yt;
1806
1807         if (*key->title) {
1808             int center = (keybox.xl + keybox.xr) / 2;
1809             double extra_height = 0.0;
1810
1811             if ((t->flags & TERM_ENHANCED_TEXT) && strchr(key->title,'^'))
1812                 extra_height += 0.51;
1813             write_multiline(center, yl - (0.5 + extra_height/2.0) * t->v_char,
1814                             key->title, CENTRE, JUST_TOP, 0, NULL);
1815             if ((t->flags & TERM_ENHANCED_TEXT) && strchr(key->title,'_'))
1816                 extra_height += 0.3;
1817             ktitl_lines += extra_height;
1818             keybox.yb -= extra_height * t->v_char;
1819             yl -= t->v_char * ktitl_lines;
1820         }
1821
1822         yl -= (int)(0.5 * key->height_fix * t->v_char);
1823         yl_ref = yl -= key_entry_height / 2;    /* centralise the keys */
1824         key_count = 0;
1825
1826         if (key->box.l_type > LT_NODRAW) {
1827             BoundingBox *clip_save = clip_area;
1828             if (term->flags & TERM_CAN_CLIP)
1829                 clip_area = NULL;
1830             else
1831                 clip_area = &canvas;
1832             term_apply_lp_properties(&key->box);
1833             newpath();
1834             draw_clip_line(keybox.xl, keybox.yb, keybox.xl, keybox.yt);
1835             draw_clip_line(keybox.xl, keybox.yt, keybox.xr, keybox.yt);
1836             draw_clip_line(keybox.xr, keybox.yt, keybox.xr, keybox.yb);
1837             draw_clip_line(keybox.xr, keybox.yb, keybox.xl, keybox.yb);
1838             closepath();
1839             /* draw a horizontal line between key title and first entry */
1840             draw_clip_line(keybox.xl, keybox.yt - (ktitl_lines) * t->v_char,
1841                            keybox.xr, keybox.yt - (ktitl_lines) * t->v_char);
1842             clip_area = clip_save;
1843         }
1844     } /* lkey */
1845
1846     /* DRAW CURVES */
1847     this_plot = plots;
1848     for (curve = 0; curve < pcount; this_plot = this_plot->next, curve++) {
1849         TBOOLEAN localkey = lkey;       /* a local copy */
1850
1851         /* set scaling for this plot's axes */
1852         x_axis = this_plot->x_axis;
1853         y_axis = this_plot->y_axis;
1854
1855         term_apply_lp_properties(&(this_plot->lp_properties));
1856
1857 #ifdef EAM_HISTOGRAMS
1858         /* Why only for histograms? */
1859         if (this_plot->plot_style == HISTOGRAMS ) {
1860             if (prefer_line_styles)
1861                 lp_use_properties(&this_plot->lp_properties,
1862                         this_plot->lp_properties.l_type+1, FALSE);
1863         }
1864
1865         /* Skip a line in the key between histogram clusters */
1866         if (this_plot->plot_style == HISTOGRAMS
1867         &&  this_plot->histogram_sequence == 0 && yl != yl_ref) {
1868             if (++key_count >= key_rows) {
1869                 yl = yl_ref;
1870                 xl += key_col_wth;
1871                 key_count = 0;
1872             } else
1873                 yl = yl - key_entry_height;
1874         }
1875
1876         /* Column-stacked histograms store their key titles internally */
1877         if (this_plot->plot_style == HISTOGRAMS
1878         &&  histogram_opts.type == HT_STACKED_IN_TOWERS) {
1879             text_label *key_entry;
1880             localkey = 0;
1881             if (this_plot->labels) {
1882                 struct lp_style_type save_lp = this_plot->lp_properties;
1883                 for (key_entry = this_plot->labels->next; key_entry; key_entry = key_entry->next) {
1884                     key_count++;
1885                     this_plot->lp_properties.l_type = key_entry->tag;
1886                     this_plot->fill_properties.fillpattern = key_entry->tag;
1887                     if (key_entry->text) {
1888                         if (prefer_line_styles)
1889                             lp_use_properties(&this_plot->lp_properties, key_entry->tag + 1, FALSE);
1890                         do_key_sample(this_plot, key, key_entry->text, t, xl, yl);
1891                     }
1892                     yl = yl - key_entry_height;
1893                 }
1894                 free_labels(this_plot->labels);
1895                 this_plot->labels = NULL;
1896                 this_plot->lp_properties = save_lp;
1897             }
1898         } else
1899 #endif
1900
1901         if (this_plot->title && !*this_plot->title) {
1902             localkey = FALSE;
1903         } else if (this_plot->plot_type == NODATA) {
1904             localkey = FALSE;
1905         } else {
1906             ignore_enhanced(this_plot->title_no_enhanced);
1907                 /* don't write filename or function enhanced */
1908             if (localkey && this_plot->title && !this_plot->title_is_suppressed) {
1909                 key_count++;
1910                 if (key->invert)
1911                     yl = keybox.yb + yl_ref + key_entry_height/2 - yl;
1912                 do_key_sample(this_plot, key, this_plot->title, t, xl, yl);
1913             }
1914             ignore_enhanced(FALSE);
1915         }
1916
1917         /* and now the curves, plus any special key requirements */
1918         /* be sure to draw all lines before drawing any points */
1919         /* Skip missing/empty curves */
1920         if (this_plot->plot_type != NODATA) {
1921
1922             switch (this_plot->plot_style) {
1923             case IMPULSES:
1924                 plot_impulses(this_plot, X_AXIS.term_zero, Y_AXIS.term_zero);
1925                 break;
1926             case LINES:
1927                 plot_lines(this_plot);
1928                 break;
1929             case STEPS:
1930                 plot_steps(this_plot);
1931                 break;
1932             case FSTEPS:
1933                 plot_fsteps(this_plot);
1934                 break;
1935             case HISTEPS:
1936                 plot_histeps(this_plot);
1937                 break;
1938             case POINTSTYLE:
1939                 plot_points(this_plot);
1940                 break;
1941             case LINESPOINTS:
1942                 plot_lines(this_plot);
1943                 plot_points(this_plot);
1944                 break;
1945             case DOTS:
1946                 if (localkey && this_plot->title && !this_plot->title_is_suppressed) {
1947                     if (on_page(xl + key_point_offset, yl))
1948                         (*t->point) (xl + key_point_offset, yl, -1);
1949                 }
1950                 plot_dots(this_plot);
1951                 break;
1952             case YERRORLINES:
1953             case XERRORLINES:
1954             case XYERRORLINES:
1955                 plot_lines(this_plot);
1956                 plot_bars(this_plot);
1957                 plot_points(this_plot);
1958                 break;
1959             case YERRORBARS:
1960             case XERRORBARS:
1961             case XYERRORBARS:
1962                 plot_bars(this_plot);
1963                 plot_points(this_plot);
1964                 break;
1965             case BOXXYERROR:
1966             case BOXES:
1967                 plot_boxes(this_plot, Y_AXIS.term_zero);
1968                 break;
1969
1970 #ifdef EAM_HISTOGRAMS
1971             case HISTOGRAMS:
1972                 /* Draw the bars first, so that the box will cover the bottom half */
1973                 if (histogram_opts.type == HT_ERRORBARS) {
1974                     (term->linewidth)(histogram_opts.bar_lw);
1975                     if (default_fillstyle.border_linetype > LT_NODRAW)
1976                         (term->linetype)(default_fillstyle.border_linetype);
1977                     else
1978                         (term->linetype)(this_plot->lp_properties.l_type);
1979                     plot_bars(this_plot);
1980                     term_apply_lp_properties(&(this_plot->lp_properties));
1981                 }
1982                 plot_boxes(this_plot, Y_AXIS.term_zero);
1983                 break;
1984 #endif
1985
1986             case BOXERROR:
1987                 plot_boxes(this_plot, Y_AXIS.term_zero);
1988                 plot_bars(this_plot);
1989                 break;
1990
1991             case FILLEDCURVES:
1992                 if (this_plot->filledcurves_options.closeto == FILLEDCURVES_BETWEEN)
1993                     plot_betweencurves(this_plot);
1994                 else {
1995                     plot_filledcurves(this_plot);
1996                     if (this_plot->fill_properties.border_linetype == LT_NODRAW)
1997                         break;
1998                     if (this_plot->fill_properties.border_linetype != LT_UNDEFINED)
1999                         (*t->linetype)(this_plot->fill_properties.border_linetype);
2000                     plot_lines(this_plot);
2001                 }
2002                 break;
2003
2004             case VECTOR:
2005                 plot_vectors(this_plot);
2006                 break;
2007             case FINANCEBARS:
2008                 plot_f_bars(this_plot);
2009                 break;
2010             case CANDLESTICKS:
2011                 plot_c_bars(this_plot);
2012                 break;
2013
2014             case PM3DSURFACE:
2015                 fprintf(stderr, "** warning: can't use pm3d for 2d plots -- please unset pm3d\n");
2016                 break;
2017
2018 #ifdef EAM_DATASTRINGS
2019             case LABELPOINTS:
2020                 place_labels( this_plot->labels->next, LAYER_PLOTLABELS, TRUE);
2021                 break;
2022 #endif
2023 #ifdef WITH_IMAGE
2024             case IMAGE:
2025                 PLOT_IMAGE(this_plot, IC_PALETTE);
2026                 break;
2027
2028             case RGBIMAGE:
2029                 PLOT_IMAGE(this_plot, IC_RGB);
2030                 break;
2031 #endif
2032             }
2033         }
2034
2035
2036         if (localkey && this_plot->title && !this_plot->title_is_suppressed) {
2037             /* we deferred point sample until now */
2038             if (this_plot->plot_style & PLOT_STYLE_HAS_POINT) {
2039                 if (this_plot->lp_properties.p_size == PTSZ_VARIABLE)
2040                     (*t->pointsize)(pointsize);
2041                 if (on_page(xl + key_point_offset, yl))
2042                     (*t->point) (xl + key_point_offset, yl, this_plot->lp_properties.p_type);
2043             }
2044             if (key->invert)
2045                 yl = keybox.yb + yl_ref + key_entry_height/2 - yl;
2046             if (key_count >= key_rows) {
2047                 yl = yl_ref;
2048                 xl += key_col_wth;
2049                 key_count = 0;
2050             } else
2051                 yl = yl - key_entry_height;
2052         }
2053     }
2054
2055     /* DRAW TICS AND GRID */
2056     if (grid_layer == 1)
2057         place_grid();
2058
2059     /* REDRAW PLOT BORDER */
2060     if (draw_border && border_layer == 1)
2061         plot_border();
2062
2063     /* Add front colorbox if appropriate */
2064     if (is_plot_with_palette() && is_plot_with_colorbox() && term->set_color
2065         && color_box.layer == LAYER_FRONT)
2066             draw_color_smooth_box(MODE_PLOT);
2067
2068     /* And rectangles */
2069     place_rectangles( first_object, 1, 2, clip_area );
2070
2071     /* PLACE LABELS */
2072     place_labels( first_label, 1, FALSE );
2073
2074 #ifdef EAM_HISTOGRAMS
2075 /* PLACE HISTOGRAM TITLES */
2076     place_histogram_titles();
2077 #endif
2078
2079     /* PLACE ARROWS */
2080     place_arrows( 1 );
2081
2082     /* Release the palette if we have used one (PostScript only?) */
2083     if (is_plot_with_palette() && term->previous_palette)
2084         term->previous_palette();
2085
2086     term_end_plot();
2087 }
2088
2089
2090 /* plot_impulses:
2091  * Plot the curves in IMPULSES style
2092  */
2093
2094 static void
2095 plot_impulses(struct curve_points *plot, int yaxis_x, int xaxis_y)
2096 {
2097     int i;
2098     int x, y;
2099     struct termentry *t = term;
2100
2101     for (i = 0; i < plot->p_count; i++) {
2102         switch (plot->points[i].type) {
2103         case INRANGE:
2104             x = map_x(plot->points[i].x);
2105             y = map_y(plot->points[i].y);
2106             break;
2107         case OUTRANGE:
2108             if (!inrange(plot->points[i].x, X_AXIS.min, X_AXIS.max))
2109                 continue;
2110             {
2111                 double clipped_y = plot->points[i].y;
2112
2113                 x = map_x(plot->points[i].x);
2114                 cliptorange(clipped_y, Y_AXIS.min, Y_AXIS.max);
2115                 y = map_y(clipped_y);
2116
2117                 break;
2118             }
2119         default:                /* just a safety */
2120         case UNDEFINED:{
2121                 continue;
2122             }
2123         }
2124
2125         /* variable color read from data column */
2126         check_for_variable_color(plot, &plot->points[i]);
2127
2128         if (polar)
2129             (*t->move) (yaxis_x, xaxis_y);
2130         else
2131             (*t->move) (x, xaxis_y);
2132         (*t->vector) (x, y);
2133     }
2134
2135 }
2136
2137 /* plot_lines:
2138  * Plot the curves in LINES style
2139  */
2140 static void
2141 plot_lines(struct curve_points *plot)
2142 {
2143     int i;                      /* point index */
2144     int x, y;                   /* point in terminal coordinates */
2145     struct termentry *t = term;
2146     enum coord_type prev = UNDEFINED;   /* type of previous point */
2147     double ex, ey;              /* an edge point */
2148     double lx[2], ly[2];        /* two edge points */
2149
2150     for (i = 0; i < plot->p_count; i++) {
2151
2152         /* rgb variable  -  color read from data column */
2153         check_for_variable_color(plot, &plot->points[i]);
2154
2155         switch (plot->points[i].type) {
2156         case INRANGE:{
2157                 x = map_x(plot->points[i].x);
2158                 y = map_y(plot->points[i].y);
2159
2160                 if (prev == INRANGE) {
2161                     (*t->vector) (x, y);
2162                 } else if (prev == OUTRANGE) {
2163                     /* from outrange to inrange */
2164                     if (!clip_lines1) {
2165                         (*t->move) (x, y);
2166                     } else {
2167                         edge_intersect(plot->points, i, &ex, &ey);
2168                         (*t->move) (map_x(ex), map_y(ey));
2169                         (*t->vector) (x, y);
2170                     }
2171                 } else {        /* prev == UNDEFINED */
2172                     (*t->move) (x, y);
2173                     (*t->vector) (x, y);
2174                 }
2175
2176                 break;
2177             }
2178         case OUTRANGE:{
2179                 if (prev == INRANGE) {
2180                     /* from inrange to outrange */
2181                     if (clip_lines1) {
2182                         edge_intersect(plot->points, i, &ex, &ey);
2183                         (*t->vector) (map_x(ex), map_y(ey));
2184                     }
2185                 } else if (prev == OUTRANGE) {
2186                     /* from outrange to outrange */
2187                     if (clip_lines2) {
2188                         if (two_edge_intersect(plot->points, i, lx, ly)) {
2189                             (*t->move) (map_x(lx[0]), map_y(ly[0]));
2190                             (*t->vector) (map_x(lx[1]), map_y(ly[1]));
2191                         }
2192                     }
2193                 }
2194                 break;
2195             }
2196         default:                /* just a safety */
2197         case UNDEFINED:{
2198                 break;
2199             }
2200         }
2201         prev = plot->points[i].type;
2202     }
2203 }
2204
2205 /* plot_filledcurves:
2206  * Plot FILLED curves.
2207  * pm 8.9.2001 (main routine); pm 5.1.2002 (full support for options)
2208  */
2209
2210 /* finalize and draw the filled curve */
2211 static void
2212 finish_filled_curve(
2213     int points,
2214     gpiPoint *corners,
2215     struct curve_points *plot)
2216 {
2217     filledcurves_opts *filledcurves_options = &plot->filledcurves_options;
2218     long side = 0;
2219     int i;
2220
2221     if (points <= 0) return;
2222     /* add side (closing) points */
2223     switch (filledcurves_options->closeto) {
2224         case FILLEDCURVES_CLOSED:
2225                 break;
2226         case FILLEDCURVES_X1:
2227                 corners[points].x   = corners[points-1].x;
2228                 corners[points+1].x = corners[0].x;
2229                 corners[points].y   =
2230                 corners[points+1].y = axis_array[FIRST_Y_AXIS].term_lower;
2231                 points += 2;
2232                 break;
2233         case FILLEDCURVES_X2:
2234                 corners[points].x   = corners[points-1].x;
2235                 corners[points+1].x = corners[0].x;
2236                 corners[points].y   =
2237                 corners[points+1].y = axis_array[FIRST_Y_AXIS].term_upper;
2238                 points += 2;
2239                 break;
2240         case FILLEDCURVES_Y1:
2241                 corners[points].y   = corners[points-1].y;
2242                 corners[points+1].y = corners[0].y;
2243                 corners[points].x   =
2244                 corners[points+1].x = axis_array[FIRST_X_AXIS].term_lower;
2245                 points += 2;
2246                 break;
2247         case FILLEDCURVES_Y2:
2248                 corners[points].y   = corners[points-1].y;
2249                 corners[points+1].y = corners[0].y;
2250                 corners[points].x   =
2251                 corners[points+1].x = axis_array[FIRST_X_AXIS].term_upper;
2252                 points += 2;
2253                 break;
2254         case FILLEDCURVES_ATX1:
2255                 corners[points].x   =
2256                 corners[points+1].x = map_x(filledcurves_options->at);
2257                     /* should be mapping real x1axis/graph/screen => screen */
2258                 corners[points].y   = corners[points-1].y;
2259                 corners[points+1].y = corners[0].y;
2260                 for (i=0; i<points; i++)
2261                     side += corners[i].x - corners[points].x;
2262                 points += 2;
2263                 break;
2264         case FILLEDCURVES_ATX2:
2265                 corners[points].x   =
2266                 corners[points+1].x = map_x(filledcurves_options->at);
2267                     /* should be mapping real x2axis/graph/screen => screen */
2268                 corners[points].y   = corners[points-1].y;
2269                 corners[points+1].y = corners[0].y;
2270                 for (i=0; i<points; i++)
2271                     side += corners[i].x - corners[points].x;
2272                 points += 2;
2273                 break;
2274         case FILLEDCURVES_ATY1:
2275                 corners[points].y   =
2276                 corners[points+1].y = map_y(filledcurves_options->at);
2277                     /* should be mapping real y1axis/graph/screen => screen */
2278                 corners[points].x   = corners[points-1].x;
2279                 corners[points+1].x = corners[0].x;
2280                 for (i=0; i<points; i++)
2281                     side += corners[i].y - corners[points].y;
2282                 points += 2;
2283                 break;
2284         case FILLEDCURVES_ATY2:
2285                 corners[points].y   =
2286                 corners[points+1].y = map_y(filledcurves_options->at);
2287                     /* should be mapping real y2axis/graph/screen => screen */
2288                 corners[points].x   = corners[points-1].x;
2289                 corners[points+1].x = corners[0].x;
2290                 for (i=0; i<points; i++)
2291                     side += corners[i].y - corners[points].y;
2292                 points += 2;
2293                 break;
2294         case FILLEDCURVES_ATXY:
2295                 corners[points].x = map_x(filledcurves_options->at);
2296                     /* should be mapping real x1axis/graph/screen => screen */
2297                 corners[points].y = map_y(filledcurves_options->aty);
2298                     /* should be mapping real y1axis/graph/screen => screen */
2299                 points++;
2300                 break;
2301         case FILLEDCURVES_BETWEEN:
2302                 side = (corners[points].x > 0) ? 1 : -1;
2303                 break;
2304         default: /* the polygon is closed by default */
2305                 break;
2306     }
2307
2308 #if 0
2309     { /* for debugging purposes */
2310     int i;
2311     fprintf(stderr, "List of %i corners:\n", points);
2312     for (i=0; i<points; i++)
2313         fprintf(stderr, "%2i: %3i,%3i | ", i, corners[i].x, corners[i].y);
2314     fprintf(stderr, " side = %ld",side);
2315     fprintf(stderr, "\n");
2316     }
2317 #endif
2318
2319     /* Check for request to fill only on one side of a bounding line */
2320     if (filledcurves_options->oneside > 0 && side < 0)
2321         return;
2322     if (filledcurves_options->oneside < 0 && side > 0)
2323         return;
2324
2325     /* EAM Mar 2004 - Apply fill style to filled curves */
2326     corners->style = style_from_fill(&plot->fill_properties);
2327     term->filled_polygon(points, corners);
2328 }
2329
2330
2331 static void
2332 plot_filledcurves(struct curve_points *plot)
2333 {
2334     int i;                      /* point index */
2335     int x, y;                   /* point in terminal coordinates */
2336     struct termentry *t = term;
2337     enum coord_type prev = UNDEFINED;   /* type of previous point */
2338     double ex, ey;                      /* an edge point */
2339     double lx[2], ly[2];                /* two edge points */
2340     int points = 0;                     /* how many corners */
2341     static gpiPoint *corners = 0;       /* array of corners */
2342     static int corners_allocated = 0;   /* how many allocated */
2343
2344     /* This set of variables is for tracking closed curve fill areas */
2345     int exit_edge = 0;          /* Which edge did an OUTRANGE point exit via? */
2346     int reentry_edge = 0;       /* Where did it reenter? */
2347     int out_updown = 0;         /* And where has it been in the meantime? */
2348     int out_leftright = 0;
2349     int first_entry = 0;        /* If the start point of the curve was OUTRANGE */
2350
2351     if (!t->filled_polygon) { /* filled polygons are not available */
2352         plot_lines(plot);
2353         return;
2354     }
2355
2356     if (!plot->filledcurves_options.opt_given) {
2357         /* no explicitly given filledcurves option for the current plot =>
2358            use the default for data or function, respectively
2359         */
2360         if (plot->plot_type == DATA)
2361             memcpy(&plot->filledcurves_options, &filledcurves_opts_data, sizeof(filledcurves_opts));
2362         else
2363             memcpy(&plot->filledcurves_options, &filledcurves_opts_func, sizeof(filledcurves_opts));
2364     }
2365
2366     /* clip the "at" coordinate to the drawing area */
2367 #define MYNOMIN(x,ax) if (x<axis_array[ax].min) x=axis_array[ax].min;
2368 #define MYNOMAX(x,ax) if (x>axis_array[ax].max) x=axis_array[ax].max;
2369     /* FIXME HBB 20030127: replace by cliptorange()!? */
2370     switch (plot->filledcurves_options.closeto) {
2371         case FILLEDCURVES_ATX1:
2372             MYNOMIN(plot->filledcurves_options.at,FIRST_X_AXIS);
2373             MYNOMAX(plot->filledcurves_options.at,FIRST_X_AXIS);
2374             break;
2375         case FILLEDCURVES_ATX2:
2376             MYNOMIN(plot->filledcurves_options.at,SECOND_X_AXIS);
2377             MYNOMAX(plot->filledcurves_options.at,SECOND_X_AXIS);
2378             break;
2379         case FILLEDCURVES_ATY1:
2380             MYNOMIN(plot->filledcurves_options.at,FIRST_Y_AXIS);
2381             MYNOMAX(plot->filledcurves_options.at,FIRST_Y_AXIS);
2382             break;
2383         case FILLEDCURVES_ATY2:
2384             MYNOMIN(plot->filledcurves_options.at,SECOND_Y_AXIS);
2385             MYNOMAX(plot->filledcurves_options.at,SECOND_Y_AXIS);
2386             break;
2387         case FILLEDCURVES_ATXY:
2388             MYNOMIN(plot->filledcurves_options.at,FIRST_X_AXIS);
2389             MYNOMAX(plot->filledcurves_options.at,FIRST_X_AXIS);
2390             MYNOMIN(plot->filledcurves_options.aty,FIRST_Y_AXIS);
2391             MYNOMAX(plot->filledcurves_options.aty,FIRST_Y_AXIS);
2392             break;
2393     }
2394 #undef MYNOMIN
2395 #undef MYNOMAX
2396
2397     for (i = 0; i < plot->p_count; i++) {
2398         if (points+2 >= corners_allocated) { /* there are 2 side points */
2399             corners_allocated += 128; /* reallocate more corners */
2400             corners = gp_realloc( corners, corners_allocated*sizeof(gpiPoint), "corners for filledcurves");
2401         }
2402         switch (plot->points[i].type) {
2403         case INRANGE:{
2404                 x = map_x(plot->points[i].x);
2405                 y = map_y(plot->points[i].y);
2406
2407                 if (prev == INRANGE) {
2408                     /* Split this segment if it crosses a bounding line */
2409                     if (bound_intersect(plot->points, i, &ex, &ey,
2410                                         &plot->filledcurves_options)) {
2411                         corners[points].x = map_x(ex);
2412                         corners[points++].y = map_y(ey);
2413                         finish_filled_curve(points, corners, plot);
2414                         points = 0;
2415                         corners[points].x = map_x(ex);
2416                         corners[points++].y = map_y(ey);
2417                     }
2418                     /* vector(x,y) */
2419                     corners[points].x = x;
2420                     corners[points++].y = y;
2421                 } else if (prev == OUTRANGE) {
2422                     /* from outrange to inrange */
2423                     if (clip_fill) {  /* EAM concave bounding curves */
2424                         reentry_edge = edge_intersect(plot->points, i, &ex, &ey);
2425
2426                         if (!exit_edge)
2427                             /* Curve must have started outside the plot area */
2428                             first_entry = reentry_edge;
2429                         else if (reentry_edge != exit_edge)
2430                             /* Fill in dummy points at plot corners if the bounding curve */
2431                             /* went around the corner while out of range */
2432                             fill_missing_corners(corners, &points, 
2433                                 exit_edge, reentry_edge, out_updown, out_leftright);
2434
2435                         /* vector(map_x(ex),map_y(ey)); */
2436                         corners[points].x = map_x(ex);
2437                         corners[points++].y = map_y(ey);
2438                         /* vector(x,y); */
2439                         corners[points].x = x;
2440                         corners[points++].y = y;
2441
2442                     } else if (!clip_lines1) {
2443                         finish_filled_curve(points, corners, plot);
2444                         points = 0;
2445                         /* move(x,y) */
2446                         corners[points].x = x;
2447                         corners[points++].y = y;
2448
2449                     } else {
2450                         finish_filled_curve(points, corners, plot);
2451                         points = 0;
2452                         edge_intersect(plot->points, i, &ex, &ey);
2453                         /* move(map_x(ex),map_y(ey)); */
2454                         corners[points].x = map_x(ex);
2455                         corners[points++].y = map_y(ey);
2456                         /* vector(x,y); */
2457                         corners[points].x = x;
2458                         corners[points++].y = y;
2459                     }
2460                 } else {        /* prev == UNDEFINED */
2461                     finish_filled_curve(points, corners, plot);
2462                     points = 0;
2463                     /* move(x,y) */
2464                     corners[points].x = x;
2465                     corners[points++].y = y;
2466                     /* vector(x,y); */
2467                     corners[points].x = x;
2468                     corners[points++].y = y;
2469                 }
2470                 break;
2471             }
2472         case OUTRANGE:{
2473                 if (clip_fill) {
2474                     int where_was_I = clip_point(map_x(plot->points[i].x), map_y(plot->points[i].y));
2475                     if (where_was_I & (LEFT_EDGE|RIGHT_EDGE))
2476                         out_leftright = where_was_I & (LEFT_EDGE|RIGHT_EDGE);
2477                     if (where_was_I & (TOP_EDGE|BOTTOM_EDGE))
2478                         out_updown = where_was_I & (TOP_EDGE|BOTTOM_EDGE);
2479                 }
2480                 if (prev == INRANGE) {
2481                     /* from inrange to outrange */
2482                     if (clip_lines1 || clip_fill) {
2483                         exit_edge = edge_intersect(plot->points, i, &ex, &ey);
2484                         /* vector(map_x(ex),map_y(ey)); */
2485                         corners[points].x = map_x(ex);
2486                         corners[points++].y = map_y(ey);
2487                     }
2488                 } else if (prev == OUTRANGE) {
2489                     /* from outrange to outrange */
2490                     if (clip_fill) {
2491                         if (two_edge_intersect(plot->points, i, lx, ly)) {
2492                             coordinate temp;
2493
2494                             /* vector(map_x(lx[0]),map_y(ly[0])); */
2495                             corners[points].x = map_x(lx[0]);
2496                             corners[points++].y = map_y(ly[0]);
2497
2498                             /* Figure out which side we entered by */
2499                             temp.x = plot->points[i].x;
2500                             temp.y = plot->points[i].y;
2501                             plot->points[i].x = lx[1];
2502                             plot->points[i].y = ly[1];
2503                             reentry_edge = edge_intersect(plot->points, i, &ex, &ey);
2504                             plot->points[i].x = temp.x;
2505                             plot->points[i].y = temp.y;
2506
2507                             if (!exit_edge) {
2508                             /* Curve must have started outside the plot area */
2509                                 first_entry = reentry_edge;
2510                             } else if (reentry_edge != exit_edge) {
2511                                 fill_missing_corners(corners, &points, exit_edge, reentry_edge,
2512                                         out_updown, out_leftright);
2513                             }
2514                             /* vector(map_x(lx[1]),map_y(ly[1])); */
2515                             corners[points].x = map_x(lx[1]);
2516                             corners[points++].y = map_y(ly[1]);
2517
2518                             /* Figure out which side we left by */
2519                             temp.x = plot->points[i-1].x;
2520                             temp.y = plot->points[i-1].y;
2521                             plot->points[i-1].x = lx[0];
2522                             plot->points[i-1].y = ly[0];
2523                             exit_edge = edge_intersect(plot->points, i, &ex, &ey);
2524                             plot->points[i-1].x = temp.x;
2525                             plot->points[i-1].y = temp.y;
2526                         }
2527                     }
2528                     else if (clip_lines2) {
2529                         if (two_edge_intersect(plot->points, i, lx, ly)) {
2530                             finish_filled_curve(points, corners, plot);
2531                             points = 0;
2532                             /* move(map_x(lx[0]),map_y(ly[0])); */
2533                             corners[points].x = map_x(lx[0]);
2534                             corners[points++].y = map_y(ly[0]);
2535                             /* vector(map_x(lx[1]),map_y(ly[1])); */
2536                             corners[points].x = map_x(lx[1]);
2537                             corners[points++].y = map_y(ly[1]);
2538                         }
2539                     }
2540                 }
2541                 break;
2542             }
2543         case UNDEFINED:{
2544                 /* UNDEFINED flags a blank line in the input file. 
2545                  * Unfortunately, it can also mean that the point was undefined.
2546                  * Is there a clean way to detect or handle the latter case?
2547                  */
2548                 if (prev != UNDEFINED) {
2549                     if (first_entry && first_entry != exit_edge)
2550                         fill_missing_corners(corners, &points, 
2551                                 exit_edge, first_entry, out_updown, out_leftright);
2552                     finish_filled_curve(points, corners, plot);
2553                     points = 0;
2554                     exit_edge = reentry_edge = first_entry = 0;
2555                 }
2556                 break;
2557             }
2558         default:                /* just a safety */
2559                 break;
2560         }
2561         prev = plot->points[i].type;
2562     }
2563
2564     if (clip_fill) {   /* Did we finish cleanly, or is there an unresolved corner-crossing? */
2565         if (first_entry && first_entry != exit_edge) {
2566             fill_missing_corners(corners, &points, exit_edge, first_entry,
2567                 out_updown, out_leftright);
2568         }
2569     }
2570     
2571     finish_filled_curve(points, corners, plot);
2572 }
2573
2574 /*
2575  * When the bounding curve of a filled area passes through the plot box but
2576  * exits through a different edge than it entered by, in order to properly
2577  * fill the enclosed area we must add dummy points at the plot corners.
2578  */
2579 static void
2580 fill_missing_corners(gpiPoint *corners, int *points, int exit, int reentry, int updown, int leftright)
2581 {
2582     if ((exit | reentry) == (LEFT_EDGE | RIGHT_EDGE)) {
2583         corners[(*points)].x   = (exit & LEFT_EDGE)
2584                         ? map_x(X_AXIS.min) : map_x(X_AXIS.max);
2585         corners[(*points)++].y = (updown & TOP_EDGE)
2586                         ? map_y(Y_AXIS.max) : map_y(Y_AXIS.min);
2587         corners[(*points)].x   = (reentry & LEFT_EDGE)
2588                         ? map_x(X_AXIS.min) : map_x(X_AXIS.max);
2589         corners[(*points)++].y = (updown & TOP_EDGE)
2590                         ? map_y(Y_AXIS.max) : map_y(Y_AXIS.min);
2591     } else  if ((exit | reentry) == (BOTTOM_EDGE | TOP_EDGE)) {
2592         corners[(*points)].x   = (leftright & LEFT_EDGE)
2593                         ? map_x(X_AXIS.min) : map_x(X_AXIS.max);
2594         corners[(*points)++].y = (exit & TOP_EDGE)
2595                         ? map_y(Y_AXIS.max) : map_y(Y_AXIS.min);
2596         corners[(*points)].x   = (leftright & LEFT_EDGE)
2597                         ? map_x(X_AXIS.min) : map_x(X_AXIS.max);
2598         corners[(*points)++].y = (reentry & TOP_EDGE)
2599                         ? map_y(Y_AXIS.max) : map_y(Y_AXIS.min);
2600     } else {
2601         corners[(*points)].x   = (exit | reentry) & LEFT_EDGE
2602                         ? map_x(X_AXIS.min) : map_x(X_AXIS.max);
2603         corners[(*points)++].y = (exit | reentry) & TOP_EDGE
2604                         ? map_y(Y_AXIS.max) : map_y(Y_AXIS.min);
2605     }
2606 }
2607 /*
2608  * Fill the area between two curves
2609  */
2610 static void
2611 plot_betweencurves(struct curve_points *plot)
2612 {
2613     double x1, x2, yl1, yu1, yl2, yu2;
2614     double xmid, ymid;
2615     int i;
2616
2617     /* If terminal doesn't support filled polygons, approximate with bars */
2618     if (!term->filled_polygon) {
2619         plot_bars(plot);
2620         return;
2621     }
2622
2623     /*
2624      * Fill the region one quadrilateral at a time.
2625      * Check each interval to see if the curves cross.
2626      * If so, split the interval into two parts.
2627      */
2628     for (i = 0; i < plot->p_count-1; i++) {
2629
2630         /* FIXME: This isn't really testing for undefined points, it    */
2631         /* is looking for blank lines. We need to distinguish these.    */
2632         /* Anyhow, if there's a blank line then start a new fill area.  */
2633         if (plot->points[i].type == UNDEFINED
2634             || plot->points[i+1].type == UNDEFINED)
2635             continue;
2636
2637         x1  = plot->points[i].x;
2638         yl1 = plot->points[i].y;
2639         yu1 = plot->points[i].yhigh;
2640         x2  = plot->points[i+1].x;
2641         yl2 = plot->points[i+1].y;
2642         yu2 = plot->points[i+1].yhigh;
2643
2644         if ((yu1-yl1)*(yu2-yl2) < 0) {
2645             xmid = (x1*(yl2-yu2) + x2*(yu1-yl1))
2646                  / ((yu1-yl1) + (yl2-yu2));
2647             ymid = yu1 + (yu2-yu1)*(xmid-x1)/(x2-x1);
2648             fill_between(x1,yl1,yu1,xmid,ymid,ymid,plot);
2649             fill_between(xmid,ymid,ymid,x2,yl2,yu2,plot);
2650         } else
2651             fill_between(x1,yl1,yu1,x2,yl2,yu2,plot);
2652
2653     }
2654 }
2655
2656 static void
2657 fill_between(
2658 double x1, double yl1, double yu1, double x2, double yl2, double yu2,
2659 struct curve_points *plot)
2660 {
2661     double xmin, xmax, ymin, ymax, dx, dy1, dy2;
2662     int axis;
2663     int ic, iy;
2664     gpiPoint box[8];
2665     struct { double x,y; } corners[8];
2666
2667     /* Clip against x-axis range */
2668     /* It would be nice if we could trust xmin to be less than xmax */
2669         axis = plot->x_axis;
2670         xmin = GPMIN(axis_array[axis].min, axis_array[axis].max);
2671         xmax = GPMAX(axis_array[axis].min, axis_array[axis].max);
2672         if (!(inrange(x1, xmin, xmax)) && !(inrange(x2, xmin, xmax)))
2673             return;
2674         
2675     /* Clip end segments. It would be nice to use edge_intersect() here, */
2676     /* but as currently written it cannot handle the second curve.       */
2677         dx = x2 - x1;
2678         if (x1<xmin) {
2679             yl1 += (yl2-yl1) * (xmin - x1) / dx;
2680             yu1 += (yu2-yu1) * (xmin - x1) / dx;
2681             x1 = xmin;
2682         }
2683         if (x2>xmax) {
2684             yl2 += (yl2-yl1) * (xmax - x2) / dx;
2685             yu2 += (yu2-yu1) * (xmax - x2) / dx;
2686             x2 = xmax;
2687         }
2688
2689     /* Clip against y-axis range */
2690         axis = plot->y_axis;
2691         ymin = GPMIN(axis_array[axis].min, axis_array[axis].max);
2692         ymax = GPMAX(axis_array[axis].min, axis_array[axis].max);
2693         if (yl1<ymin && yu1<ymin && yl2<ymin && yu2<ymin)
2694             return;
2695         if (yl1>ymax && yu1>ymax && yl2>ymax && yu2>ymax)
2696             return;
2697
2698         ic = 0;
2699         corners[ic].x   = map_x(x1);
2700         corners[ic++].y = map_y(yl1);
2701         corners[ic].x   = map_x(x1);
2702         corners[ic++].y = map_y(yu1);
2703
2704 #define INTERPOLATE(Y1,Y2,YBOUND) do { \
2705         dy1 = YBOUND - Y1; \
2706         dy2 = YBOUND - Y2; \
2707         if (dy1 != dy2 && dy1*dy2 < 0) { \
2708             corners[ic].y = map_y(YBOUND); \
2709             corners[ic++].x = map_x(x1 + dx * dy1 / (dy1-dy2)); \
2710         } \
2711         } while (0)
2712
2713         INTERPOLATE( yu1, yu2, ymin );
2714         INTERPOLATE( yu1, yu2, ymax );
2715         
2716         corners[ic].x   = map_x(x2);
2717         corners[ic++].y = map_y(yu2);
2718         corners[ic].x   = map_x(x2);
2719         corners[ic++].y = map_y(yl2);
2720
2721         INTERPOLATE( yl1, yl2, ymin );
2722         INTERPOLATE( yl1, yl2, ymax );
2723
2724 #undef INTERPOLATE
2725
2726     /* Copy the polygon vertices into a gpiPoints structure */
2727         for (iy=0; iy<ic; iy++) {
2728             box[iy].x = corners[iy].x;
2729             cliptorange(corners[iy].y, map_y(ymin), map_y(ymax));
2730             box[iy].y = corners[iy].y;
2731         }
2732
2733     /* finish_filled_curve() will handle   */
2734     /* current fill style (stored in plot) */
2735     /* above/below (stored in box[ic].x)   */
2736         box[ic].x = ((yu1-yl1) + (yu2-yl2) < 0) ? 1 : 0;
2737         finish_filled_curve(ic, box, plot);
2738 }
2739
2740
2741 /* XXX - JG  */
2742 /* plot_steps:
2743  * Plot the curves in STEPS style
2744  */
2745 static void
2746 plot_steps(struct curve_points *plot)
2747 {
2748     int i;                      /* point index */
2749     int x, y;                   /* point in terminal coordinates */
2750     struct termentry *t = term;
2751     enum coord_type prev = UNDEFINED;   /* type of previous point */
2752     double ex, ey;              /* an edge point */
2753     double lx[2], ly[2];        /* two edge points */
2754     int yprev = 0;              /* previous point coordinates */
2755
2756     for (i = 0; i < plot->p_count; i++) {
2757         switch (plot->points[i].type) {
2758         case INRANGE:{
2759                 x = map_x(plot->points[i].x);
2760                 y = map_y(plot->points[i].y);
2761
2762                 if (prev == INRANGE) {
2763                     (*t->vector) (x, yprev);
2764                     (*t->vector) (x, y);
2765                 } else if (prev == OUTRANGE) {
2766                     /* from outrange to inrange */
2767                     if (!clip_lines1) {
2768                         (*t->move) (x, y);
2769                     } else {    /* find edge intersection */
2770                         edge_intersect_steps(plot->points, i, &ex, &ey);
2771                         (*t->move) (map_x(ex), map_y(ey));
2772                         (*t->vector) (x, map_y(ey));
2773                         (*t->vector) (x, y);
2774                     }
2775                 } else {        /* prev == UNDEFINED */
2776                     (*t->move) (x, y);
2777                     (*t->vector) (x, y);
2778                 }
2779                 yprev = y;
2780                 break;
2781             }
2782         case OUTRANGE:{
2783                 if (prev == INRANGE) {
2784                     /* from inrange to outrange */
2785                     if (clip_lines1) {
2786                         edge_intersect_steps(plot->points, i, &ex, &ey);
2787                         (*t->vector) (map_x(ex), yprev);
2788                         (*t->vector) (map_x(ex), map_y(ey));
2789                     }
2790                 } else if (prev == OUTRANGE) {
2791                     /* from outrange to outrange */
2792                     if (clip_lines2) {
2793                         if (two_edge_intersect_steps(plot->points, i, lx, ly)) {
2794                             (*t->move) (map_x(lx[0]), map_y(ly[0]));
2795                             (*t->vector) (map_x(lx[1]), map_y(ly[0]));
2796                             (*t->vector) (map_x(lx[1]), map_y(ly[1]));
2797                         }
2798                     }
2799                 }
2800                 break;
2801             }
2802         default:                /* just a safety */
2803         case UNDEFINED:{
2804                 break;
2805             }
2806         }
2807         prev = plot->points[i].type;
2808     }
2809 }
2810
2811 /* XXX - HOE  */
2812 /* plot_fsteps:
2813  * Plot the curves in STEPS style by step on forward yvalue
2814  */
2815 static void
2816 plot_fsteps(struct curve_points *plot)
2817 {
2818     int i;                      /* point index */
2819     int x, y;                   /* point in terminal coordinates */
2820     struct termentry *t = term;
2821     enum coord_type prev = UNDEFINED;   /* type of previous point */
2822     double ex, ey;              /* an edge point */
2823     double lx[2], ly[2];        /* two edge points */
2824     int xprev = 0;              /* previous point coordinates */
2825
2826     for (i = 0; i < plot->p_count; i++) {
2827         switch (plot->points[i].type) {
2828         case INRANGE:{
2829                 x = map_x(plot->points[i].x);
2830                 y = map_y(plot->points[i].y);
2831
2832                 if (prev == INRANGE) {
2833                     (*t->vector) (xprev, y);
2834                     (*t->vector) (x, y);
2835                 } else if (prev == OUTRANGE) {
2836                     /* from outrange to inrange */
2837                     if (!clip_lines1) {
2838                         (*t->move) (x, y);
2839                     } else {    /* find edge intersection */
2840                         edge_intersect_fsteps(plot->points, i, &ex, &ey);
2841                         (*t->move) (map_x(ex), map_y(ey));
2842                         (*t->vector) (map_x(ex), y);
2843                         (*t->vector) (x, y);
2844                     }
2845                 } else {        /* prev == UNDEFINED */
2846                     (*t->move) (x, y);
2847                     (*t->vector) (x, y);
2848                 }
2849                 xprev = x;
2850                 break;
2851             }
2852         case OUTRANGE:{
2853                 if (prev == INRANGE) {
2854                     /* from inrange to outrange */
2855                     if (clip_lines1) {
2856                         edge_intersect_fsteps(plot->points, i, &ex, &ey);
2857                         (*t->vector) (xprev, map_y(ey));
2858                         (*t->vector) (map_x(ex), map_y(ey));
2859                     }
2860                 } else if (prev == OUTRANGE) {
2861                     /* from outrange to outrange */
2862                     if (clip_lines2) {
2863                         if (two_edge_intersect_fsteps(plot->points, i, lx, ly)) {
2864                             (*t->move) (map_x(lx[0]), map_y(ly[0]));
2865                             (*t->vector) (map_x(lx[0]), map_y(ly[1]));
2866                             (*t->vector) (map_x(lx[1]), map_y(ly[1]));
2867                         }
2868                     }
2869                 }
2870                 break;
2871             }
2872         default:                /* just a safety */
2873         case UNDEFINED:{
2874                 break;
2875             }
2876         }
2877         prev = plot->points[i].type;
2878     }
2879 }
2880
2881 /* HBB 20010625: replaced homegrown bubblesort in plot_histeps() by
2882  * call of standard routine qsort(). Need to tell the compare function
2883  * about the plotted dataset via this file scope variable: */
2884 static struct curve_points *histeps_current_plot;
2885
2886 /* NOTE: I'd have made the comp.function 'static', but the HP-sUX gcc
2887  * bug seems to forbid that :-( */
2888 int
2889 histeps_compare(SORTFUNC_ARGS p1, SORTFUNC_ARGS p2)
2890 {
2891     double x1=histeps_current_plot->points[*(int *)p1].x;
2892     double x2=histeps_current_plot->points[*(int *)p2].x;
2893
2894     if (x1 < x2)
2895         return -1;
2896     else
2897         return (x1 > x2);
2898 }
2899
2900 /* CAC  */
2901 /* plot_histeps:
2902  * Plot the curves in HISTEPS style
2903  */
2904 static void
2905 plot_histeps(struct curve_points *plot)
2906 {
2907     int i;                      /* point index */
2908     int xl, yl;                 /* cursor position in terminal coordinates */
2909     struct termentry *t = term;
2910     double x, y, xn, yn;        /* point position */
2911     double y_null;              /* y coordinate of histogram baseline */
2912     int *gl, goodcount;         /* array to hold list of valid points */
2913
2914     /* preliminary count of points inside array */
2915     goodcount = 0;
2916     for (i = 0; i < plot->p_count; i++)
2917         if (plot->points[i].type == INRANGE || plot->points[i].type == OUTRANGE)
2918             ++goodcount;
2919     if (goodcount < 2)
2920         return;                 /* cannot plot less than 2 points */
2921
2922     gl = gp_alloc(goodcount * sizeof(int), "histeps valid point mapping");
2923
2924     /* fill gl array with indexes of valid (non-undefined) points.  */
2925     goodcount = 0;
2926     for (i = 0; i < plot->p_count; i++)
2927         if (plot->points[i].type == INRANGE || plot->points[i].type == OUTRANGE) {
2928             gl[goodcount] = i;
2929             ++goodcount;
2930         }
2931
2932     /* sort the data --- tell histeps_compare about the plot
2933      * datastructure to look at, then call qsort() */
2934     histeps_current_plot = plot;
2935     qsort(gl, goodcount, sizeof(*gl), histeps_compare);
2936     /* play it safe: invalidate the static pointer after usage */
2937     histeps_current_plot = NULL;
2938
2939     /* HBB 20010625: log y axis must treat 0.0 as -infinity. Define
2940      * the correct y position for the histogram's baseline once. It'll
2941      * be used twice (once for each endpoint of the histogram). */
2942     if (Y_AXIS.log)
2943         y_null = GPMIN(Y_AXIS.min, Y_AXIS.max);
2944     else
2945         y_null = 0.0;
2946
2947     x = (3.0 * plot->points[gl[0]].x - plot->points[gl[1]].x) / 2.0;
2948     y = y_null;
2949
2950     xl = map_x(x);
2951     yl = map_y(y);
2952     (*t->move) (xl, yl);
2953
2954     for (i = 0; i < goodcount - 1; i++) {       /* loop over all points except last  */
2955         yn = plot->points[gl[i]].y;
2956         xn = (plot->points[gl[i]].x + plot->points[gl[i + 1]].x) / 2.0;
2957         histeps_vertical(&xl, &yl, x, y, yn);
2958         histeps_horizontal(&xl, &yl, x, xn, yn);
2959
2960         x = xn;
2961         y = yn;
2962     }
2963
2964     yn = plot->points[gl[i]].y;
2965     xn = (3.0 * plot->points[gl[i]].x - plot->points[gl[i - 1]].x) / 2.0;
2966     histeps_vertical(&xl, &yl, x, y, yn);
2967     histeps_horizontal(&xl, &yl, x, xn, yn);
2968     histeps_vertical(&xl, &yl, xn, yn, y_null);
2969
2970     free(gl);
2971 }
2972
2973 /* CAC
2974  * Draw vertical line for the histeps routine.
2975  * Performs clipping.
2976  */
2977 /* HBB 20010214: renamed parameters. xl vs. x1 is just _too_ easy to
2978  * mis-read */
2979 static void
2980 histeps_vertical(
2981     int *cur_x, int *cur_y,     /* keeps track of "cursor" position */
2982     double x,
2983     double y1, double y2)       /* coordinates of vertical line */
2984 {
2985     struct termentry *t = term;
2986     int xm, y1m, y2m;
2987
2988     /* FIXME HBB 20010215: wouldn't it be simpler to call
2989      * draw_clip_line() instead? And in histeps_horizontal(), too, of
2990      * course? */
2991
2992     /* HBB 20010215: reversed axes need special treatment, here: */
2993     if (X_AXIS.min <= X_AXIS.max) {
2994         if ((x < X_AXIS.min) || (x > X_AXIS.max))
2995             return;
2996     } else {
2997         if ((x < X_AXIS.max) || (x > X_AXIS.min))
2998             return;
2999     }
3000
3001     if (Y_AXIS.min <= Y_AXIS.max) {
3002         if ((y1 < Y_AXIS.min && y2 < Y_AXIS.min)
3003             || (y1 > Y_AXIS.max && y2 > Y_AXIS.max))
3004             return;
3005         if (y1 < Y_AXIS.min)
3006             y1 = Y_AXIS.min;
3007         if (y1 > Y_AXIS.max)
3008             y1 = Y_AXIS.max;
3009         if (y2 < Y_AXIS.min)
3010             y2 = Y_AXIS.min;
3011         if (y2 > Y_AXIS.max)
3012             y2 = Y_AXIS.max;
3013     } else {
3014         if ((y1 < Y_AXIS.max && y2 < Y_AXIS.max)
3015             || (y1 > Y_AXIS.min && y2 > Y_AXIS.min))
3016             return;
3017
3018         if (y1 < Y_AXIS.max)
3019             y1 = Y_AXIS.max;
3020         if (y1 > Y_AXIS.min)
3021             y1 = Y_AXIS.min;
3022         if (y2 < Y_AXIS.max)
3023             y2 = Y_AXIS.max;
3024         if (y2 > Y_AXIS.min)
3025             y2 = Y_AXIS.min;
3026     }
3027     xm = map_x(x);
3028     y1m = map_y(y1);
3029     y2m = map_y(y2);
3030
3031     if (y1m != *cur_y || xm != *cur_x)
3032         (*t->move) (xm, y1m);
3033     (*t->vector) (xm, y2m);
3034     *cur_x = xm;
3035     *cur_y = y2m;
3036
3037     return;
3038 }
3039
3040 /* CAC
3041  * Draw horizontal line for the histeps routine.
3042  * Performs clipping.
3043  */
3044 static void
3045 histeps_horizontal(
3046     int *cur_x, int *cur_y,     /* keeps track of "cursor" position */
3047     double x1, double x2,
3048     double y)                   /* coordinates of vertical line */
3049 {
3050     struct termentry *t = term;
3051     int x1m, x2m, ym;
3052
3053     /* HBB 20010215: reversed axes need special treatment, here: */
3054
3055     if (Y_AXIS.min <= Y_AXIS.max) {
3056         if ((y < Y_AXIS.min) || (y > Y_AXIS.max))
3057             return;
3058     } else {
3059         if ((y < Y_AXIS.max) || (y > Y_AXIS.min))
3060             return;
3061     }
3062
3063     if (X_AXIS.min <= X_AXIS.max) {
3064         if ((x1 < X_AXIS.min && x2 < X_AXIS.min)
3065             || (x1 > X_AXIS.max && x2 > X_AXIS.max))
3066             return;
3067
3068         if (x1 < X_AXIS.min)
3069             x1 = X_AXIS.min;
3070         if (x1 > X_AXIS.max)
3071             x1 = X_AXIS.max;
3072         if (x2 < X_AXIS.min)
3073             x2 = X_AXIS.min;
3074         if (x2 > X_AXIS.max)
3075             x2 = X_AXIS.max;
3076     } else {
3077         if ((x1 < X_AXIS.max && x2 < X_AXIS.max)
3078             || (x1 > X_AXIS.min && x2 > X_AXIS.min))
3079             return;
3080
3081         if (x1 < X_AXIS.max)
3082             x1 = X_AXIS.max;
3083         if (x1 > X_AXIS.min)
3084             x1 = X_AXIS.min;
3085         if (x2 < X_AXIS.max)
3086             x2 = X_AXIS.max;
3087         if (x2 > X_AXIS.min)
3088             x2 = X_AXIS.min;
3089     }
3090     ym = map_y(y);
3091     x1m = map_x(x1);
3092     x2m = map_x(x2);
3093
3094     if (x1m != *cur_x || ym != *cur_y)
3095         (*t->move) (x1m, ym);
3096     (*t->vector) (x2m, ym);
3097     *cur_x = x2m;
3098     *cur_y = ym;
3099
3100     return;
3101 }
3102
3103
3104 /* plot_bars:
3105  * Plot the curves in ERRORBARS style
3106  *  we just plot the bars; the points are plotted in plot_points
3107  */
3108 static void
3109 plot_bars(struct curve_points *plot)
3110 {
3111     int i;                      /* point index */
3112     struct termentry *t = term;
3113     double x, y;                /* position of the bar */
3114     double ylow, yhigh;         /* the ends of the bars */
3115     double xlow, xhigh;
3116     double x1, y1, x2, y2, slope;       /* parameters for polar error bars */
3117     unsigned int xM, ylowM, yhighM;     /* the mapped version of above */
3118     unsigned int yM, xlowM, xhighM;
3119     TBOOLEAN low_inrange, high_inrange;
3120     int tic = ERRORBARTIC;
3121 #ifdef EAM_HISTOGRAMS
3122     double halfwidth = 0;               /* Used to calculate full box width */
3123 #endif
3124
3125     /* Limitation: no boxes with x errorbars */
3126
3127     if ((plot->plot_style == YERRORBARS)
3128         || (plot->plot_style == XYERRORBARS)
3129         || (plot->plot_style == BOXERROR)
3130         || (plot->plot_style == YERRORLINES)
3131         || (plot->plot_style == XYERRORLINES)
3132 #ifdef EAM_HISTOGRAMS
3133         || (plot->plot_style == HISTOGRAMS)
3134 #endif
3135         || (plot->plot_style == FILLEDCURVES) /* Only if term has no filled_polygon! */
3136         ) {
3137         /* Draw the vertical part of the bar */
3138         for (i = 0; i < plot->p_count; i++) {
3139             /* undefined points don't count */
3140             if (plot->points[i].type == UNDEFINED)
3141                 continue;
3142
3143             /* check to see if in xrange */
3144             x = plot->points[i].x;
3145 #ifdef EAM_HISTOGRAMS
3146             if (plot->plot_style == HISTOGRAMS) {
3147                 /* Shrink each cluster to fit within one unit along X axis,   */
3148                 /* centered about the integer representing the cluster number */
3149                 /* 'start' is reset to 0 at the top of eval_plots(), and then */
3150                 /* incremented if 'plot new histogram' is encountered.        */
3151                 int clustersize = plot->histogram->clustersize + histogram_opts.gap;
3152                 x  += (i-1) * (clustersize - 1) + plot->histogram_sequence;
3153                 x  += histogram_opts.gap/2;
3154                 x  /= clustersize;
3155                 x  += plot->histogram->start + 0.5;
3156                 /* Calculate width also */
3157                 halfwidth = (plot->points[i].xhigh - plot->points[i].xlow)
3158                           / (2. * clustersize);
3159             }
3160 #endif
3161             if (!inrange(x, X_AXIS.min, X_AXIS.max))
3162                 continue;
3163             xM = map_x(x);
3164
3165             /* check to see if in yrange */
3166             y = plot->points[i].y;
3167             if (!inrange(y, Y_AXIS.min, Y_AXIS.max))
3168                 continue;
3169             yM = map_y(y);
3170
3171             /* find low and high points of bar, and check yrange */
3172             yhigh = plot->points[i].yhigh;
3173             ylow = plot->points[i].ylow;
3174
3175             high_inrange = inrange(yhigh, Y_AXIS.min, Y_AXIS.max);
3176             low_inrange = inrange(ylow, Y_AXIS.min, Y_AXIS.max);
3177
3178             /* compute the plot position of yhigh */
3179             if (high_inrange)
3180                 yhighM = map_y(yhigh);
3181             else if (samesign(yhigh - Y_AXIS.max, Y_AXIS.max - Y_AXIS.min))
3182                 yhighM = map_y(Y_AXIS.max);
3183             else
3184                 yhighM = map_y(Y_AXIS.min);
3185
3186             /* compute the plot position of ylow */
3187             if (low_inrange)
3188                 ylowM = map_y(ylow);
3189             else if (samesign(ylow - Y_AXIS.max, Y_AXIS.max - Y_AXIS.min))
3190                 ylowM = map_y(Y_AXIS.max);
3191             else
3192                 ylowM = map_y(Y_AXIS.min);
3193
3194             if (!high_inrange && !low_inrange && ylowM == yhighM)
3195                 /* both out of range on the same side */
3196                 continue;
3197
3198             /* find low and high points of bar, and check xrange */
3199             xhigh = plot->points[i].xhigh;
3200             xlow = plot->points[i].xlow;
3201
3202 #ifdef EAM_HISTOGRAMS
3203             if (plot->plot_style == HISTOGRAMS) {
3204                 xlowM = map_x(x-halfwidth);
3205                 xhighM = map_x(x+halfwidth);
3206             } else {
3207 #endif
3208             high_inrange = inrange(xhigh, X_AXIS.min, X_AXIS.max);
3209             low_inrange = inrange(xlow, X_AXIS.min, X_AXIS.max);
3210
3211             /* compute the plot position of xhigh */
3212             if (high_inrange)
3213                 xhighM = map_x(xhigh);
3214             else if (samesign(xhigh - X_AXIS.max, X_AXIS.max - X_AXIS.min))
3215                 xhighM = map_x(X_AXIS.max);
3216             else
3217                 xhighM = map_x(X_AXIS.min);
3218
3219             /* compute the plot position of xlow */
3220             if (low_inrange)
3221                 xlowM = map_x(xlow);
3222             else if (samesign(xlow - X_AXIS.max, X_AXIS.max - X_AXIS.min))
3223                 xlowM = map_x(X_AXIS.max);
3224             else
3225                 xlowM = map_x(X_AXIS.min);
3226
3227             if (!high_inrange && !low_inrange && xlowM == xhighM)
3228                 /* both out of range on the same side */
3229                 continue;
3230 #ifdef EAM_HISTOGRAMS
3231             }
3232 #endif
3233
3234             /* by here everything has been mapped */
3235             if (!polar) {
3236                 /* HBB 981130: use Igor's routine *only* for polar errorbars */
3237                 (*t->move) (xM, ylowM);
3238                 /* draw the main bar */
3239                 (*t->vector) (xM, yhighM);
3240                 if (bar_size < 0.0) {
3241                     /* draw the bottom tic same width as box */
3242                     (*t->move) ((unsigned int) (xlowM), ylowM);
3243                     (*t->vector) ((unsigned int) (xhighM), ylowM);
3244                     /* draw the top tic same width as box */
3245                     (*t->move) ((unsigned int) (xlowM), yhighM);
3246                     (*t->vector) ((unsigned int) (xhighM), yhighM);
3247                 } else if (bar_size > 0.0) {
3248                     /* draw the bottom tic */
3249                     (*t->move) ((unsigned int) (xM - bar_size * tic), ylowM);
3250                     (*t->vector) ((unsigned int) (xM + bar_size * tic), ylowM);
3251                     /* draw the top tic */
3252                     (*t->move) ((unsigned int) (xM - bar_size * tic), yhighM);
3253                     (*t->vector) ((unsigned int) (xM + bar_size * tic), yhighM);
3254                 }
3255             } else {
3256                 /* HBB 981130: see above */
3257                 /* The above has been replaced by Igor inorder to get errorbars
3258                    coming out in polar mode AND to stop the bar from going
3259                    through the symbol */
3260                 if ((xhighM - xlowM) * (xhighM - xlowM) + (yhighM - ylowM) * (yhighM - ylowM)
3261                     > pointsize * tic * pointsize * tic * 4.5) {
3262                     /* Only plot the error bar if it is bigger than the
3263                      * symbol */
3264                     /* The factor of 4.5 should strictly be 4.0, but it looks
3265                      * better to drop the error bar if it is only slightly
3266                      * bigger than the symbol, Igor. */
3267                     if (xlowM == xhighM) {
3268                         (*t->move) (xM, ylowM);
3269                         /* draw the main bar to the symbol end */
3270                         (*t->vector) (xM, (unsigned int) (yM - pointsize * tic));
3271                         (*t->move) (xM, (unsigned int) (yM + pointsize * tic));
3272                         /* draw the other part of the main bar */
3273                         (*t->vector) (xM, yhighM);
3274                     } else {
3275                         (*t->move) (xlowM, ylowM);
3276                         /* draw the main bar in polar mode. Note that here
3277                          * the bar is drawn through the symbol. I tried to
3278                          * fix this, but got into trouble with the two bars
3279                          * (on either side of symbol) not being perfectly
3280                          * parallel due to mapping considerations. Igor
3281                          */
3282                         (*t->vector) (xhighM, yhighM);
3283                     }
3284                     if (bar_size > 0.0) {
3285                         /* The following attempts to ensure that the tics
3286                          * are perpendicular to the error bar, Igor. */
3287                         /*perpendicular to the main bar */
3288                         slope = (xlowM * 1.0 - xhighM * 1.0) / (yhighM * 1.0 - ylowM * 1.0 + 1e-10);
3289                         x1 = xlowM + bar_size * tic / sqrt(1.0 + slope * slope);
3290                         x2 = xlowM - bar_size * tic / sqrt(1.0 + slope * slope);
3291                         y1 = slope * (x1 - xlowM) + ylowM;
3292                         y2 = slope * (x2 - xlowM) + ylowM;
3293
3294                         /* draw the bottom tic */
3295                         (*t->move) ((unsigned int) x1, (unsigned int) y1);
3296                         (*t->vector) ((unsigned int) x2, (unsigned int) y2);
3297
3298                         x1 = xhighM + bar_size * tic / sqrt(1.0 + slope * slope);
3299                         x2 = xhighM - bar_size * tic / sqrt(1.0 + slope * slope);
3300                         y1 = slope * (x1 - xhighM) + yhighM;
3301                         y2 = slope * (x2 - xhighM) + yhighM;
3302                         /* draw the top tic */
3303                         (*t->move) ((unsigned int) x1, (unsigned int) y1);
3304                         (*t->vector) ((unsigned int) x2, (unsigned int) y2);
3305                     }           /* if error bar is bigger than symbol */
3306                 }
3307             }                   /* HBB 981130: see above */
3308         }                       /* for loop */
3309     }                           /* if yerrorbars OR xyerrorbars OR yerrorlines OR xyerrorlines */
3310     if ((plot->plot_style == XERRORBARS)
3311         || (plot->plot_style == XYERRORBARS)
3312         || (plot->plot_style == XERRORLINES)
3313         || (plot->plot_style == XYERRORLINES)) {
3314
3315         /* Draw the horizontal part of the bar */
3316         for (i = 0; i < plot->p_count; i++) {
3317             /* undefined points don't count */
3318             if (plot->points[i].type == UNDEFINED)
3319                 continue;
3320
3321             /* check to see if in yrange */
3322             y = plot->points[i].y;
3323             if (!inrange(y, Y_AXIS.min, Y_AXIS.max))
3324                 continue;
3325             yM = map_y(y);
3326
3327             /* find low and high points of bar, and check xrange */
3328             xhigh = plot->points[i].xhigh;
3329             xlow = plot->points[i].xlow;
3330
3331             high_inrange = inrange(xhigh, X_AXIS.min, X_AXIS.max);
3332             low_inrange = inrange(xlow, X_AXIS.min, X_AXIS.max);
3333
3334             /* compute the plot position of xhigh */
3335             if (high_inrange)
3336                 xhighM = map_x(xhigh);
3337             else if (samesign(xhigh - X_AXIS.max, X_AXIS.max - X_AXIS.min))
3338                 xhighM = map_x(X_AXIS.max);
3339             else
3340                 xhighM = map_x(X_AXIS.min);
3341
3342             /* compute the plot position of xlow */
3343             if (low_inrange)
3344                 xlowM = map_x(xlow);
3345             else if (samesign(xlow - X_AXIS.max, X_AXIS.max - X_AXIS.min))
3346                 xlowM = map_x(X_AXIS.max);
3347             else
3348                 xlowM = map_x(X_AXIS.min);
3349
3350             if (!high_inrange && !low_inrange && xlowM == xhighM)
3351                 /* both out of range on the same side */
3352                 continue;
3353
3354             /* by here everything has been mapped */
3355             (*t->move) (xlowM, yM);
3356             (*t->vector) (xhighM, yM);  /* draw the main bar */
3357             if (bar_size > 0.0) {
3358                 (*t->move) (xlowM, (unsigned int) (yM - bar_size * tic));       /* draw the left tic */
3359                 (*t->vector) (xlowM, (unsigned int) (yM + bar_size * tic));
3360                 (*t->move) (xhighM, (unsigned int) (yM - bar_size * tic));      /* draw the right tic */
3361                 (*t->vector) (xhighM, (unsigned int) (yM + bar_size * tic));
3362             }
3363         }                       /* for loop */
3364     }                           /* if xerrorbars OR xyerrorbars OR xerrorlines OR xyerrorlines */
3365 }
3366
3367 /* plot_boxes:
3368  * EAM Sep 2002 - Consolidate BOXES and FILLEDBOXES
3369  */
3370 static void
3371 plot_boxes(struct curve_points *plot, int xaxis_y)
3372 {
3373     int i;                      /* point index */
3374     int xl, xr, yb, yt;         /* point in terminal coordinates */
3375     double dxl, dxr, dyt;
3376     struct termentry *t = term;
3377     enum coord_type prev = UNDEFINED;   /* type of previous point */
3378
3379 #ifdef EAM_HISTOGRAMS
3380     double dyb = 0.0;
3381     /* The stackheight[] array contains the y coord of the top   */
3382     /* of the stack so far for each point.                       */
3383     if (plot->plot_style == HISTOGRAMS) {
3384         int newsize = plot->p_count;
3385         if (histogram_opts.type == HT_STACKED_IN_TOWERS)
3386             stack_count = 0;
3387         if (histogram_opts.type == HT_STACKED_IN_LAYERS && plot->histogram_sequence == 0)
3388             stack_count = 0;
3389         if (!stackheight) {
3390             stackheight = gp_alloc(
3391                                 newsize * sizeof(struct coordinate GPHUGE),
3392                                 "stackheight array");
3393             for (i = 0; i < newsize; i++) {
3394                 stackheight[i].yhigh = 0;
3395                 stackheight[i].ylow = 0;
3396             }
3397             stack_count = newsize;
3398         } else if (stack_count < newsize) {
3399             stackheight = gp_realloc( stackheight,
3400                                 newsize * sizeof(struct coordinate GPHUGE),
3401                                 "stackheight array");
3402             for (i = stack_count; i < newsize; i++) {
3403                 stackheight[i].yhigh = 0;
3404                 stackheight[i].ylow = 0;
3405             }
3406             stack_count = newsize;
3407         }
3408     }
3409 #endif
3410
3411     for (i = 0; i < plot->p_count; i++) {
3412
3413         switch (plot->points[i].type) {
3414         case OUTRANGE:
3415         case INRANGE:{
3416                 if (plot->points[i].z < 0.0) {
3417                     /* need to auto-calc width */
3418                     if (prev != UNDEFINED)
3419                         if (boxwidth < 0)
3420                             dxl = (plot->points[i-1].x - plot->points[i].x) / 2.0;
3421                         else if (! boxwidth_is_absolute)
3422                             dxl = (plot->points[i-1].x - plot->points[i].x) * boxwidth / 2.0;
3423                         else /* Hits here on 3 column BOXERRORBARS */
3424                             dxl = -boxwidth / 2.0;
3425                     else
3426                         dxl = 0.0;
3427
3428                     if (i < plot->p_count - 1) {
3429                         if (plot->points[i + 1].type != UNDEFINED)
3430                             if (boxwidth < 0)
3431                                 dxr = (plot->points[i+1].x - plot->points[i].x) / 2.0;
3432                             else if (! boxwidth_is_absolute)
3433                                 dxr = (plot->points[i+1].x - plot->points[i].x) * boxwidth / 2.0;
3434                             else /* Hits here on 3 column BOXERRORBARS */
3435                                 dxr = boxwidth / 2.0;
3436                         else
3437                             dxr = -dxl;
3438                     } else {
3439                         dxr = -dxl;
3440                     }
3441
3442                     if (prev == UNDEFINED)
3443                         dxl = -dxr;
3444
3445                     dxl = plot->points[i].x + dxl;
3446                     dxr = plot->points[i].x + dxr;
3447                 } else {        /* z >= 0 */
3448                     dxr = plot->points[i].xhigh;
3449                     dxl = plot->points[i].xlow;
3450                 }
3451
3452                 /* HBB 20040521: ylow should be clipped to the y range. */
3453                 if (plot->plot_style == BOXXYERROR) {
3454                     double temp_y = plot->points[i].ylow;
3455
3456                     cliptorange(temp_y, Y_AXIS.min, Y_AXIS.max);
3457                     xaxis_y = map_y(temp_y);
3458                     dyt = plot->points[i].yhigh;
3459                 } else {
3460                     dyt = plot->points[i].y;
3461                 }
3462
3463 #ifdef EAM_HISTOGRAMS
3464                 if (plot->plot_style == HISTOGRAMS) {
3465                     int ix = i;
3466                     int histogram_linetype = i;
3467                     if (plot->histogram->startcolor > 0)
3468                         histogram_linetype += plot->histogram->startcolor;
3469
3470                     /* Shrink each cluster to fit within one unit along X axis,   */
3471                     /* centered about the integer representing the cluster number */
3472                     /* 'start' is reset to 0 at the top of eval_plots(), and then */
3473                     /* incremented if 'plot new histogram' is encountered.        */
3474                     if (histogram_opts.type == HT_CLUSTERED
3475                     ||  histogram_opts.type == HT_ERRORBARS) {
3476                         int clustersize = plot->histogram->clustersize + histogram_opts.gap;
3477                         dxl  += (i-1) * (clustersize - 1) + plot->histogram_sequence;
3478                         dxr  += (i-1) * (clustersize - 1) + plot->histogram_sequence;
3479                         dxl  += histogram_opts.gap/2;
3480                         dxr  += histogram_opts.gap/2;
3481                         dxl  /= clustersize;
3482                         dxr  /= clustersize;
3483                         dxl  += plot->histogram->start + 0.5;
3484                         dxr  += plot->histogram->start + 0.5;
3485                     } else if (histogram_opts.type == HT_STACKED_IN_TOWERS) {
3486                         dxl  = plot->histogram->start - boxwidth / 2.0;
3487                         dxr  = plot->histogram->start + boxwidth / 2.0;
3488                         dxl += plot->histogram_sequence;
3489                         dxr += plot->histogram_sequence;
3490                     } else if (histogram_opts.type == HT_STACKED_IN_LAYERS) {
3491                         dxl += plot->histogram->start;
3492                         dxr += plot->histogram->start;
3493                     }
3494
3495                     switch (histogram_opts.type) {
3496                     case HT_STACKED_IN_TOWERS:
3497                         ix = 0;
3498                         /* Line type (color) must match row number */
3499                         if (prefer_line_styles) {
3500                             struct lp_style_type ls;
3501                             lp_use_properties(&ls, histogram_linetype+1, FALSE);
3502                             apply_pm3dcolor(&ls.pm3d_color, term);
3503                         } else
3504                             (*t->linetype)(histogram_linetype);
3505                         plot->fill_properties.fillpattern = histogram_linetype;
3506                         /* Fall through */
3507                     case HT_STACKED_IN_LAYERS:
3508
3509                         if( plot->points[i].y >= 0 ){
3510                             dyb = stackheight[ix].yhigh;
3511                             dyt += stackheight[ix].yhigh;
3512                             stackheight[ix].yhigh += plot->points[i].y;
3513                         } else {
3514                             dyb = stackheight[ix].ylow;
3515                             dyt += stackheight[ix].ylow;
3516                             stackheight[ix].ylow += plot->points[i].y;
3517                         }
3518
3519                         if ((Y_AXIS.min < Y_AXIS.max && dyb < Y_AXIS.min)
3520                         ||  (Y_AXIS.max < Y_AXIS.min && dyb > Y_AXIS.min))
3521                             dyb = Y_AXIS.min;
3522                         if ((Y_AXIS.min < Y_AXIS.max && dyb > Y_AXIS.max)
3523                         ||  (Y_AXIS.max < Y_AXIS.min && dyb < Y_AXIS.max))
3524                             dyb = Y_AXIS.max;
3525                         break;
3526                     case HT_CLUSTERED:
3527                     case HT_ERRORBARS:
3528                         break;
3529                     }
3530                 }
3531 #endif
3532
3533                 /* clip to border */
3534                 cliptorange(dyt, Y_AXIS.min, Y_AXIS.max);
3535                 cliptorange(dxr, X_AXIS.min, X_AXIS.max);
3536                 cliptorange(dxl, X_AXIS.min, X_AXIS.max);
3537
3538                 xl = map_x(dxl);
3539                 xr = map_x(dxr);
3540                 yt = map_y(dyt);
3541                 yb = xaxis_y;
3542
3543 #ifdef EAM_HISTOGRAMS
3544                 if (plot->plot_style == HISTOGRAMS
3545                 && (histogram_opts.type == HT_STACKED_IN_LAYERS
3546                     || histogram_opts.type == HT_STACKED_IN_TOWERS))
3547                         yb = map_y(dyb);
3548 #endif
3549
3550                 /* Variable color */
3551                 if (plot->plot_style == BOXES) {
3552                     check_for_variable_color(plot, &plot->points[i]);
3553                 }
3554
3555                 if ((plot->fill_properties.fillstyle != FS_EMPTY) && t->fillbox) {
3556                     int x, y, w, h;
3557                     int style;
3558
3559                     x = xl;
3560                     y = yb;
3561                     w = xr - xl + 1;
3562                     h = yt - yb + 1;
3563                     /* avoid negative width/height */
3564                     if( w <= 0 ) {
3565                         x = xr;
3566                         w = xl - xr + 1;
3567                     }
3568                     if( h <= 0 ) {
3569                         y = yt;
3570                         h = yb - yt + 1;
3571                     }
3572
3573                     style = style_from_fill(&plot->fill_properties);
3574
3575                     if (plot->lp_properties.use_palette && t->filled_polygon) {
3576                         (*t->filled_polygon)(4, fill_corners(style,x,y,w-1,h-1));
3577                     } else
3578                         (*t->fillbox) (style, x, y, w, h);
3579
3580                     /* FIXME EAM - Is this still correct??? */
3581                     if (strcmp(t->name, "fig") == 0) break;
3582
3583                     if (plot->fill_properties.border_linetype == LT_NODRAW)
3584                         break;
3585                     if (plot->fill_properties.border_linetype != LT_UNDEFINED)
3586                         (*t->linetype)(plot->fill_properties.border_linetype);
3587                 }
3588
3589                 newpath();
3590                 (*t->move) (xl, yb);
3591                 (*t->vector) (xl, yt);
3592                 (*t->vector) (xr, yt);
3593                 (*t->vector) (xr, yb);
3594                 (*t->vector) (xl, yb);
3595                 closepath();
3596
3597                 if( t->fillbox && plot->fill_properties.border_linetype != LT_UNDEFINED) {
3598                     (*t->linetype)(plot->lp_properties.l_type);
3599                     if (plot->lp_properties.use_palette)
3600                         apply_pm3dcolor(&plot->lp_properties.pm3d_color,t);
3601                 }
3602
3603                 break;
3604             }                   /* case OUTRANGE, INRANGE */
3605
3606         default:                /* just a safety */
3607         case UNDEFINED:{
3608                 break;
3609             }
3610
3611         }                       /* switch point-type */
3612
3613         prev = plot->points[i].type;
3614
3615     }                           /*loop */
3616 }
3617
3618
3619
3620 /* plot_points:
3621  * Plot the curves in POINTSTYLE style
3622  */
3623 static void
3624 plot_points(struct curve_points *plot)
3625 {
3626     int i;
3627     int x, y;
3628     struct termentry *t = term;
3629
3630     for (i = 0; i < plot->p_count; i++) {
3631         if (plot->points[i].type == INRANGE) {
3632             x = map_x(plot->points[i].x);
3633             y = map_y(plot->points[i].y);
3634             /* do clipping if necessary */
3635             if (!clip_points
3636                 || (x >= plot_bounds.xleft + p_width
3637                     && y >= plot_bounds.ybot + p_height
3638                     && x <= plot_bounds.xright - p_width
3639                     && y <= plot_bounds.ytop - p_height)) {
3640
3641                 check_for_variable_color(plot, &plot->points[i]);
3642
3643                 if (plot->plot_style == POINTSTYLE
3644                 &&  plot->lp_properties.p_size == PTSZ_VARIABLE)
3645                     (*t->pointsize)(pointsize * plot->points[i].z);
3646                 (*t->point) (x, y, plot->lp_properties.p_type);
3647             }
3648         }
3649     }
3650 }
3651
3652 /* plot_dots:
3653  * Plot the curves in DOTS style
3654  */
3655 static void
3656 plot_dots(struct curve_points *plot)
3657 {
3658     int i;
3659     int x, y;
3660     struct termentry *t = term;
3661
3662     for (i = 0; i < plot->p_count; i++) {
3663         if (plot->points[i].type == INRANGE) {
3664             x = map_x(plot->points[i].x);
3665             y = map_y(plot->points[i].y);
3666             check_for_variable_color(plot, &plot->points[i]);
3667             /* point type -1 is a dot */
3668             (*t->point) (x, y, -1);
3669         }
3670     }
3671 }
3672
3673 /* plot_vectors:
3674  * Plot the curves in VECTORS style
3675  */
3676 static void
3677 plot_vectors(struct curve_points *plot)
3678 {
3679     int i;
3680     int x1, y1, x2, y2;
3681     struct termentry *t = term;
3682     struct coordinate points[2];
3683     double ex, ey;
3684     double lx[2], ly[2];
3685
3686     /* Only necessary once because all arrows equal */
3687     term_apply_lp_properties(&(plot->arrow_properties.lp_properties));
3688     apply_head_properties(&(plot->arrow_properties));
3689
3690     for (i = 0; i < plot->p_count; i++) {
3691         points[0] = plot->points[i];
3692         points[1].x = plot->points[i].xhigh;
3693         points[1].y = plot->points[i].yhigh;
3694
3695         if (points[0].type == UNDEFINED)
3696             continue;
3697
3698         /* variable color read from extra data column. Most styles */
3699         /* have this stored in yhigh, but VECTOR stuffed it into z */
3700         points[0].yhigh = points[0].z;
3701         check_for_variable_color(plot, &points[0]);
3702
3703         if (inrange(points[1].x, X_AXIS.min, X_AXIS.max)
3704             && inrange(points[1].y, Y_AXIS.min, Y_AXIS.max)) {
3705             /* to inrange */
3706             points[1].type = INRANGE;
3707             x2 = map_x(points[1].x);
3708             y2 = map_y(points[1].y);
3709             if (points[0].type == INRANGE) {
3710                 x1 = map_x(points[0].x);
3711                 y1 = map_y(points[0].y);
3712                 (*t->arrow) (x1, y1, x2, y2, plot->arrow_properties.head);
3713             } else if (points[0].type == OUTRANGE) {
3714                 /* from outrange to inrange */
3715                 if (clip_lines1) {
3716                     edge_intersect(points, 1, &ex, &ey);
3717                     x1 = map_x(ex);
3718                     y1 = map_y(ey);
3719                     if (plot->arrow_properties.head & END_HEAD)
3720                         (*t->arrow) (x1, y1, x2, y2, END_HEAD);
3721                     else
3722                         (*t->arrow) (x1, y1, x2, y2, NOHEAD);
3723                 }
3724             }
3725         } else {
3726             /* to outrange */
3727             points[1].type = OUTRANGE;
3728             if (points[0].type == INRANGE) {
3729                 /* from inrange to outrange */
3730                 if (clip_lines1) {
3731                     x1 = map_x(points[0].x);
3732                     y1 = map_y(points[0].y);
3733                     edge_intersect(points, 1, &ex, &ey);
3734                     x2 = map_x(ex);
3735                     y2 = map_y(ey);
3736                     if (plot->arrow_properties.head & BACKHEAD)
3737                         (*t->arrow) (x2, y2, x1, y1, BACKHEAD);
3738                     else
3739                         (*t->arrow) (x1, y1, x2, y2, NOHEAD);
3740                 }
3741             } else if (points[0].type == OUTRANGE) {
3742                 /* from outrange to outrange */
3743                 if (clip_lines2) {
3744                     if (two_edge_intersect(points, 1, lx, ly)) {
3745                         x1 = map_x(lx[0]);
3746                         y1 = map_y(ly[0]);
3747                         x2 = map_x(lx[1]);
3748                         y2 = map_y(ly[1]);
3749                         (*t->arrow) (x1, y1, x2, y2, NOHEAD);
3750                     }
3751                 }
3752             }
3753         }
3754     }
3755 }
3756
3757
3758 /* plot_f_bars() - finance bars */
3759 static void
3760 plot_f_bars(struct curve_points *plot)
3761 {
3762     int i;                      /* point index */
3763     struct termentry *t = term;
3764     double x;                   /* position of the bar */
3765     double ylow, yhigh, yclose, yopen;  /* the ends of the bars */
3766     unsigned int xM, ylowM, yhighM;     /* the mapped version of above */
3767     TBOOLEAN low_inrange, high_inrange;
3768     int tic = GPMAX(ERRORBARTIC/2,1);
3769
3770     for (i = 0; i < plot->p_count; i++) {
3771         /* undefined points don't count */
3772         if (plot->points[i].type == UNDEFINED)
3773             continue;
3774
3775         /* check to see if in xrange */
3776         x = plot->points[i].x;
3777         if (!inrange(x, X_AXIS.min, X_AXIS.max))
3778             continue;
3779         xM = map_x(x);
3780
3781         /* find low and high points of bar, and check yrange */
3782         yhigh = plot->points[i].yhigh;
3783         ylow = plot->points[i].ylow;
3784         yclose = plot->points[i].z;
3785         yopen = plot->points[i].y;
3786
3787         high_inrange = inrange(yhigh, Y_AXIS.min, Y_AXIS.max);
3788         low_inrange = inrange(ylow, Y_AXIS.min, Y_AXIS.max);
3789
3790         /* compute the plot position of yhigh */
3791         if (high_inrange)
3792             yhighM = map_y(yhigh);
3793         else if (samesign(yhigh - Y_AXIS.max, Y_AXIS.max - Y_AXIS.min))
3794             yhighM = map_y(Y_AXIS.max);
3795         else
3796             yhighM = map_y(Y_AXIS.min);
3797
3798         /* compute the plot position of ylow */
3799         if (low_inrange)
3800             ylowM = map_y(ylow);
3801         else if (samesign(ylow - Y_AXIS.max, Y_AXIS.max - Y_AXIS.min))
3802             ylowM = map_y(Y_AXIS.max);
3803         else
3804             ylowM = map_y(Y_AXIS.min);
3805
3806         if (!high_inrange && !low_inrange && ylowM == yhighM)
3807             /* both out of range on the same side */
3808             continue;
3809
3810         /* by here everything has been mapped */
3811         (*t->move) (xM, ylowM);
3812         (*t->vector) (xM, yhighM);      /* draw the main bar */
3813         /* draw the open tic */
3814         (*t->move) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
3815         (*t->vector) (xM, map_y(yopen));
3816         /* draw the close tic */
3817         (*t->move) ((unsigned int) (xM + bar_size * tic), map_y(yclose));
3818         (*t->vector) (xM, map_y(yclose));
3819     }
3820 }
3821
3822
3823 /* plot_c_bars:
3824  * Plot the curves in CANDLESTICSK style
3825  *  we just plot the bars; the points are not plotted
3826  */
3827 static void
3828 plot_c_bars(struct curve_points *plot)
3829 {
3830     struct termentry *t = term;
3831     int i;
3832     double x;                                           /* position of the bar */
3833     double dxl, dxr, ylow, yhigh, yclose, yopen;        /* the ends of the bars */
3834     int xlowM, xhighM, xM, ylowM, yhighM;               /* mapped version of above */
3835     int ymin, ymax;                                     /* clipped to plot extent */
3836     enum coord_type prev = UNDEFINED;                   /* type of previous point */
3837     TBOOLEAN low_inrange, high_inrange;
3838     int tic = GPMAX(ERRORBARTIC/2,1);
3839
3840     for (i = 0; i < plot->p_count; i++) {
3841         /* undefined points don't count */
3842         if (plot->points[i].type == UNDEFINED)
3843             continue;
3844
3845         /* check to see if in xrange */
3846         x = plot->points[i].x;
3847         if (!inrange(x, X_AXIS.min, X_AXIS.max))
3848             continue;
3849         xM = map_x(x);
3850
3851         /* find low and high points of bar, and check yrange */
3852         yhigh = plot->points[i].yhigh;
3853         ylow = plot->points[i].ylow;
3854         yclose = plot->points[i].z;
3855         yopen = plot->points[i].y;
3856
3857         /* HBB 20010928: To make code match the documentation, ensure
3858          * yhigh is actually higher than ylow */
3859         if (yhigh < ylow) {
3860             double temp = ylow;
3861             ylow = yhigh;
3862             yhigh = temp;
3863         }
3864
3865         high_inrange = inrange(yhigh, axis_array[y_axis].min, axis_array[y_axis].max);
3866         low_inrange = inrange(ylow, axis_array[y_axis].min, axis_array[y_axis].max);
3867
3868         /* compute the plot position of yhigh */
3869         if (high_inrange)
3870             yhighM = map_y(yhigh);
3871         else if (samesign(yhigh - axis_array[y_axis].max,
3872                           axis_array[y_axis].max - axis_array[y_axis].min))
3873             yhighM = map_y(axis_array[y_axis].max);
3874         else
3875             yhighM = map_y(axis_array[y_axis].min);
3876
3877         /* compute the plot position of ylow */
3878         if (low_inrange)
3879             ylowM = map_y(ylow);
3880         else if (samesign(ylow - axis_array[y_axis].max,
3881                           axis_array[y_axis].max - axis_array[y_axis].min))
3882             ylowM = map_y(axis_array[y_axis].max);
3883         else
3884             ylowM = map_y(axis_array[y_axis].min);
3885
3886         if (!high_inrange && !low_inrange && ylowM == yhighM)
3887             /* both out of range on the same side */
3888             continue;
3889
3890         if (boxwidth < 0.0) {
3891             /* EAM Feb 2003 - Old code did essentially this */
3892             xlowM = xM - bar_size * tic;
3893             xhighM = xM + bar_size * tic;
3894         } else {
3895
3896             dxl = -boxwidth / 2.0;
3897             if (prev != UNDEFINED)
3898                 if (! boxwidth_is_absolute)
3899                     dxl = (plot->points[i-1].x - plot->points[i].x) * boxwidth / 2.0;
3900
3901             dxr = -dxl;
3902             if (i < plot->p_count - 1) {
3903                 if (plot->points[i + 1].type != UNDEFINED) {
3904                     if (! boxwidth_is_absolute)
3905                         dxr = (plot->points[i+1].x - plot->points[i].x) * boxwidth / 2.0;
3906                     else
3907                         dxr = boxwidth / 2.0;
3908                 }
3909             }
3910
3911         if (prev == UNDEFINED)
3912             dxl = -dxr;
3913
3914         dxl = plot->points[i].x + dxl;
3915         dxr = plot->points[i].x + dxr;
3916         cliptorange(dxr, X_AXIS.min, X_AXIS.max);
3917         cliptorange(dxl, X_AXIS.min, X_AXIS.max);
3918         xlowM = map_x(dxl);
3919         xhighM = map_x(dxr);
3920         }
3921
3922         /* EAM Feb 2006 Clip to plot vertical extent */
3923         cliptorange(yopen, Y_AXIS.min, Y_AXIS.max);
3924         cliptorange(yclose, Y_AXIS.min, Y_AXIS.max);
3925         if (map_y(yopen) < map_y(yclose)) {
3926             ymin = map_y(yopen); ymax = map_y(yclose);
3927         } else {
3928             ymax = map_y(yopen); ymin = map_y(yclose);
3929         }
3930
3931         if ((plot->fill_properties.fillstyle != FS_EMPTY) && term->fillbox) {
3932             int style = style_from_fill(&plot->fill_properties);
3933             unsigned int x = xlowM;
3934             unsigned int y = ymin;
3935             unsigned int w = (xhighM-xlowM);
3936             unsigned int h = (ymax-ymin);
3937
3938             if (plot->lp_properties.use_palette && t->filled_polygon)
3939                 (*t->filled_polygon)(4, fill_corners(style,x,y,w,h));
3940             else
3941                 (*t->fillbox)(style, x, y, w, h);
3942
3943             if ((plot->fill_properties.border_linetype != LT_NODRAW)
3944             &&  (plot->fill_properties.border_linetype != LT_UNDEFINED))
3945                 (*t->linetype)(plot->fill_properties.border_linetype);
3946         }
3947
3948         /* Draw whiskers and an open box */
3949             (*t->move)   (xM, ylowM);
3950             (*t->vector) (xM, map_y(yopen));
3951             (*t->move)   (xM, map_y(yclose));
3952             (*t->vector) (xM, yhighM);
3953
3954             newpath();
3955             (*t->move)   (xlowM, map_y(yopen));
3956             (*t->vector) (xhighM, map_y(yopen));
3957             (*t->vector) (xhighM, map_y(yclose));
3958             (*t->vector) (xlowM, map_y(yclose));
3959             (*t->vector) (xlowM, map_y(yopen));
3960             closepath();
3961
3962         /* Some users prefer bars at the end of the whiskers */
3963         if (plot->arrow_properties.head == BOTH_HEADS) {
3964             double frac = plot->arrow_properties.head_length;
3965             unsigned int d = (frac <= 0) ? 0 : (xhighM-xlowM)*(1.-frac)/2.;
3966         
3967             (*t->move)   (xlowM+d, yhighM);
3968             (*t->vector) (xhighM-d, yhighM);
3969             (*t->move)   (xlowM+d, ylowM);
3970             (*t->vector) (xhighM-d, ylowM);
3971         }
3972
3973         /* Reset to original color, if we changed it for the border */
3974         if ((plot->fill_properties.fillstyle != FS_EMPTY) && term->fillbox) {
3975             if ((plot->fill_properties.border_linetype != LT_NODRAW)
3976             &&  (plot->fill_properties.border_linetype != LT_UNDEFINED)) {
3977                 (*t->linetype)(plot->lp_properties.l_type);
3978                 if (plot->lp_properties.use_palette)
3979                     apply_pm3dcolor(&plot->lp_properties.pm3d_color,t);
3980             }
3981         }
3982
3983         /* draw two extra vertical bars to indicate open > close */
3984         if (yopen > yclose) {
3985             (*t->move)   ( (xM + xlowM) / 2, ymin);
3986             (*t->vector) ( (xM + xlowM) / 2, ymax);
3987             (*t->move)   ( (xM + xhighM) / 2, ymin);
3988             (*t->vector) ( (xM + xhighM) / 2, ymax);
3989         }
3990
3991         prev = plot->points[i].type;
3992     }
3993 }
3994
3995
3996 /* FIXME
3997  * there are LOADS of == style double comparisons in here!
3998  */
3999 /* single edge intersection algorithm */
4000 /* Given two points, one inside and one outside the plot, return
4001  * the point where an edge of the plot intersects the line segment defined
4002  * by the two points.
4003  */
4004 static int
4005 edge_intersect(
4006     struct coordinate GPHUGE *points, /* the points array */
4007     int i,                      /* line segment from point i-1 to point i */
4008     double *ex, double *ey)     /* the point where it crosses an edge */
4009 {
4010     double ix = points[i - 1].x;
4011     double iy = points[i - 1].y;
4012     double ox = points[i].x;
4013     double oy = points[i].y;
4014     double x, y;                /* possible intersection point */
4015
4016     if (points[i].type == INRANGE) {
4017         /* swap points around so that ix/ix/iz are INRANGE and
4018          * ox/oy/oz are OUTRANGE
4019          */
4020         x = ix;
4021         ix = ox;
4022         ox = x;
4023         y = iy;
4024         iy = oy;
4025         oy = y;
4026     }
4027     /* nasty degenerate cases, effectively drawing to an infinity point (?)
4028      * cope with them here, so don't process them as a "real" OUTRANGE point
4029      *
4030      * If more than one coord is -VERYLARGE, then can't ratio the "infinities"
4031      * so drop out by returning the INRANGE point.
4032      *
4033      * Obviously, only need to test the OUTRANGE point (coordinates) */
4034     if (ox == -VERYLARGE || oy == -VERYLARGE) {
4035         *ex = ix;
4036         *ey = iy;
4037
4038         if (ox == -VERYLARGE) {
4039             /* can't get a direction to draw line, so simply
4040              * return INRANGE point */
4041             if (oy == -VERYLARGE)
4042                 return LEFT_EDGE|BOTTOM_EDGE;
4043
4044             *ex = X_AXIS.min;
4045             return LEFT_EDGE;
4046         }
4047         /* obviously oy is -VERYLARGE and ox != -VERYLARGE */
4048         *ey = Y_AXIS.min;
4049         return BOTTOM_EDGE;
4050     }
4051     /*
4052      * Can't have case (ix == ox && iy == oy) as one point
4053      * is INRANGE and one point is OUTRANGE.
4054      */
4055     if (iy == oy) {
4056         /* horizontal line */
4057         /* assume inrange(iy, Y_AXIS.min, Y_AXIS.max) */
4058         *ey = iy;               /* == oy */
4059
4060         if (inrange(X_AXIS.max, ix, ox)) {
4061             *ex = X_AXIS.max;
4062             return RIGHT_EDGE;
4063         } else if (inrange(X_AXIS.min, ix, ox)) {
4064             *ex = X_AXIS.min;
4065             return LEFT_EDGE;
4066         } else {
4067             graph_error("error in edge_intersect");
4068             return 0;
4069         }
4070     } else if (ix == ox) {
4071         /* vertical line */
4072         /* assume inrange(ix, X_AXIS.min, X_AXIS.max) */
4073         *ex = ix;               /* == ox */
4074
4075         if (inrange(Y_AXIS.max, iy, oy)) {
4076             *ey = Y_AXIS.max;
4077             return TOP_EDGE;
4078         } else if (inrange(Y_AXIS.min, iy, oy)) {
4079             *ey = Y_AXIS.min;
4080             return BOTTOM_EDGE;
4081         } else {
4082             graph_error("error in edge_intersect");
4083             return 0;
4084         }
4085     }
4086     /* slanted line of some kind */
4087
4088     /* does it intersect Y_AXIS.min edge */
4089     if (inrange(Y_AXIS.min, iy, oy) && Y_AXIS.min != iy && Y_AXIS.min != oy) {
4090         x = ix + (Y_AXIS.min - iy) * ((ox - ix) / (oy - iy));
4091         if (inrange(x, X_AXIS.min, X_AXIS.max)) {
4092             *ex = x;
4093             *ey = Y_AXIS.min;
4094             return BOTTOM_EDGE;         /* yes */
4095         }
4096     }
4097     /* does it intersect Y_AXIS.max edge */
4098     if (inrange(Y_AXIS.max, iy, oy) && Y_AXIS.max != iy && Y_AXIS.max != oy) {
4099         x = ix + (Y_AXIS.max - iy) * ((ox - ix) / (oy - iy));
4100         if (inrange(x, X_AXIS.min, X_AXIS.max)) {
4101             *ex = x;
4102             *ey = Y_AXIS.max;
4103             return TOP_EDGE;            /* yes */
4104         }
4105     }
4106     /* does it intersect X_AXIS.min edge */
4107     if (inrange(X_AXIS.min, ix, ox) && X_AXIS.min != ix && X_AXIS.min != ox) {
4108         y = iy + (X_AXIS.min - ix) * ((oy - iy) / (ox - ix));
4109         if (inrange(y, Y_AXIS.min, Y_AXIS.max)) {
4110             *ex = X_AXIS.min;
4111             *ey = y;
4112             return LEFT_EDGE;
4113         }
4114     }
4115     /* does it intersect X_AXIS.max edge */
4116     if (inrange(X_AXIS.max, ix, ox) && X_AXIS.max != ix && X_AXIS.max != ox) {
4117         y = iy + (X_AXIS.max - ix) * ((oy - iy) / (ox - ix));
4118         if (inrange(y, Y_AXIS.min, Y_AXIS.max)) {
4119             *ex = X_AXIS.max;
4120             *ey = y;
4121             return RIGHT_EDGE;
4122         }
4123     }
4124     /* If we reach here, the inrange point is on the edge, and
4125      * the line segment from the outrange point does not cross any
4126      * other edges to get there. In this case, we return the inrange
4127      * point as the 'edge' intersection point. This will basically draw
4128      * line.
4129      */
4130     *ex = ix;
4131     *ey = iy;
4132     return 0;
4133 }
4134
4135 /* XXX - JG  */
4136 /* single edge intersection algorithm for "steps" curves */
4137 /*
4138  * Given two points, one inside and one outside the plot, return
4139  * the point where an edge of the plot intersects the line segments
4140  * forming the step between the two points.
4141  *
4142  * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from
4143  * P1 to P2 is drawn as two line segments: (x1,y1)->(x2,y1) and
4144  * (x2,y1)->(x2,y2).
4145  */
4146 static void
4147 edge_intersect_steps(
4148     struct coordinate GPHUGE *points, /* the points array */
4149     int i,                      /* line segment from point i-1 to point i */
4150     double *ex, double *ey)     /* the point where it crosses an edge */
4151 {
4152     /* global X_AXIS.min, X_AXIS.max, Y_AXIS.min, X_AXIS.max */
4153     double ax = points[i - 1].x;
4154     double ay = points[i - 1].y;
4155     double bx = points[i].x;
4156     double by = points[i].y;
4157
4158     if (points[i].type == INRANGE) {    /* from OUTRANGE to INRANG */
4159         if (inrange(ay, Y_AXIS.min, Y_AXIS.max)) {
4160             *ey = ay;
4161             cliptorange(ax, X_AXIS.min, X_AXIS.max);
4162             *ex = ax;
4163         } else {
4164             *ex = bx;
4165             cliptorange(ay, Y_AXIS.min, Y_AXIS.max);
4166             *ey = ay;
4167         }
4168     } else {                    /* from INRANGE to OUTRANGE */
4169         if (inrange(bx, X_AXIS.min, X_AXIS.max)) {
4170             *ex = bx;
4171             cliptorange(by, Y_AXIS.min, Y_AXIS.max);
4172             *ey = by;
4173         } else {
4174             *ey = ay;
4175             cliptorange(bx, X_AXIS.min, X_AXIS.max);
4176             *ex = bx;
4177         }
4178     }
4179     return;
4180 }
4181
4182 /* XXX - HOE  */
4183 /* single edge intersection algorithm for "fsteps" curves */
4184 /* fsteps means step on forward y-value.
4185  * Given two points, one inside and one outside the plot, return
4186  * the point where an edge of the plot intersects the line segments
4187  * forming the step between the two points.
4188  *
4189  * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from
4190  * P1 to P2 is drawn as two line segments: (x1,y1)->(x1,y2) and
4191  * (x1,y2)->(x2,y2).
4192  */
4193 static void
4194 edge_intersect_fsteps(
4195     struct coordinate GPHUGE *points, /* the points array */
4196     int i,                      /* line segment from point i-1 to point i */
4197     double *ex, double *ey)     /* the point where it crosses an edge */
4198 {
4199     /* global X_AXIS.min, X_AXIS.max, Y_AXIS.min, X_AXIS.max */
4200     double ax = points[i - 1].x;
4201     double ay = points[i - 1].y;
4202     double bx = points[i].x;
4203     double by = points[i].y;
4204
4205     if (points[i].type == INRANGE) {    /* from OUTRANGE to INRANG */
4206         if (inrange(ax, X_AXIS.min, X_AXIS.max)) {
4207             *ex = ax;
4208             cliptorange(ay, Y_AXIS.min, Y_AXIS.max);
4209             *ey = ay;
4210         } else {
4211             *ey = by;
4212             cliptorange(bx, X_AXIS.min, X_AXIS.max);
4213             *ex = bx;
4214         }
4215     } else {                    /* from INRANGE to OUTRANGE */
4216         if (inrange(by, Y_AXIS.min, Y_AXIS.max)) {
4217             *ey = by;
4218             cliptorange(bx, X_AXIS.min, X_AXIS.max);
4219             *ex = bx;
4220         } else {
4221             *ex = ax;
4222             cliptorange(by, Y_AXIS.min, Y_AXIS.max);
4223             *ey = by;
4224         }
4225     }
4226     return;
4227 }
4228
4229 /* XXX - JG  */
4230 /* double edge intersection algorithm for "steps" plot */
4231 /* Given two points, both outside the plot, return the points where an
4232  * edge of the plot intersects the line segments forming a step
4233  * by the two points. There may be zero, one, two, or an infinite number
4234  * of intersection points. (One means an intersection at a corner, infinite
4235  * means overlaying the edge itself). We return FALSE when there is nothing
4236  * to draw (zero intersections), and TRUE when there is something to
4237  * draw (the one-point case is a degenerate of the two-point case and we do
4238  * not distinguish it - we draw it anyway).
4239  *
4240  * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from
4241  * P1 to P2 is drawn as two line segments: (x1,y1)->(x2,y1) and
4242  * (x2,y1)->(x2,y2).
4243  */
4244 static TBOOLEAN                 /* any intersection? */
4245 two_edge_intersect_steps(
4246     struct coordinate GPHUGE *points, /* the points array */
4247     int i,                      /* line segment from point i-1 to point i */
4248     double *lx, double *ly)     /* lx[2], ly[2]: points where it crosses edges */
4249 {
4250     /* global X_AXIS.min, X_AXIS.max, Y_AXIS.min, X_AXIS.max */
4251     double ax = points[i - 1].x;
4252     double ay = points[i - 1].y;
4253     double bx = points[i].x;
4254     double by = points[i].y;
4255
4256     if (GPMAX(ax, bx) < X_AXIS.min || GPMIN(ax, bx) > X_AXIS.max
4257         || GPMAX(ay, by) < Y_AXIS.min || GPMIN(ay, by) > Y_AXIS.max
4258         || (!inrange(ay, Y_AXIS.min, Y_AXIS.max)
4259             && !inrange(bx, X_AXIS.min, X_AXIS.max))
4260         ) {
4261         return (FALSE);
4262     } else if (inrange(ay, Y_AXIS.min, Y_AXIS.max)
4263                && inrange(bx, X_AXIS.min, X_AXIS.max)) {
4264         /* corner of step inside plotspace */
4265         cliptorange(ax, X_AXIS.min, X_AXIS.max);
4266         *lx++ = ax;
4267         *ly++ = ay;
4268
4269         cliptorange(by, Y_AXIS.min, Y_AXIS.max);
4270         *lx = bx;
4271         *ly = by;
4272
4273         return (TRUE);
4274     } else if (inrange(ay, Y_AXIS.min, Y_AXIS.max)) {
4275         /* cross plotspace in x-direction */
4276         *lx++ = X_AXIS.min;
4277         *ly++ = ay;
4278         *lx = X_AXIS.max;
4279         *ly = ay;
4280         return (TRUE);
4281     } else if (inrange(ax, X_AXIS.min, X_AXIS.max)) {
4282         /* cross plotspace in y-direction */
4283         *lx++ = bx;
4284         *ly++ = Y_AXIS.min;
4285         *lx = bx;
4286         *ly = Y_AXIS.max;
4287         return (TRUE);
4288     } else
4289         return (FALSE);
4290 }
4291
4292 /* XXX - HOE  */
4293 /* double edge intersection algorithm for "fsteps" plot */
4294 /* Given two points, both outside the plot, return the points where an
4295  * edge of the plot intersects the line segments forming a step
4296  * by the two points. There may be zero, one, two, or an infinite number
4297  * of intersection points. (One means an intersection at a corner, infinite
4298  * means overlaying the edge itself). We return FALSE when there is nothing
4299  * to draw (zero intersections), and TRUE when there is something to
4300  * draw (the one-point case is a degenerate of the two-point case and we do
4301  * not distinguish it - we draw it anyway).
4302  *
4303  * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from
4304  * P1 to P2 is drawn as two line segments: (x1,y1)->(x1,y2) and
4305  * (x1,y2)->(x2,y2).
4306  */
4307 static TBOOLEAN                 /* any intersection? */
4308 two_edge_intersect_fsteps(
4309     struct coordinate GPHUGE *points, /* the points array */
4310     int i,                      /* line segment from point i-1 to point i */
4311     double *lx, double *ly)     /* lx[2], ly[2]: points where it crosses edges */
4312 {
4313     /* global X_AXIS.min, X_AXIS.max, Y_AXIS.min, X_AXIS.max */
4314     double ax = points[i - 1].x;
4315     double ay = points[i - 1].y;
4316     double bx = points[i].x;
4317     double by = points[i].y;
4318
4319     if (GPMAX(ax, bx) < X_AXIS.min || GPMIN(ax, bx) > X_AXIS.max
4320         || GPMAX(ay, by) < Y_AXIS.min || GPMIN(ay, by) > Y_AXIS.max
4321         || (!inrange(by, Y_AXIS.min, Y_AXIS.max)
4322             && !inrange(ax, X_AXIS.min, X_AXIS.max))
4323         ) {
4324         return (FALSE);
4325     } else if (inrange(by, Y_AXIS.min, Y_AXIS.max)
4326                && inrange(ax, X_AXIS.min, X_AXIS.max)) {
4327         /* corner of step inside plotspace */
4328         cliptorange(ay, Y_AXIS.min, Y_AXIS.max);
4329         *lx++ = ax;
4330         *ly++ = ay;
4331
4332         cliptorange(bx, X_AXIS.min, X_AXIS.max);
4333         *lx = bx;
4334         *ly = by;
4335
4336         return (TRUE);
4337     } else if (inrange(by, Y_AXIS.min, Y_AXIS.max)) {
4338         /* cross plotspace in x-direction */
4339         *lx++ = X_AXIS.min;
4340         *ly++ = by;
4341         *lx = X_AXIS.max;
4342         *ly = by;
4343         return (TRUE);
4344     } else if (inrange(ax, X_AXIS.min, X_AXIS.max)) {
4345         /* cross plotspace in y-direction */
4346         *lx++ = ax;
4347         *ly++ = Y_AXIS.min;
4348         *lx = ax;
4349         *ly = Y_AXIS.max;
4350         return (TRUE);
4351     } else
4352         return (FALSE);
4353 }
4354
4355 /* double edge intersection algorithm */
4356 /* Given two points, both outside the plot, return
4357  * the points where an edge of the plot intersects the line segment defined
4358  * by the two points. There may be zero, one, two, or an infinite number
4359  * of intersection points. (One means an intersection at a corner, infinite
4360  * means overlaying the edge itself). We return FALSE when there is nothing
4361  * to draw (zero intersections), and TRUE when there is something to
4362  * draw (the one-point case is a degenerate of the two-point case and we do
4363  * not distinguish it - we draw it anyway).
4364  */
4365 static TBOOLEAN                 /* any intersection? */
4366 two_edge_intersect(
4367     struct coordinate GPHUGE *points, /* the points array */
4368     int i,                      /* line segment from point i-1 to point i */
4369     double *lx, double *ly)     /* lx[2], ly[2]: points where it crosses edges */
4370 {
4371     /* global X_AXIS.min, X_AXIS.max, Y_AXIS.min, X_AXIS.max */
4372     int count;
4373     double ix = points[i - 1].x;
4374     double iy = points[i - 1].y;
4375     double ox = points[i].x;
4376     double oy = points[i].y;
4377     double t[4];
4378     double swap;
4379     double t_min, t_max;
4380
4381     /* nasty degenerate cases, effectively drawing to an infinity
4382      * point (?)  cope with them here, so don't process them as a
4383      * "real" OUTRANGE point
4384
4385      * If more than one coord is -VERYLARGE, then can't ratio the
4386      * "infinities" so drop out by returning FALSE */
4387
4388     count = 0;
4389     if (ix == -VERYLARGE)
4390         count++;
4391     if (ox == -VERYLARGE)
4392         count++;
4393     if (iy == -VERYLARGE)
4394         count++;
4395     if (oy == -VERYLARGE)
4396         count++;
4397
4398     /* either doesn't pass through graph area *or* can't ratio
4399      * infinities to get a direction to draw line, so simply
4400      * return(FALSE) */
4401     if (count > 1) {
4402         return (FALSE);
4403     }
4404
4405     if (ox == -VERYLARGE || ix == -VERYLARGE) {
4406         /* Horizontal line */
4407         if (ix == -VERYLARGE) {
4408             /* swap points so ix/iy don't have a -VERYLARGE component */
4409             swap = ix;
4410             ix = ox;
4411             ox = swap;
4412             swap = iy;
4413             iy = oy;
4414             oy = swap;
4415         }
4416         /* check actually passes through the graph area */
4417         if (ix > GPMAX(X_AXIS.max, X_AXIS.min)
4418             && inrange(iy, Y_AXIS.min, Y_AXIS.max)) {
4419             lx[0] = X_AXIS.min;
4420             ly[0] = iy;
4421
4422             lx[1] = X_AXIS.max;
4423             ly[1] = iy;
4424             return (TRUE);
4425         } else {
4426             return (FALSE);
4427         }
4428     }
4429     if (oy == -VERYLARGE || iy == -VERYLARGE) {
4430         /* Vertical line */
4431         if (iy == -VERYLARGE) {
4432             /* swap points so ix/iy don't have a -VERYLARGE component */
4433             swap = ix;
4434             ix = ox;
4435             ox = swap;
4436             swap = iy;
4437             iy = oy;
4438             oy = swap;
4439         }
4440         /* check actually passes through the graph area */
4441         if (iy > GPMAX(Y_AXIS.min, Y_AXIS.max)
4442             && inrange(ix, X_AXIS.min, X_AXIS.max)) {
4443             lx[0] = ix;
4444             ly[0] = Y_AXIS.min;
4445
4446             lx[1] = ix;
4447             ly[1] = Y_AXIS.max;
4448             return (TRUE);
4449         } else {
4450             return (FALSE);
4451         }
4452     }
4453     /*
4454      * Special horizontal/vertical, etc. cases are checked and remaining
4455      * slant lines are checked separately.
4456      *
4457      * The slant line intersections are solved using the parametric form
4458      * of the equation for a line, since if we test x/y min/max planes explicitly
4459      * then e.g. a  line passing through a corner point (X_AXIS.min,Y_AXIS.min)
4460      * actually intersects 2 planes and hence further tests would be required
4461      * to anticipate this and similar situations.
4462      */
4463
4464     /*
4465      * Can have case (ix == ox && iy == oy) as both points OUTRANGE
4466      */
4467     if (ix == ox && iy == oy) {
4468         /* but as only define single outrange point, can't intersect graph area */
4469         return (FALSE);
4470     }
4471     if (ix == ox) {
4472         /* line parallel to y axis */
4473
4474         /* x coord must be in range, and line must span both Y_AXIS.min and Y_AXIS.max */
4475         /* note that spanning Y_AXIS.min implies spanning Y_AXIS.max, as both points OUTRANGE */
4476         if (!inrange(ix, X_AXIS.min, X_AXIS.max)) {
4477             return (FALSE);
4478         }
4479         if (inrange(Y_AXIS.min, iy, oy)) {
4480             lx[0] = ix;
4481             ly[0] = Y_AXIS.min;
4482
4483             lx[1] = ix;
4484             ly[1] = Y_AXIS.max;
4485             return (TRUE);
4486         } else
4487             return (FALSE);
4488     }
4489     if (iy == oy) {
4490         /* already checked case (ix == ox && iy == oy) */
4491
4492         /* line parallel to x axis */
4493         /* y coord must be in range, and line must span both X_AXIS.min and X_AXIS.max */
4494         /* note that spanning X_AXIS.min implies spanning X_AXIS.max, as both points OUTRANGE */
4495         if (!inrange(iy, Y_AXIS.min, Y_AXIS.max)) {
4496             return (FALSE);
4497         }
4498         if (inrange(X_AXIS.min, ix, ox)) {
4499             lx[0] = X_AXIS.min;
4500             ly[0] = iy;
4501
4502             lx[1] = X_AXIS.max;
4503             ly[1] = iy;
4504             return (TRUE);
4505         } else
4506             return (FALSE);
4507     }
4508     /* nasty 2D slanted line in an xy plane */
4509
4510     /* From here on, it's essentially the classical Cyrus-Beck, or
4511      * Liang-Barsky algorithm for line clipping to a rectangle */
4512     /*
4513        Solve parametric equation
4514
4515        (ix, iy) + t (diff_x, diff_y)
4516
4517        where 0.0 <= t <= 1.0 and
4518
4519        diff_x = (ox - ix);
4520        diff_y = (oy - iy);
4521      */
4522
4523     t[0] = (X_AXIS.min - ix) / (ox - ix);
4524     t[1] = (X_AXIS.max - ix) / (ox - ix);
4525     if (t[0] > t[1]) {
4526         swap = t[0];
4527         t[0] = t[1];
4528         t[1] = swap;
4529     }
4530
4531     t[2] = (Y_AXIS.min - iy) / (oy - iy);
4532     t[3] = (Y_AXIS.max - iy) / (oy - iy);
4533     if (t[2] > t[3]) {
4534         swap = t[2];
4535         t[2] = t[3];
4536         t[3] = swap;
4537     }
4538
4539     t_min = GPMAX(GPMAX(t[0], t[2]), 0.0);
4540     t_max = GPMIN(GPMIN(t[1], t[3]), 1.0);
4541
4542     if (t_min > t_max)
4543         return (FALSE);
4544
4545     lx[0] = ix + t_min * (ox - ix);
4546     ly[0] = iy + t_min * (oy - iy);
4547
4548     lx[1] = ix + t_max * (ox - ix);
4549     ly[1] = iy + t_max * (oy - iy);
4550
4551     /*
4552      * Can only have 0 or 2 intersection points -- only need test one coord
4553      */
4554     /* FIXME: this is UGLY. Need an 'almost_inrange()' function */
4555     if (inrange(lx[0],
4556                 (X_AXIS.min - 1e-5 * (X_AXIS.max - X_AXIS.min)),
4557                 (X_AXIS.max + 1e-5 * (X_AXIS.max - X_AXIS.min)))
4558         && inrange(ly[0],
4559                    (Y_AXIS.min - 1e-5 * (Y_AXIS.max - Y_AXIS.min)),
4560                    (Y_AXIS.max + 1e-5 * (Y_AXIS.max - Y_AXIS.min))))
4561     {
4562
4563         return (TRUE);
4564     }
4565     return (FALSE);
4566 }
4567
4568
4569 /* EAM April 2004 - If the line segment crosses a bounding line we will
4570  * interpolate an extra corner and split the filled polygon into two.
4571  */
4572 static TBOOLEAN
4573 bound_intersect(
4574 struct coordinate GPHUGE *points,
4575 int i,                          /* line segment from point i-1 to point i */
4576 double *ex, double *ey,         /* the point where it crosses a boundary */
4577 filledcurves_opts *filledcurves_options)
4578 {
4579     double dx1, dx2, dy1, dy2;
4580
4581     /* If there are no bounding lines in effect, don't bother */
4582     if (!filledcurves_options->oneside)
4583         return FALSE;
4584
4585     switch (filledcurves_options->closeto) {
4586         case FILLEDCURVES_ATX1:
4587         case FILLEDCURVES_ATX2:
4588             dx1 = filledcurves_options->at - points[i-1].x;
4589             dx2 = filledcurves_options->at - points[i].x;
4590             dy1 = points[i].y - points[i-1].y;
4591             if (dx1*dx2 < 0) {
4592                 *ex = filledcurves_options->at;
4593                 *ey = points[i-1].y + dy1 * dx1 / (dx1-dx2);
4594                 return TRUE;
4595             }
4596             break;
4597         case FILLEDCURVES_ATY1:
4598         case FILLEDCURVES_ATY2:
4599             dy1 = filledcurves_options->at - points[i-1].y;
4600             dy2 = filledcurves_options->at - points[i].y;
4601             dx1 = points[i].x - points[i-1].x;
4602             if (dy1*dy2 < 0) {
4603                 *ex = points[i-1].x + dx1 * dy1 / (dy1-dy2);
4604                 *ey = filledcurves_options->at;
4605                 return TRUE;
4606             }
4607             break;
4608         case FILLEDCURVES_ATXY:
4609         default:
4610             break;
4611     }
4612
4613     return FALSE;
4614 }
4615
4616
4617 /* HBB 20010118: all the *_callback() functions made non-static. This
4618  * is necessary to work around a bug in HP's assembler shipped with
4619  * HP-UX 10 and higher, if GCC tries to use it */
4620
4621 /* display a x-axis ticmark - called by gen_ticks */
4622 /* also uses global tic_start, tic_direction, tic_text and tic_just */
4623 void
4624 xtick2d_callback(
4625     AXIS_INDEX axis,
4626     double place,
4627     char *text,
4628     struct lp_style_type grid)  /* linetype or -2 for no grid */
4629 {
4630     struct termentry *t = term;
4631     /* minitick if text is NULL - beware - h_tic is unsigned */
4632     int ticsize = tic_direction * (int) t->v_tic * (text ? axis_array[axis].ticscale : axis_array[axis].miniticscale);
4633     unsigned int x = map_x(place);
4634
4635     (void) axis;                /* avoid "unused parameter" warning */
4636
4637     if (grid.l_type > LT_NODRAW) {
4638         term_apply_lp_properties(&grid);
4639         if (polar_grid_angle) {
4640             double x = place, y = 0, s = sin(0.1), c = cos(0.1);
4641             int i;
4642             int ogx = map_x(x);
4643             int ogy = map_y(0);
4644             int tmpgx, tmpgy, gx, gy;
4645
4646             if (place > largest_polar_circle)
4647                 largest_polar_circle = place;
4648             else if (-place > largest_polar_circle)
4649                 largest_polar_circle = -place;
4650             for (i = 1; i <= 63 /* 2pi/0.1 */ ; ++i) {
4651                 {
4652                     /* cos(t+dt) = cos(t)cos(dt)-sin(t)cos(dt) */
4653                     double tx = x * c - y * s;
4654                     /* sin(t+dt) = sin(t)cos(dt)+cos(t)sin(dt) */
4655                     y = y * c + x * s;
4656                     x = tx;
4657                 }
4658                 tmpgx = gx = map_x(x);
4659                 tmpgy = gy = map_y(y);
4660                 if (clip_line(&ogx, &ogy, &tmpgx, &tmpgy)) {
4661                     (*t->move) ((unsigned int) ogx, (unsigned int) ogy);
4662                     (*t->vector) ((unsigned int) tmpgx, (unsigned int) tmpgy);
4663                 }
4664                 ogx = gx;
4665                 ogy = gy;
4666             }
4667         } else {
4668             if (lkey && x < keybox.xr && x > keybox.xl
4669             &&  keybox.yt > plot_bounds.ybot && keybox.yb < plot_bounds.ytop) {
4670                 if (keybox.yb > plot_bounds.ybot) {
4671                     (*t->move) (x, plot_bounds.ybot);
4672                     (*t->vector) (x, keybox.yb);
4673                 }
4674                 if (keybox.yt < plot_bounds.ytop) {
4675                     (*t->move) (x, keybox.yt);
4676                     (*t->vector) (x, plot_bounds.ytop);
4677                 }
4678             } else {
4679                 (*t->move) (x, plot_bounds.ybot);
4680                 (*t->vector) (x, plot_bounds.ytop);
4681             }
4682         }
4683         term_apply_lp_properties(&border_lp);   /* border linetype */
4684     }
4685     /* we precomputed tic posn and text posn in global vars */
4686
4687     (*t->move) (x, tic_start);
4688     (*t->vector) (x, tic_start + ticsize);
4689
4690     if (tic_mirror >= 0) {
4691         (*t->move) (x, tic_mirror);
4692         (*t->vector) (x, tic_mirror - ticsize);
4693     }
4694     if (text) {
4695         /* get offset */
4696         double offsetx_d, offsety_d;
4697         map_position_r(&(axis_array[axis].ticdef.offset),
4698                        &offsetx_d, &offsety_d, "xtics");
4699         /* User-specified different color for the tics text */
4700         if (axis_array[axis].ticdef.textcolor.type != TC_DEFAULT)
4701             apply_pm3dcolor(&(axis_array[axis].ticdef.textcolor), t);
4702         write_multiline(x+(int)offsetx_d, tic_text+(int)offsety_d, text,
4703                         tic_hjust, tic_vjust, rotate_tics,
4704                         axis_array[axis].ticdef.font);
4705         term_apply_lp_properties(&border_lp);   /* reset to border linetype */
4706     }
4707 }
4708
4709 /* display a y-axis ticmark - called by gen_ticks */
4710 /* also uses global tic_start, tic_direction, tic_text and tic_just */
4711 void
4712 ytick2d_callback(
4713     AXIS_INDEX axis,
4714     double place,
4715     char *text,
4716     struct lp_style_type grid)  /* linetype or -2 */
4717 {
4718     struct termentry *t = term;
4719     /* minitick if text is NULL - v_tic is unsigned */
4720     int ticsize = tic_direction * (int) t->h_tic * (text ? axis_array[axis].ticscale : axis_array[axis].miniticscale);
4721     unsigned int y = map_y(place);
4722
4723     (void) axis;                /* avoid "unused parameter" warning */
4724
4725     if (grid.l_type > LT_NODRAW) {
4726         term_apply_lp_properties(&grid);
4727         if (polar_grid_angle) {
4728             double x = 0, y = place, s = sin(0.1), c = cos(0.1);
4729             int i;
4730             if (place > largest_polar_circle)
4731                 largest_polar_circle = place;
4732             else if (-place > largest_polar_circle)
4733                 largest_polar_circle = -place;
4734             clip_move(map_x(x), map_y(y));
4735             for (i = 1; i <= 63 /* 2pi/0.1 */ ; ++i) {
4736                 {
4737                     /* cos(t+dt) = cos(t)cos(dt)-sin(t)cos(dt) */
4738                     double tx = x * c - y * s;
4739                     /* sin(t+dt) = sin(t)cos(dt)+cos(t)sin(dt) */
4740                     y = y * c + x * s;
4741                     x = tx;
4742                 }
4743                 clip_vector(map_x(x), map_y(y));
4744             }
4745         } else {
4746             /* Make the grid avoid the key box */
4747             if (lkey && y < keybox.yt && y > keybox.yb
4748             &&  keybox.xl < plot_bounds.xright && keybox.xr > plot_bounds.xleft) {
4749                 if (keybox.xl > plot_bounds.xleft) {
4750                     (*t->move) (plot_bounds.xleft, y);
4751                     (*t->vector) (keybox.xl, y);
4752                 }
4753                 if (keybox.xr < plot_bounds.xright) {
4754                     (*t->move) (keybox.xr, y);
4755                     (*t->vector) (plot_bounds.xright, y);
4756                 }
4757             } else {
4758                 (*t->move) (plot_bounds.xleft, y);
4759                 (*t->vector) (plot_bounds.xright, y);
4760             }
4761         }
4762         term_apply_lp_properties(&border_lp);   /* border linetype */
4763     }
4764     /* we precomputed tic posn and text posn */
4765
4766     (*t->move) (tic_start, y);
4767     (*t->vector) (tic_start + ticsize, y);
4768
4769     if (tic_mirror >= 0) {
4770         (*t->move) (tic_mirror, y);
4771         (*t->vector) (tic_mirror - ticsize, y);
4772     }
4773     if (text) {
4774         /* get offset */
4775         double offsetx_d, offsety_d;
4776         map_position_r(&(axis_array[axis].ticdef.offset),
4777                        &offsetx_d, &offsety_d, "ytics");
4778         /* User-specified different color for the tics text */
4779         if (axis_array[axis].ticdef.textcolor.type != TC_DEFAULT)
4780             apply_pm3dcolor(&(axis_array[axis].ticdef.textcolor), t);
4781         write_multiline(tic_text+(int)offsetx_d, y+(int)offsety_d, text,
4782                         tic_hjust, tic_vjust, rotate_tics,
4783                         axis_array[axis].ticdef.font);
4784         term_apply_lp_properties(&border_lp);   /* reset to border linetype */
4785     }
4786 }
4787
4788 /* STR points to a label string, possibly with several lines separated
4789    by \n.  Return the number of characters in the longest line.  If
4790    LINES is not NULL, set *LINES to the number of lines in the
4791    label. */
4792 int
4793 label_width(const char *str, int *lines)
4794 {
4795     char *lab = NULL, *s, *e;
4796     int mlen, len, l;
4797
4798     l = mlen = len = 0;
4799     lab = gp_alloc(strlen(str) + 2, "in label_width");
4800     strcpy(lab, str);
4801     strcat(lab, "\n");
4802     s = lab;
4803     while ((e = (char *) strchr(s, '\n')) != NULL) {
4804         *e = '\0';
4805         len = estimate_strlen(s);       /* = e-s ? */
4806         if (len > mlen)
4807             mlen = len;
4808         if (len || l || *str == '\n')
4809             l++;
4810         s = ++e;
4811     }
4812     /* lines = NULL => not interested - div */
4813     if (lines)
4814         *lines = l;
4815
4816     free(lab);
4817     return (mlen);
4818 }
4819
4820
4821 /*{{{  map_position, wrapper, which maps double to int */
4822 void
4823 map_position(
4824     struct position *pos,
4825     int *x, int *y,
4826     const char *what)
4827 {
4828     double xx, yy;
4829     map_position_double(pos, &xx, &yy, what);
4830     *x = xx;
4831     *y = yy;
4832 }
4833
4834 /*}}} */
4835
4836 /*{{{  map_position_double */
4837 static void
4838 map_position_double(
4839     struct position *pos,
4840     double *x, double *y,
4841     const char *what)
4842 {
4843     switch (pos->scalex) {
4844     case first_axes:
4845         {
4846             double xx = axis_log_value_checked(FIRST_X_AXIS, pos->x, what);
4847             *x = AXIS_MAP(FIRST_X_AXIS, xx);
4848             break;
4849         }
4850     case second_axes:
4851         {
4852             double xx = axis_log_value_checked(SECOND_X_AXIS, pos->x, what);
4853             *x = AXIS_MAP(SECOND_X_AXIS, xx);
4854             break;
4855         }
4856     case graph:
4857         {
4858             *x = plot_bounds.xleft + pos->x * (plot_bounds.xright - plot_bounds.xleft);
4859             break;
4860         }
4861     case screen:
4862         {
4863             struct termentry *t = term;
4864             /* HBB 20000914: Off-by-one bug. Max. allowable result is
4865              * t->xmax - 1, not t->xmax ! */
4866             *x = pos->x * (t->xmax - 1);
4867             break;
4868         }
4869     case character:
4870         {
4871             register struct termentry *t = term;
4872             *x = pos->x * t->h_char;
4873             break;
4874         }
4875     }
4876     switch (pos->scaley) {
4877     case first_axes:
4878         {
4879             double yy = axis_log_value_checked(FIRST_Y_AXIS, pos->y, what);
4880             *y = AXIS_MAP(FIRST_Y_AXIS, yy);
4881             break;
4882         }
4883     case second_axes:
4884         {
4885             double yy = axis_log_value_checked(SECOND_Y_AXIS, pos->y, what);
4886             *y = AXIS_MAP(SECOND_Y_AXIS, yy);
4887             break;
4888         }
4889     case graph:
4890         {
4891             *y = plot_bounds.ybot + pos->y * (plot_bounds.ytop - plot_bounds.ybot);
4892             break;
4893         }
4894     case screen:
4895         {
4896             struct termentry *t = term;
4897             /* HBB 20000914: Off-by-one bug. Max. allowable result is
4898              * t->ymax - 1, not t->ymax ! */
4899             *y = pos->y * (t->ymax -1);
4900             break;
4901         }
4902     case character:
4903         {
4904             register struct termentry *t = term;
4905             *y = pos->y * t->v_char;
4906             break;
4907         }
4908     }
4909     *x += 0.5;
4910     *y += 0.5;
4911 }
4912
4913 /*}}} */
4914
4915 /*{{{  map_position_r */
4916 void
4917 map_position_r(
4918     struct position *pos,
4919     double *x, double *y,
4920     const char *what)
4921 {
4922     switch (pos->scalex) {
4923     case first_axes:
4924         {
4925             double xx = axis_log_value_checked(FIRST_X_AXIS, pos->x, what);
4926             *x = xx * axis_array[FIRST_X_AXIS].term_scale;
4927             break;
4928         }
4929     case second_axes:
4930         {
4931             double xx = axis_log_value_checked(SECOND_X_AXIS, pos->x, what);
4932             *x = xx * axis_array[SECOND_X_AXIS].term_scale;
4933             break;
4934         }
4935     case graph:
4936         {
4937             *x = pos->x * (plot_bounds.xright - plot_bounds.xleft);
4938             break;
4939         }
4940     case screen:
4941         {
4942             struct termentry *t = term;
4943             *x = pos->x * (t->xmax - 1);
4944             break;
4945         }
4946     case character:
4947         {
4948             register struct termentry *t = term;
4949             *x = pos->x * t->h_char;
4950             break;
4951         }
4952     }
4953     switch (pos->scaley) {
4954     case first_axes:
4955         {
4956             double yy = axis_log_value_checked(FIRST_Y_AXIS, pos->y, what);
4957             *y = yy * axis_array[FIRST_Y_AXIS].term_scale;
4958             return;
4959         }
4960     case second_axes:
4961         {
4962             double yy = axis_log_value_checked(SECOND_Y_AXIS, pos->y, what);
4963             *y = yy * axis_array[SECOND_Y_AXIS].term_scale;
4964             return;
4965         }
4966     case graph:
4967         {
4968             *y = pos->y * (plot_bounds.ytop - plot_bounds.ybot);
4969             return;
4970         }
4971     case screen:
4972         {
4973             struct termentry *t = term;
4974             /* HBB 20000914: Off-by-one bug. Max. allowable result is
4975              * t->ymax - 1, not t->ymax ! */
4976             *y = pos->y * (t->ymax -1);
4977             return;
4978         }
4979     case character:
4980         {
4981             register struct termentry *t = term;
4982             *y = pos->y * t->v_char;
4983             break;
4984         }
4985     }
4986 }
4987 /*}}} */
4988
4989 static void
4990 plot_border()
4991 {
4992     int min, max;
4993
4994         term_apply_lp_properties(&border_lp);   /* border linetype */
4995         if (border_complete)
4996             newpath();
4997         (*term->move) (plot_bounds.xleft, plot_bounds.ytop);
4998
4999         if (border_west && axis_array[FIRST_Y_AXIS].ticdef.rangelimited) {
5000                 max = AXIS_MAP(FIRST_Y_AXIS,axis_array[FIRST_Y_AXIS].data_max);
5001                 min = AXIS_MAP(FIRST_Y_AXIS,axis_array[FIRST_Y_AXIS].data_min);
5002                 (*term->move) (plot_bounds.xleft, max);
5003                 (*term->vector) (plot_bounds.xleft, min);
5004                 (*term->move) (plot_bounds.xleft, plot_bounds.ybot);
5005         } else if (border_west) {
5006             (*term->vector) (plot_bounds.xleft, plot_bounds.ybot);
5007         } else {
5008             (*term->move) (plot_bounds.xleft, plot_bounds.ybot);
5009         }
5010
5011         if (border_south && axis_array[FIRST_X_AXIS].ticdef.rangelimited) {
5012                 max = AXIS_MAP(FIRST_X_AXIS,axis_array[FIRST_X_AXIS].data_max);
5013                 min = AXIS_MAP(FIRST_X_AXIS,axis_array[FIRST_X_AXIS].data_min);
5014                 (*term->move) (min, plot_bounds.ybot);
5015                 (*term->vector) (max, plot_bounds.ybot);
5016                 (*term->move) (plot_bounds.xright, plot_bounds.ybot);
5017         } else if (border_south) {
5018             (*term->vector) (plot_bounds.xright, plot_bounds.ybot);
5019         } else {
5020             (*term->move) (plot_bounds.xright, plot_bounds.ybot);
5021         }
5022
5023         if (border_east && axis_array[SECOND_Y_AXIS].ticdef.rangelimited) {
5024                 max = AXIS_MAP(SECOND_Y_AXIS,axis_array[SECOND_Y_AXIS].data_max);
5025                 min = AXIS_MAP(SECOND_Y_AXIS,axis_array[SECOND_Y_AXIS].data_min);
5026                 (*term->move) (plot_bounds.xright, max);
5027                 (*term->vector) (plot_bounds.xright, min);
5028                 (*term->move) (plot_bounds.xright, plot_bounds.ybot);
5029         } else if (border_east) {
5030             (*term->vector) (plot_bounds.xright, plot_bounds.ytop);
5031         } else {
5032             (*term->move) (plot_bounds.xright, plot_bounds.ytop);
5033         }
5034
5035         if (border_north && axis_array[SECOND_X_AXIS].ticdef.rangelimited) {
5036                 max = AXIS_MAP(SECOND_X_AXIS,axis_array[SECOND_X_AXIS].data_max);
5037                 min = AXIS_MAP(SECOND_X_AXIS,axis_array[SECOND_X_AXIS].data_min);
5038                 (*term->move) (min, plot_bounds.ytop);
5039                 (*term->vector) (max, plot_bounds.ytop);
5040                 (*term->move) (plot_bounds.xright, plot_bounds.ytop);
5041         } else if (border_north) {
5042             (*term->vector) (plot_bounds.xleft, plot_bounds.ytop);
5043         } else {
5044             (*term->move) (plot_bounds.xleft, plot_bounds.ytop);
5045         }
5046
5047         if (border_complete)
5048             closepath();
5049 }
5050
5051
5052 #ifdef EAM_HISTOGRAMS
5053 void
5054 init_histogram(struct histogram_style *histogram, char *title)
5055 {
5056     if (stackheight)
5057         free(stackheight);
5058     stackheight = NULL;
5059     if (histogram) {
5060         memcpy(histogram,&histogram_opts,sizeof(histogram_opts));
5061         memset(&(histogram->title), 0, sizeof(text_label));
5062         /* Insert in linked list */
5063         histogram_opts.next = histogram;
5064         histogram->title.text = title;
5065     }
5066 }
5067
5068 void
5069 free_histlist(struct histogram_style *hist)
5070 {
5071     if (!hist)
5072         return;
5073     if (hist->title.text)
5074         free(hist->title.text);
5075     if (hist->next) {
5076         free_histlist(hist->next);
5077         free(hist->next);
5078         hist->next = NULL;
5079     }
5080 }
5081
5082 static void
5083 place_histogram_titles()
5084 {
5085     histogram_style *hist = &histogram_opts;
5086     unsigned int x, y;
5087     while ((hist = hist->next)) {
5088         if (hist->title.text && *(hist->title.text)) {
5089             double xoffset_d, yoffset_d;
5090             map_position_r(&(histogram_opts.title.offset), &xoffset_d, &yoffset_d,
5091                            "histogram");
5092             x = map_x((hist->start + hist->end) / 2.);
5093             y = xlabel_y;
5094             x += (int)xoffset_d;
5095             y += (int)yoffset_d + 0.25 * term->v_char;
5096             apply_pm3dcolor(&hist->title.textcolor,term);
5097             write_multiline(x, y, hist->title.text,
5098                             CENTRE, JUST_BOT, 0, hist->title.font);
5099             reset_textcolor(&hist->title.textcolor,term);
5100         }
5101     }
5102 }
5103
5104 #endif
5105
5106 /*
5107  * Make this code a subroutine, rather than in-line, so that it can
5108  * eventually be shared by other callers. It would be nice to share it
5109  * with the 3d code also, but as of now the two code sections are not
5110  * very parallel.  EAM Nov 2003
5111  */
5112
5113 static void
5114 do_key_sample(
5115     struct curve_points *this_plot,
5116     legend_key *key,
5117     char *title,
5118     struct termentry *t,
5119     int xl, int yl)
5120 {
5121     /* Clip key box against canvas */
5122     BoundingBox *clip_save = clip_area;
5123     if (term->flags & TERM_CAN_CLIP)
5124         clip_area = NULL;
5125     else
5126         clip_area = &canvas;
5127
5128     /* Draw key text in black */
5129     (*t->linetype)(LT_BLACK);
5130
5131     if (key->just == GPKEY_LEFT) {
5132         write_multiline(xl + key_text_left, yl, title, LEFT, JUST_TOP, 0, NULL);
5133     } else {
5134         if ((*t->justify_text) (RIGHT)) {
5135             write_multiline(xl + key_text_right, yl, title, RIGHT, JUST_TOP, 0, NULL);
5136         } else {
5137             int x = xl + key_text_right - t->h_char * estimate_strlen(title);
5138             if (key->region == GPKEY_AUTO_EXTERIOR_LRTBC ||     /* HBB 990327 */
5139                 key->region == GPKEY_AUTO_EXTERIOR_MARGIN ||
5140                 i_inrange(x, plot_bounds.xleft, plot_bounds.xright))
5141                 write_multiline(x, yl, title, LEFT, JUST_TOP, 0, NULL);
5142         }
5143     }
5144
5145     /* Draw sample in same style and color as the corresponding plot */
5146     (*t->linetype)(this_plot->lp_properties.l_type);
5147     if (this_plot->lp_properties.use_palette)
5148         apply_pm3dcolor(&this_plot->lp_properties.pm3d_color,t);
5149
5150     /* draw sample depending on bits set in plot_style */
5151     if (this_plot->plot_style & PLOT_STYLE_HAS_FILL
5152         && t->fillbox) {
5153         struct fill_style_type *fs = &this_plot->fill_properties;
5154         int style = style_from_fill(fs);
5155         unsigned int x = xl + key_sample_left;
5156         unsigned int y = yl - key_entry_height/4;
5157         unsigned int w = key_sample_right - key_sample_left;
5158         unsigned int h = key_entry_height/2;
5159
5160         if (w > 0) {
5161             if (style != FS_EMPTY) {
5162                 if (this_plot->lp_properties.use_palette && t->filled_polygon)
5163                     (*t->filled_polygon)(4, fill_corners(style,x,y,w,h));
5164                 else
5165                     (*t->fillbox)(style,x,y,w,h);
5166             }
5167
5168             if (fs->fillstyle != FS_EMPTY && fs->border_linetype != LT_UNDEFINED)
5169                 (*t->linetype)(fs->border_linetype);
5170             if (fs->border_linetype != LT_NODRAW) {
5171                 newpath();
5172                 draw_clip_line( xl + key_sample_left,  yl - key_entry_height/4,
5173                             xl + key_sample_right, yl - key_entry_height/4);
5174                 draw_clip_line( xl + key_sample_right, yl - key_entry_height/4,
5175                             xl + key_sample_right, yl + key_entry_height/4);
5176                 draw_clip_line( xl + key_sample_right, yl + key_entry_height/4,
5177                             xl + key_sample_left,  yl + key_entry_height/4);
5178                 draw_clip_line( xl + key_sample_left,  yl + key_entry_height/4,
5179                             xl + key_sample_left,  yl - key_entry_height/4);
5180                 closepath();
5181             }
5182             if (fs->fillstyle != FS_EMPTY && fs->border_linetype != LT_UNDEFINED) {
5183                 (*t->linetype)(this_plot->lp_properties.l_type);
5184                 if (this_plot->lp_properties.use_palette)
5185                     apply_pm3dcolor(&this_plot->lp_properties.pm3d_color,t);
5186             }
5187         }
5188
5189     } else if (this_plot->plot_style == VECTOR && t->arrow) {
5190             apply_head_properties(&(this_plot->arrow_properties));
5191             curr_arrow_headlength = -1;
5192             draw_clip_arrow(xl + key_sample_left, yl, xl + key_sample_right, yl,
5193                         this_plot->arrow_properties.head);
5194
5195     } else if ((this_plot->plot_style & PLOT_STYLE_HAS_LINE)
5196                    || ((this_plot->plot_style & PLOT_STYLE_HAS_ERRORBAR)
5197                        && this_plot->plot_type == DATA)) {
5198             /* errors for data plots only */
5199             draw_clip_line(xl + key_sample_left, yl, xl + key_sample_right, yl);
5200     }
5201
5202     if ((this_plot->plot_type == DATA)
5203         && (this_plot->plot_style & PLOT_STYLE_HAS_ERRORBAR)
5204         && (this_plot->plot_style != CANDLESTICKS)
5205         && (bar_size > 0.0)) {
5206         draw_clip_line( xl + key_sample_left, yl + ERRORBARTIC,
5207                         xl + key_sample_left, yl - ERRORBARTIC);
5208         draw_clip_line( xl + key_sample_right, yl + ERRORBARTIC,
5209                         xl + key_sample_right, yl - ERRORBARTIC);
5210     }
5211
5212     /* oops - doing the point sample now would break the postscript
5213      * terminal for example, which changes current line style
5214      * when drawing a point, but does not restore it. We must wait
5215      then draw the point sample at the end of do_plot (line 1625)
5216      */
5217
5218     /* Restore previous clipping area */
5219     clip_area = clip_save;
5220 }
5221
5222 /* Squeeze all fill information into the old style parameter.
5223  * The terminal drivers know how to extract the information.
5224  * We assume that the style (int) has only 16 bit, therefore we take
5225  * 4 bits for the style and allow 12 bits for the corresponding fill parameter.
5226  * This limits the number of styles to 16 and the fill parameter's
5227  * values to the range 0...4095, which seems acceptable.
5228  */
5229 static int
5230 style_from_fill(struct fill_style_type *fs)
5231 {
5232     int fillpar, style;
5233
5234     switch( fs->fillstyle ) {
5235     case FS_SOLID:
5236         fillpar = fs->filldensity;
5237         style = ((fillpar & 0xfff) << 4) + FS_SOLID;
5238         break;
5239     case FS_PATTERN:
5240         fillpar = fs->fillpattern;
5241         style = ((fillpar & 0xfff) << 4) + FS_PATTERN;
5242         break;
5243     default:
5244         /* solid fill with background color */
5245         style = FS_EMPTY;
5246         break;
5247     }
5248
5249     return style;
5250 }
5251
5252
5253 /*
5254  * The equivalent of t->fillbox() except that it uses PM3D colors instead
5255  * of plain line types
5256  */
5257 static gpiPoint *
5258 fill_corners(int style, unsigned int x, unsigned int y, unsigned int w, unsigned int h)
5259 {
5260     static gpiPoint corner[4];
5261
5262     corner[0].style = style;
5263     corner[0].x = x;
5264     corner[0].y = y;
5265     corner[1].x = x;
5266     corner[1].y = y+h;
5267     corner[2].x = x+w;
5268     corner[2].y = y+h;
5269     corner[3].x = x+w;
5270     corner[3].y = y;
5271
5272     return corner;
5273 }
5274
5275
5276 static TBOOLEAN
5277 check_for_variable_color(struct curve_points *plot, struct coordinate *point)
5278 {
5279     if ((plot->lp_properties.pm3d_color.value < 0.0)
5280     &&  (plot->lp_properties.pm3d_color.type == TC_RGB)) {
5281         set_rgbcolor(point->yhigh);
5282         return TRUE;
5283     } else if (plot->lp_properties.pm3d_color.type == TC_Z) {
5284         set_color( cb2gray(point->yhigh) );
5285         return TRUE;
5286     } else if (plot->lp_properties.l_type == LT_COLORFROMCOLUMN) {
5287         lp_style_type lptmp;
5288         lp_use_properties(&lptmp, (int)(point->yhigh), FALSE);
5289         apply_pm3dcolor(&(lptmp.pm3d_color), term);
5290         return TRUE;
5291     } else
5292         return FALSE;
5293 }
5294             
5295 #ifdef WITH_IMAGE
5296
5297 /* Similar to HBB's comment above, this routine is shared with
5298  * graph3d.c, so it shouldn't be in this module (graphics.c).
5299  * However, I feel that 2d and 3d graphing routines should be
5300  * made as much in common as possible.  They seem to be
5301  * bifurcating a bit too much.  (Dan Sebald)
5302  */
5303 #include "util3d.h"
5304
5305 /* These might work better as fuctions, but defines will do for now. */
5306 #define ERROR_NOTICE(str)         "\nGNUPLOT (plot_image):  " str
5307 #define ERROR_NOTICE_NEWLINE(str) "\n                       " str
5308
5309 /* hyperplane_between_points:
5310  * Compute the hyperplane representation of a line passing
5311  *  between two points.
5312  */
5313 void
5314 hyperplane_between_points(double *p1, double *p2, double *w, double *b)
5315 {
5316     w[0] = p1[1] - p2[1];
5317     w[1] = p2[0] - p1[0];
5318     *b = -(w[0]*p1[0] + w[1]*p1[1]);
5319 }
5320
5321 /* plot_image_or_update_axes:
5322  * Plot the coordinates similar to the points option except use
5323  *  pixels.  Check if the data forms a valid image array, i.e.,
5324  *  one for which points are spaced equidistant along two non-
5325  *  coincidence vectors.  If the two directions are orthogonal
5326  *  within some tolerance and they are aligned with the view
5327  *  box x and y directions, then use the image feature of the
5328  *  terminal if it has one.  Otherwise, use parallelograms via
5329  *  the polynomial function.  If it just necessary to update
5330  *  the axis ranges for `set autoscale`, do so and then return.
5331  */
5332 void
5333 plot_image_or_update_axes(void *plot, t_imagecolor pixel_planes, TBOOLEAN project_points, TBOOLEAN update_axes)
5334 {
5335
5336     struct coordinate GPHUGE *points;
5337     int p_count;
5338     int i;
5339     double w_hyp[2], b_hyp;                    /* Hyperlane vector and constant */
5340     double p_start_corner[2], p_end_corner[2]; /* Points used for computing hyperplane. */
5341     unsigned int K = 0, L = 0;                 /* Dimensions of image grid. K = <scan line length>, L = <number of scan lines>. */
5342     double p_mid_corner[2];                    /* Point representing first corner found, i.e. p(K-1) */
5343     double delta_x_grid[2] = {0, 0};           /* Spacings between points, two non-orthogonal directions. */
5344     double delta_y_grid[2] = {0, 0};
5345     int grid_corner[4] = {-1, -1, -1, -1};     /* The corner pixels of the image. */
5346     double view_port_x[2];                     /* Viewable portion of the image. */
5347     double view_port_y[2];
5348     double view_port_z[2] = {0,0};
5349
5350
5351     if (project_points) {
5352         points = ((struct surface_points *)plot)->iso_crvs->points;
5353         p_count = ((struct surface_points *)plot)->iso_crvs->p_count;
5354     } else {
5355         points = ((struct curve_points *)plot)->points;
5356         p_count = ((struct curve_points *)plot)->p_count;
5357     }
5358
5359     if (p_count < 1) {
5360         fprintf(stderr, ERROR_NOTICE("No points (visible or invisible) to plot.\n\n"));
5361         return;
5362     }
5363
5364     if (p_count < 4) {
5365         fprintf(stderr, ERROR_NOTICE("Image grid must be at least 4 points (2 x 2).\n\n"));
5366         return;
5367     }
5368
5369     /* Check if the pixel data forms a valid rectangular grid for potential image
5370      * matrix support.  A general grid orientation is considered.  If the grid
5371      * points are orthogonal and oriented along the x/y dimensions the terminal
5372      * function for images will be used.  Otherwise, the terminal function for
5373      * filled polygons are used to construct parallelograms for the pixel elements.
5374      */
5375
5376     /* Compute the hyperplane representation of the cross diagonal from
5377      * the very first point of the scan to the very last point of the
5378      * scan.
5379      */
5380     if (project_points) {
5381         map3d_xy_double(points[0].x, points[0].y, points[0].z, &p_start_corner[0], &p_start_corner[1]);
5382         map3d_xy_double(points[p_count-1].x, points[p_count-1].y, points[p_count-1].z, &p_end_corner[0], &p_end_corner[1]);
5383     } else {
5384         p_start_corner[0] = points[0].x;
5385         p_start_corner[1] = points[0].y;
5386         p_end_corner[0] = points[p_count-1].x;
5387         p_end_corner[1] = points[p_count-1].y;
5388     }
5389
5390     hyperplane_between_points(p_start_corner, p_end_corner, w_hyp, &b_hyp);
5391
5392     for (K = p_count, i=1; i < p_count; i++) {
5393         double p[2];
5394         if (project_points) {
5395             map3d_xy_double(points[i].x, points[i].y, points[i].z, &p[0], &p[1]);
5396         } else {
5397             p[0] = points[i].x;
5398             p[1] = points[i].y;
5399         }
5400         if (i == 1) {
5401             /* Determine what side (sign) of the hyperplane the second point is on.
5402              * If the second point is on the negative side of the plane, change
5403              * the sign of hyperplane variables.  Then any remaining points on the
5404              * first line will test positive in the hyperplane formula.  The first
5405              * point on the second line will test negative.
5406              */
5407             if ((w_hyp[0]*p[0] + w_hyp[1]*p[1] + b_hyp) < 0) {
5408                 w_hyp[0] = -w_hyp[0];
5409                 w_hyp[1] = -w_hyp[1];
5410                 b_hyp = -b_hyp;
5411             }
5412         } else {
5413             /* The first point on the opposite side of the hyperplane is the
5414              * candidate for the first point of the second scan line.
5415              */
5416             if ((w_hyp[0]*p[0] + w_hyp[1]*p[1] + b_hyp) < 0) {
5417                 K = i;
5418                 break;
5419             }
5420         }
5421     }
5422
5423     if (K == p_count) {
5424         fprintf(stderr, ERROR_NOTICE("Image grid must be at least 2 x 2.\n\n"));
5425         return;
5426     }
5427     L = p_count/K;
5428     if (((double)L) != ((double)p_count/K)) {
5429         fprintf(stderr, ERROR_NOTICE("Number of pixels cannot be factored into integers matching grid. N = %d  K = %d\n\n"), p_count, K);
5430         return;
5431     }
5432     grid_corner[0] = 0;
5433     grid_corner[1] = K-1;
5434     grid_corner[3] = p_count - 1;
5435     grid_corner[2] = p_count - K;
5436     if (project_points) {
5437         map3d_xy_double(points[K-1].x, points[K-1].y, points[K-1].z, &p_mid_corner[0], &p_mid_corner[1]);
5438     } else {
5439         p_mid_corner[0] = points[K-1].x;
5440         p_mid_corner[1] = points[K-1].y;
5441     }
5442     /* The grid spacing in one direction. */
5443     delta_x_grid[0] = (p_mid_corner[0] - p_start_corner[0])/(K-1);
5444     delta_y_grid[0] = (p_mid_corner[1] - p_start_corner[1])/(K-1);
5445     /* The grid spacing in the second direction. */
5446     delta_x_grid[1] = (p_end_corner[0] - p_mid_corner[0])/(L-1);
5447     delta_y_grid[1] = (p_end_corner[1] - p_mid_corner[1])/(L-1);
5448
5449     if (update_axes) {
5450         for (i=0; i < 4; i++) {
5451             int dummy_type = INRANGE;
5452             double x = points[grid_corner[i]].x;
5453             double y = points[grid_corner[i]].y;
5454             x -= (points[grid_corner[(5-i)%4]].x - points[grid_corner[i]].x)/(2*(K-1));
5455             y -= (points[grid_corner[(5-i)%4]].y - points[grid_corner[i]].y)/(2*(K-1));
5456             x -= (points[grid_corner[(i+2)%4]].x - points[grid_corner[i]].x)/(2*(L-1));
5457             y -= (points[grid_corner[(i+2)%4]].y - points[grid_corner[i]].y)/(2*(L-1));
5458             /* Update range and store value back into itself. */
5459             STORE_WITH_LOG_AND_UPDATE_RANGE(x, x, dummy_type, ((struct curve_points *)plot)->x_axis, NOOP, x = -VERYLARGE);
5460             STORE_WITH_LOG_AND_UPDATE_RANGE(y, y, dummy_type, ((struct curve_points *)plot)->y_axis, NOOP, y = -VERYLARGE);
5461         }
5462         return;
5463     }
5464
5465     /* Check if the pixel grid is orthogonal and oriented with axes.
5466      * If so, then can use efficient terminal image routines.
5467      */
5468     {TBOOLEAN rectangular_image = FALSE;
5469
5470 #define SHIFT_TOLERANCE 0.01
5471     if ( ( (fabs(delta_x_grid[0]) < SHIFT_TOLERANCE*fabs(delta_x_grid[1]))
5472         || (fabs(delta_x_grid[1]) < SHIFT_TOLERANCE*fabs(delta_x_grid[0])) )
5473         && ( (fabs(delta_y_grid[0]) < SHIFT_TOLERANCE*fabs(delta_y_grid[1]))
5474         || (fabs(delta_y_grid[1]) < SHIFT_TOLERANCE*fabs(delta_y_grid[0])) ) ) {
5475
5476         /* If the terminal does not have image support indicate so,
5477          * just once.  Then, use polygons to construct pixels.
5478          */
5479         if (term->image) {
5480             rectangular_image = TRUE;
5481         } else {
5482             static short no_image_support_indicated = 0;
5483             if (!no_image_support_indicated) {
5484                 fprintf(stderr,"\n\nNOTICE:  Visible pixels form rectangular grid, but\n"
5485                         "         there is no image matrix support for the\n"
5486                         "         active terminal.  Reverting to color boxes.\n\n");
5487                 no_image_support_indicated = 1;
5488             }
5489         }
5490
5491     }
5492
5493     if (make_palette() || !term->set_color) {
5494         fprintf(stderr, ERROR_NOTICE("Unable to make palette or set terminal color.\n\n"));
5495         return;
5496     }
5497
5498     view_port_x[0] = (X_AXIS.set_autoscale & AUTOSCALE_MIN) ? X_AXIS.min : X_AXIS.set_min;
5499     view_port_x[1] = (X_AXIS.set_autoscale & AUTOSCALE_MAX) ? X_AXIS.max : X_AXIS.set_max;
5500     view_port_y[0] = (Y_AXIS.set_autoscale & AUTOSCALE_MIN) ? Y_AXIS.min : Y_AXIS.set_min;
5501     view_port_y[1] = (Y_AXIS.set_autoscale & AUTOSCALE_MAX) ? Y_AXIS.max : Y_AXIS.set_max;
5502     if (project_points) {
5503         view_port_z[0] = (Z_AXIS.set_autoscale & AUTOSCALE_MIN) ? Z_AXIS.min : Z_AXIS.set_min;
5504         view_port_z[1] = (Z_AXIS.set_autoscale & AUTOSCALE_MAX) ? Z_AXIS.max : Z_AXIS.set_max;
5505     }
5506
5507     if (rectangular_image) {
5508
5509         /* There are eight ways that a valid pixel grid can be entered.  Use table
5510          * lookup instead of if() statements.  (Draw the various array combinations
5511          * on a sheet of paper, or see the README file.)
5512          */
5513         int line_length, i_delta_pixel, i_delta_line, i_start;
5514         int pixel_1_1, pixel_M_N;
5515         coordval *image;
5516         int array_size;
5517         float xsts, ysts;
5518
5519         if (!project_points) {
5520             /* Determine axis direction according to the sign of the terminal scale. */
5521             xsts = (axis_array[x_axis].term_scale > 0 ? +1 : -1);
5522             ysts = (axis_array[y_axis].term_scale > 0 ? +1 : -1);
5523         } else {
5524             /* 3D plots do not use the term_scale mechanism AXIS_SETSCALE(). */
5525             xsts = 1;
5526             ysts = 1;
5527         }
5528
5529         /* Set up parameters for indexing through the image matrix to transfer data.
5530          * These formulas were derived for a terminal image routine which uses the
5531          * upper left corner as pixel (1,1).
5532          */
5533         if (fabs(delta_x_grid[0]) > fabs(delta_x_grid[1])) {
5534             line_length = K;
5535             i_start = (delta_y_grid[1]*ysts > 0 ? L : 1) * K - (delta_x_grid[0]*xsts > 0 ? K : 1);
5536             i_delta_pixel = (delta_x_grid[0]*xsts > 0 ? +1 : -1);
5537             i_delta_line = (delta_x_grid[0]*xsts > 0 ? -K : +K) + (delta_y_grid[1]*ysts > 0 ? -K : +K);
5538         } else {
5539             line_length = L;
5540             i_start = (delta_x_grid[1]*xsts > 0 ? 1 : L) * K - (delta_y_grid[0]*ysts > 0 ? 1 : K);
5541             i_delta_pixel = (delta_x_grid[1]*xsts > 0 ? +K : -K);
5542             i_delta_line = K*L*(delta_x_grid[1]*xsts > 0 ? -1 : +1) + (delta_y_grid[0]*ysts > 0 ? -1 : +1);
5543         }
5544
5545         /* Assign enough memory for the maximum image size. */
5546         array_size = K*L;
5547
5548         /* If doing color, multiply size by three for RGB triples. */
5549         if (pixel_planes == IC_RGB) {
5550             array_size *= 3;
5551         }
5552
5553         image = (coordval *) gp_alloc(array_size*sizeof(image[0]),"image");
5554
5555         /* Place points into image array based upon the arrangement of point indices and
5556          * the visibility of pixels.
5557          */
5558         if (image != NULL) {
5559
5560             int j;
5561             gpiPoint corners[4];
5562             int M = 0, N = 0;  /* M = number of columns, N = number of rows.  (K and L don't
5563                                 * have a set direction, but M and N do.)
5564                                 */
5565             int i_image, i_sub_image = 0;
5566             double d_x_o_2, d_y_o_2, d_z_o_2;
5567             int line_pixel_count = 0;
5568
5569             d_x_o_2 = ( (points[grid_corner[0]].x - points[grid_corner[1]].x)/(K-1)
5570                         + (points[grid_corner[0]].x - points[grid_corner[2]].x)/(L-1) ) / 2;
5571             d_y_o_2 = ( (points[grid_corner[0]].y - points[grid_corner[1]].y)/(K-1)
5572                         + (points[grid_corner[0]].y - points[grid_corner[2]].y)/(L-1) ) / 2;
5573             d_z_o_2 = ( (points[grid_corner[0]].z - points[grid_corner[1]].z)/(K-1)
5574                         + (points[grid_corner[0]].z - points[grid_corner[2]].z)/(L-1) ) / 2;
5575
5576             pixel_1_1 = -1;
5577             pixel_M_N = -1;
5578
5579             /* Step through the points placing them in the proper spot in the matrix array. */
5580             for (i=0, j=line_length, i_image=i_start; i < p_count; i++) {
5581
5582                 TBOOLEAN visible;
5583                 double x, y, z, x_low, x_high, y_low, y_high, z_low, z_high;
5584
5585                 x = points[i_image].x;
5586                 y = points[i_image].y;
5587                 z = points[i_image].z;
5588                 x_low = x - d_x_o_2;  x_high = x + d_x_o_2;
5589                 y_low = y - d_y_o_2;  y_high = y + d_y_o_2;
5590                 z_low = z - d_z_o_2;  z_high = z + d_z_o_2;
5591
5592                 /* Check if a portion of this pixel will be visible.  Do not use the
5593                  * points[i].type == INRANGE test because a portion of a pixel can
5594                  * extend into view and the INRANGE type doesn't account for this.
5595                  *
5596                  * This series of tests is designed for speed.  If one of the corners
5597                  * of the pixel in question falls in the view port range then the pixel
5598                  * will be visible.  Do this test first because it is the more likely
5599                  * of situations.  It could also happen that the view port is smaller
5600                  * than a pixel.  In that case, if one of the view port corners lands
5601                  * inside the pixel then the pixel in question will be visible.  This
5602                  * won't be as common, so do those tests last.  Set up the if structure
5603                  * in such a way that as soon as one of the tests is true, the conditional
5604                  * tests stop.
5605                  */
5606                 if ( ( inrange(x_low, view_port_x[0], view_port_x[1]) || inrange(x_high, view_port_x[0], view_port_x[1]) )
5607                   && ( inrange(y_low, view_port_y[0], view_port_y[1]) || inrange(y_high, view_port_y[0], view_port_y[1]) )
5608                   && ( !project_points || inrange(z_low, view_port_z[0], view_port_z[1]) || inrange(z_high, view_port_z[0], view_port_z[1]) ) )
5609                     visible = TRUE;
5610                 else if ( ( inrange(view_port_x[0], x_low, x_high) || inrange(view_port_x[1], x_low, x_high) )
5611                   && ( inrange(view_port_y[0], y_low, y_high) || inrange(view_port_y[1], y_low, y_high) )
5612                   && ( !project_points || inrange(view_port_z[0], z_low, z_high) || inrange(view_port_z[1], z_low, z_high) ) )
5613                     visible = TRUE;
5614                 else
5615                     visible = FALSE;
5616
5617 #define USE_CLIP_POINTS 0
5618 #if USE_CLIP_POINTS
5619                 if ( !clip_points ||
5620 #else
5621                 if (
5622 #endif
5623                     visible ) {
5624                     if (pixel_1_1 < 0) {
5625                         /* First visible point. */
5626                         pixel_1_1 = i_image;
5627                         M = 0;
5628                         N = 1;
5629                         line_pixel_count = 1;
5630                     } else {
5631                         if (line_pixel_count == 0)
5632                             N += 1;
5633                         line_pixel_count++;
5634                         if ( (N != 1) && (line_pixel_count > M) ) {
5635                             fprintf(stderr, ERROR_NOTICE("Visible pixel grid has a scan line longer than previous scan lines."));
5636                             return;
5637                         }
5638                     }
5639
5640                     pixel_M_N = i_image;
5641
5642                     if (pixel_planes == IC_PALETTE) {
5643                         image[i_sub_image++] = cb2gray( points[i_image].CRD_COLOR );
5644                     } else {
5645                         image[i_sub_image++] = cb2gray( points[i_image].z );
5646                         image[i_sub_image++] = cb2gray( points[i_image].xlow );
5647                         image[i_sub_image++] = cb2gray( points[i_image].ylow );
5648                     }
5649
5650                 }
5651
5652                 i_image += i_delta_pixel;
5653                 j--;
5654                 if (j == 0) {
5655                     if (M == 0)
5656                         M = line_pixel_count;
5657                     else if ((line_pixel_count > 0) && (line_pixel_count != M)) {
5658                         fprintf(stderr, ERROR_NOTICE("Visible pixel grid has a scan line shorter than previous scan lines."));
5659                         return;
5660                     }
5661                     line_pixel_count = 0;
5662                     i_image += i_delta_line;
5663                     j = line_length;
5664                 }
5665             }
5666
5667             if ( (M > 0) && (N > 0) ) {
5668
5669                 /* The information collected to this point is:
5670                  *
5671                  * M = <number of columns>
5672                  * N = <number of rows>
5673                  * image[] = M x N array of pixel data.
5674                  * pixel_1_1 = position in points[] associated with pixel (1,1)
5675                  * pixel_M_N = position in points[] associated with pixel (M,N)
5676                  */
5677
5678                 /* One of the delta values in each direction is zero, so add. */
5679                 if (project_points) {
5680                     double x, y;
5681                     map3d_xy_double(points[pixel_1_1].x, points[pixel_1_1].y, points[pixel_1_1].z, &x, &y);
5682                     corners[0].x = x - fabs(delta_x_grid[0]+delta_x_grid[1])/2;
5683                     corners[0].y = y + fabs(delta_y_grid[0]+delta_y_grid[1])/2;
5684                     map3d_xy_double(points[pixel_M_N].x, points[pixel_M_N].y, points[pixel_M_N].z, &x, &y);
5685                     corners[1].x = x + fabs(delta_x_grid[0]+delta_x_grid[1])/2;
5686                     corners[1].y = y - fabs(delta_y_grid[0]+delta_y_grid[1])/2;
5687                     map3d_xy_double(view_port_x[0], view_port_y[0], view_port_z[0], &x, &y);
5688                     corners[2].x = x;
5689                     corners[2].y = y;
5690                     map3d_xy_double(view_port_x[1], view_port_y[1], view_port_z[1], &x, &y);
5691                     corners[3].x = x;
5692                     corners[3].y = y;
5693                 } else {
5694                     corners[0].x = map_x(points[pixel_1_1].x - xsts*fabs(d_x_o_2));
5695                     corners[0].y = map_y(points[pixel_1_1].y + ysts*fabs(d_y_o_2));
5696                     corners[1].x = map_x(points[pixel_M_N].x + xsts*fabs(d_x_o_2));
5697                     corners[1].y = map_y(points[pixel_M_N].y - ysts*fabs(d_y_o_2));
5698                     corners[2].x = map_x(view_port_x[0]);
5699                     corners[2].y = map_y(view_port_y[1]);
5700                     corners[3].x = map_x(view_port_x[1]);
5701                     corners[3].y = map_y(view_port_y[0]);
5702                 }
5703
5704                 if ( (pixel_planes == IC_PALETTE) || (pixel_planes == IC_RGB) )
5705                     (*term->image) (M, N, image, corners, pixel_planes);
5706                 else
5707                     fprintf(stderr, ERROR_NOTICE("Invalid pixel color planes specified.\n\n"));
5708             }
5709
5710             free ((void *)image);
5711
5712         } else {
5713             fprintf(stderr, ERROR_NOTICE("Could not allocate memory for image."));
5714             return;
5715         }
5716
5717     } else {    /* !rectangular_image */
5718
5719         if (pixel_planes != IC_RGB) {
5720
5721             /* Use sum of vectors to compute the pixel corners with respect to its center. */
5722             struct {double x; double y; double z;} delta_grid[2], delta_pixel[2];
5723             int j, i_image;
5724
5725             if (!term->filled_polygon)
5726                 int_error(NO_CARET, "This terminal does not support filled polygons");
5727
5728             /* Grid spacing in 3D space. */
5729             delta_grid[0].x = (points[grid_corner[1]].x - points[grid_corner[0]].x)/(K-1);
5730             delta_grid[0].y = (points[grid_corner[1]].y - points[grid_corner[0]].y)/(K-1);
5731             delta_grid[0].z = (points[grid_corner[1]].z - points[grid_corner[0]].z)/(K-1);
5732             delta_grid[1].x = (points[grid_corner[2]].x - points[grid_corner[0]].x)/(L-1);
5733             delta_grid[1].y = (points[grid_corner[2]].y - points[grid_corner[0]].y)/(L-1);
5734             delta_grid[1].z = (points[grid_corner[2]].z - points[grid_corner[0]].z)/(L-1);
5735
5736             /* Pixel dimensions in the 3D space. */
5737             delta_pixel[0].x = (delta_grid[0].x + delta_grid[1].x) / 2;
5738             delta_pixel[0].y = (delta_grid[0].y + delta_grid[1].y) / 2;
5739             delta_pixel[0].z = (delta_grid[0].z + delta_grid[1].z) / 2;
5740             delta_pixel[1].x = (delta_grid[0].x - delta_grid[1].x) / 2;
5741             delta_pixel[1].y = (delta_grid[0].y - delta_grid[1].y) / 2;
5742             delta_pixel[1].z = (delta_grid[0].z - delta_grid[1].z) / 2;
5743
5744             i_image = 0;
5745
5746             for (j=0; j < L; j++) {
5747
5748                 double x_line_start, y_line_start, z_line_start;
5749
5750                 x_line_start = points[grid_corner[0]].x + j * delta_grid[1].x;
5751                 y_line_start = points[grid_corner[0]].y + j * delta_grid[1].y;
5752                 z_line_start = points[grid_corner[0]].z + j * delta_grid[1].z;
5753
5754                 for (i=0; i < K; i++) {
5755
5756                     double x, y, z;
5757                     TBOOLEAN corner_in_range[4];
5758                     TBOOLEAN pixel_in_view = FALSE, view_in_pixel = FALSE;
5759                     struct {double x; double y; double z;} p_corners[4]; /* Parallelogram corners. */
5760                     int k;
5761
5762                     x = x_line_start + i * delta_grid[0].x;
5763                     y = y_line_start + i * delta_grid[0].y;
5764                     z = z_line_start + i * delta_grid[0].z;
5765
5766                     p_corners[0].x = x + delta_pixel[0].x;
5767                     p_corners[0].y = y + delta_pixel[0].y;
5768                     p_corners[0].z = z + delta_pixel[0].z;
5769                     p_corners[1].x = x + delta_pixel[1].x;
5770                     p_corners[1].y = y + delta_pixel[1].y;
5771                     p_corners[1].z = z + delta_pixel[1].z;
5772                     p_corners[2].x = x - delta_pixel[0].x;
5773                     p_corners[2].y = y - delta_pixel[0].y;
5774                     p_corners[2].z = z - delta_pixel[0].z;
5775                     p_corners[3].x = x - delta_pixel[1].x;
5776                     p_corners[3].y = y - delta_pixel[1].y;
5777                     p_corners[3].z = z - delta_pixel[1].z;
5778
5779                     /* Check if any of the corners are viewable */
5780                     for (k=0; k < 4; k++) {
5781                         corner_in_range[k] =
5782                             inrange(p_corners[k].x, view_port_x[0], view_port_x[1])
5783                             && inrange(p_corners[k].y, view_port_y[0], view_port_y[1])
5784                             && (!project_points || splot_map ||
5785                                 inrange(p_corners[k].z, view_port_z[0], view_port_z[1]));
5786                         pixel_in_view = pixel_in_view || corner_in_range[k];
5787                     }
5788
5789                     if (pixel_in_view || view_in_pixel) {
5790
5791                         int N_corners = 0;    /* Number of corners. */
5792                         gpiPoint corners[5];  /* At most 5 corners. */
5793
5794                         corners[0].style = FS_DEFAULT;
5795
5796                         if (pixel_in_view) {
5797                             if (corner_in_range[0] && corner_in_range[1] && corner_in_range[2] && corner_in_range[3]) {
5798                                 int i_corners;
5799
5800                                 N_corners = 4;
5801
5802                                 for (i_corners=0; i_corners < N_corners; i_corners++) {
5803                                     if (project_points) {
5804                                         map3d_xy_double(p_corners[i_corners].x, p_corners[i_corners].y, p_corners[i_corners].z,
5805                                                  &x, &y);
5806                                         corners[i_corners].x = x;
5807                                         corners[i_corners].y = y;
5808                                     } else {
5809                                         corners[i_corners].x = map_x(p_corners[i_corners].x);
5810                                         corners[i_corners].y = map_y(p_corners[i_corners].y);
5811                                     }
5812                                 }
5813                             } else {
5814                                 /* Clip out one or more of the corners to create triangle, quadrangle or pentagon. */
5815                                 static short clipping_yet_to_be_added_indicated = 0;
5816                                 if (!clipping_yet_to_be_added_indicated) {
5817                                     /* Some tricky geometry.  Save until certain this will be in Gnuplot. */
5818                                     fprintf(stderr,"\nNOTICE:  A triangle/quadrangle/pentagon clipping algorithm\n"
5819                                             "         needs to be added for pixels at the boundary.  Image\n"
5820                                             "         may lie outside borders in some instances.\n\n");
5821                                     clipping_yet_to_be_added_indicated = 1;
5822                                 }
5823                             }
5824                         } else {
5825                             /* Could still be visible if any of the four corners of the view port are
5826                              * within the parallelogram formed by the pixel.  This is also some tricky
5827                              * geometry.  Wait until it is certain that this will be a part of Gnuplot..
5828                              */
5829                         }
5830
5831                         if (N_corners >= 3) {
5832                             set_color( cb2gray(points[i_image].CRD_COLOR) );
5833                             (*term->filled_polygon) (N_corners, corners);
5834                         }
5835                     }
5836
5837                     i_image++;
5838                 }
5839             }
5840         } else {
5841             fprintf(stdout, ERROR_NOTICE("Color boxes cannot handle RGB components.\n\n"));
5842         }
5843     }}
5844
5845 }
5846
5847 #endif
5848