2 static char *RCSid() { return RCSid("$Id: graphics.c,v 1.194.2.43 2009/07/05 06:16:17 sfeam Exp $"); }
5 /* GNUPLOT - graphics.c */
8 * Copyright 1986 - 1993, 1998, 2004 Thomas Williams, Colin Kelley
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.
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,
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
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.
33 * This software is provided "as is" without express or implied warranty
34 * to the extent permitted by applicable law.
37 /* Daniel Sebald: added plot_image_or_update_axes() routine for images.
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 */
59 /* Externally visible/modifiable status variables */
61 /* 'set offset' --- artificial buffer zone between coordinate axes and
62 * the area actually covered by the data */
69 double bar_size = 1.0;
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.
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 */
91 /* set by tic_callback - how large to draw polar radii */
92 static double largest_polar_circle;
94 static int xlablin, x2lablin, ylablin, y2lablin, titlelin, xticlin, x2ticlin;
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 } */
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
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;
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));
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));
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));
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));
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));
153 static void boundary __PROTO((struct curve_points * plots, int count));
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));
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));
163 static int find_maxl_keys __PROTO((struct curve_points *plots, int count, int *kcnt));
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));
168 static TBOOLEAN check_for_variable_color __PROTO((struct curve_points *plot, struct coordinate *point));
170 static int style_from_fill __PROTO((struct fill_style_type *));
172 /* for plotting error bars
173 * half the width of error bar tic mark
175 #define ERRORBARTIC GPMAX((t->h_tic/2),1)
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(). */
181 #define BOTTOM_EDGE 4
184 #define clip_fill ((plot->filledcurves_options.closeto == FILLEDCURVES_CLOSED) || clip_lines2)
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
196 GP_INLINE static TBOOLEAN
197 i_inrange(int z, int min, int max)
200 ? ((z >= min) && (z <= max))
201 : ((z >= max) && (z <= min)));
204 GP_INLINE static double
205 f_max(double a, double b)
207 return (GPMAX(a, b));
210 GP_INLINE static double
211 f_min(double a, double b)
213 return (GPMIN(a, b));
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))
222 /* True if a and b have the same sign or zero (positive or negative) */
223 #define samesign(a,b) ((a) * (b) >= 0)
226 /*{{{ more variables */
228 /* we make a local copy of the 'key' variable so that if something
229 * goes wrong, we can switch it off temporarily
232 static TBOOLEAN lkey;
237 find_maxl_keys(struct curve_points *plots, int count, int *kcnt)
239 int mlen, len, curve, cnt;
240 struct curve_points *this_plot;
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);
253 ignore_enhanced(FALSE);
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)
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) {
265 len = key_entry->text ? estimate_strlen(key_entry->text) : 0;
280 /* borders of plotting area
281 * computed once on every call to do_plot
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
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.
297 * Margin computation redone by Dick Crawford (rccrawford@lanl.gov) 4/98
301 boundary(struct curve_points *plots, int count)
303 int yticlin = 0, y2ticlin = 0, timelin = 0;
304 legend_key *key = &keyT;
306 struct termentry *t = term;
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);
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 */
334 int key_cols = 1; /* # columns of keys */
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;
344 TBOOLEAN shift_labels_to_border = FALSE;
346 lkey = key->visible; /* but we may have to disable it later */
348 xticlin = ylablin = y2lablin = xlablin = x2lablin = titlelin = 0;
350 /*{{{ count lines in labels and tics */
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);
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, "_^"))
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);
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)
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);
383 label_width(timelabel.text, &timelin);
386 /*{{{ preliminary plot_bounds.ytop calculation */
388 /* first compute heights of things to be written in the margin */
393 map_position_r(&(title.offset), &tmpx, &tmpy, "boundary");
394 title_textheight = (int) ((titlelin + 1) * (t->v_char) + tmpy);
396 title_textheight = 0;
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;
407 x2label_textheight = 0;
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);
417 x2tic_textheight = (int) (x2ticlin * t->v_char);
419 x2tic_textheight = 0;
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);
431 if (timelabel.text && !timelabel_bottom) {
433 map_position_r(&(timelabel.offset), &tmpx, &tmpy, "boundary");
434 timetop_textheight = (int) ((timelin + 2) * t->v_char + tmpy);
436 timetop_textheight = 0;
438 /* horizontal ylabel */
439 if (axis_array[FIRST_Y_AXIS].label.text && !can_rotate) {
441 map_position_r(&(axis_array[FIRST_Y_AXIS].label.offset),
442 &tmpx, &tmpy, "boundary");
443 ylabel_textheight = (int) (ylablin * t->v_char + tmpy);
445 ylabel_textheight = 0;
447 /* horizontal y2label */
448 if (axis_array[SECOND_Y_AXIS].label.text && !can_rotate) {
450 map_position_r(&(axis_array[SECOND_Y_AXIS].label.offset),
451 &tmpx, &tmpy, "boundary");
452 y2label_textheight = (int) (y2lablin * t->v_char + tmpy);
454 y2label_textheight = 0;
456 /* compute plot_bounds.ytop from the various components
457 * unless tmargin is explicitly specified */
459 /* HBB 20010118: fix round-off bug */
460 plot_bounds.ytop = (int) (0.5 + (ysize + yoffset) * t->ymax);
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;
469 /* Auto-calculation of space required */
470 int top_margin = x2label_textheight + title_textheight;
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;
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;
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);
491 /* end of preliminary plot_bounds.ytop calculation }}} */
494 /*{{{ preliminary plot_bounds.xleft, needed for "under" */
495 if (lmargin.scalex == screen)
496 plot_bounds.xleft = lmargin.x * (float)t->xmax;
498 plot_bounds.xleft = xoffset * t->xmax
499 + t->h_char * (lmargin.x >= 0 ? lmargin.x : 2);
503 /*{{{ tentative plot_bounds.xright, needed for "under" */
504 if (rmargin.scalex == screen)
505 plot_bounds.xright = rmargin.x * (float)t->xmax;
507 plot_bounds.xright = (xsize + xoffset) * t->xmax
508 - t->h_char * (rmargin.x >= 0 ? rmargin.x : 2);
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);
524 /*{{{ preliminary plot_bounds.ybot calculation
525 * first compute heights of labels and tics */
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;
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);
545 xtic_textheight = (int) (t->v_char * (xticlin + 1));
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);
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;
569 xlabel_textheight = 0;
572 if (timelabel.text && timelabel_bottom) {
573 /* && !vertical_timelabel)
574 * DBT 11-18-98 resize plot for vertical timelabels too !
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);
581 timebot_textheight = 0;
583 /* compute plot_bounds.ybot from the various components
584 * unless bmargin is explicitly specified */
586 plot_bounds.ybot = yoffset * (float)t->ymax + 0.5;
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;
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);
608 /* end of preliminary plot_bounds.ybot calculation }}} */
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);
621 TBOOLEAN key_panic = FALSE;
622 /*{{{ essential key features */
624 p_width = pointsize * t->h_tic;
625 p_height = pointsize * t->v_tic;
627 if (key->swidth >= 0)
628 key_sample_width = key->swidth * t->h_char + p_width;
630 key_sample_width = 0;
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;
639 /* Count max_len key and number keys with len > 0 */
640 max_ptitl_len = find_maxl_keys(plots, count, &ptitl_cnt);
642 /* Key title length and height */
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;
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;
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;
670 key_point_offset = (key_sample_left + key_sample_right) / 2;
672 /* advance width for cols */
673 key_col_wth = key_size_left + key_size_right;
675 key_rows = ptitl_cnt;
678 /* calculate rows and cols for key */
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 */
687 key_col_wth = (plot_bounds.xright - plot_bounds.xleft);
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;
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)
708 key_cols = (int) (ptitl_cnt + i - 1) / i;
709 /* now calculate actual no rows depending on no cols */
714 key_rows = (int) (ptitl_cnt + key_cols - 1) / key_cols;
718 /* warn if we had to punt on key size calculations */
720 int_warn(NO_CARET, "Warning - difficulty fitting plot titles into key");
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;
742 /*{{{ set up y and y2 tics */
743 setup_tics(FIRST_Y_AXIS, 20);
744 setup_tics(SECOND_Y_AXIS, 20);
747 /* Adjust color axis limits if necessary. */
748 if (is_plot_with_palette()) {
750 axis_checked_extend_empty_range(COLOR_AXIS, "All points of color axis undefined.");
751 setup_tics(COLOR_AXIS, 20);
754 /*{{{ recompute plot_bounds.xleft based on widths of ytics, ylabel etc
755 unless it has been explicitly set by lmargin */
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;
769 if ((axis_array[FIRST_Y_AXIS].ticmode & TICS_ON_BORDER)
770 || shift_labels_to_border) {
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));
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...
782 gen_tics(FIRST_Y_AXIS, /* 0, */ widest_tic_callback);
784 ytic_textwidth = (int) (t->h_char * (widest_tic_strlen + 2));
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);
800 if (axis_array[FIRST_Y_AXIS].label.text && can_rotate) {
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;
808 /* this should get large for NEGATIVE ylabel.xoffsets DBT 11-5-98 */
809 ylabel_textwidth = 0;
812 if (timelabel.text && vertical_timelabel) {
814 map_position_r(&(timelabel.offset), &tmpx, &tmpy, "boundary");
815 timelabel_textwidth = (int) ((timelin + 1.5) * t->v_char - tmpx);
817 timelabel_textwidth = 0;
820 /* Auto-calculation */
823 plot_bounds.xleft += (timelabel_textwidth > ylabel_textwidth
824 ? timelabel_textwidth : ylabel_textwidth)
825 + ytic_width + ytic_textwidth;
827 /* make sure plot_bounds.xleft is wide enough for a negatively
828 * x-offset horizontal timestamp
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);
839 /* DBT 12-3-98 extra margin just in case */
840 plot_bounds.xleft += 0.5 * t->h_char;
842 /* Note: we took care of explicit 'set lmargin foo' at line 492 */
844 /* end of plot_bounds.xleft calculation }}} */
846 /*{{{ recompute plot_bounds.xright based on widest y2tic. y2labels, key "outside"
847 unless it has been explicitly set by rmargin */
850 if (axis_array[SECOND_Y_AXIS].ticmode & TICS_ON_BORDER) {
852 y2tic_textwidth = (int) (t->v_char * (y2ticlin + 2));
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...
859 gen_tics(SECOND_Y_AXIS, /* 0, */ widest_tic_callback);
861 y2tic_textwidth = (int) (t->h_char * (widest_tic_strlen + 2));
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);
877 if (can_rotate && axis_array[SECOND_Y_AXIS].label.text) {
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;
885 y2label_textwidth = 0;
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);
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;
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);
905 /* DBT 12-3-98 extra margin just in case */
906 plot_bounds.xright -= 0.5 * t->h_char;
908 /* Note: we took care of explicit 'set rmargin foo' at line 502 */
911 /* end of plot_bounds.xright calculation }}} */
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
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 */
923 setup_tics(FIRST_X_AXIS, 20);
924 setup_tics(SECOND_X_AXIS, 20);
927 /* Modify the bounding box to fit the aspect ratio, if any was
929 if (aspect_ratio != 0.0) {
930 double current_aspect_ratio;
933 && (X_AXIS.max - X_AXIS.min) != 0.0
935 current_aspect_ratio = - aspect_ratio
936 * fabs((Y_AXIS.max - Y_AXIS.min) / (X_AXIS.max - X_AXIS.min));
938 current_aspect_ratio = aspect_ratio;
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;
947 if (current > required) {
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;
956 plot_bounds.ybot += (old_height - new_height) / 2;
957 plot_bounds.ytop -= (old_height - new_height) / 2;
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;
968 plot_bounds.xleft += (old_width - new_width) / 2;
969 plot_bounds.xright -= (old_width - new_width) / 2;
975 /* adjust top and bottom margins for tic label rotation */
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;
988 x2tic_textheight = t->v_char;
989 plot_bounds.ytop -= x2tic_textheight;
992 && axis_array[FIRST_X_AXIS].ticmode & TICS_ON_BORDER
995 if (axis_array[FIRST_X_AXIS].tic_rotate == 90)
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
1005 plot_bounds.ybot += xtic_textheight;
1009 * Notwithstanding all these fancy calculations, plot_bounds.ytop must always be above plot_bounds.ybot
1011 if (plot_bounds.ytop < plot_bounds.ybot) {
1012 int i = plot_bounds.ytop;
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"));
1019 /* compute coordinates for axis labels, title et al
1020 * (some of these may not be used) */
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;
1026 title_y = x2label_y + title_textheight;
1028 ylabel_y = plot_bounds.ytop + x2tic_height + x2tic_textheight + ylabel_textheight;
1030 y2label_y = plot_bounds.ytop + x2tic_height + x2tic_textheight + y2label_textheight;
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;
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;
1043 if (vertical_timelabel) {
1044 if (timelabel_bottom)
1045 time_y = xlabel_y - timebot_textheight + xlabel_textheight;
1047 time_y = title_y + timetop_textheight - title_textheight
1048 - x2label_textheight;
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;
1057 time_y = plot_bounds.ytop + x2tic_height + x2tic_textheight
1058 + timetop_textheight + (int) t->h_char;
1060 if (vertical_timelabel)
1061 time_x = plot_bounds.xleft - ytic_width - ytic_textwidth - timelabel_textwidth;
1064 map_position_r(&(timelabel.offset), &tmpx, &tmpy, "boundary");
1065 time_x = plot_bounds.xleft - ytic_width - ytic_textwidth + (int) (tmpx);
1068 xtic_y = plot_bounds.ybot - xtic_height
1069 - (int) (vertical_xtics ? t->h_char : t->v_char);
1071 x2tic_y = plot_bounds.ytop + x2tic_height
1072 + (vertical_x2tics ? (int) t->h_char : x2tic_textheight);
1074 ytic_x = plot_bounds.xleft - ytic_width
1076 ? (ytic_textwidth - (int) t->v_char)
1079 y2tic_x = plot_bounds.xright + y2tic_width
1080 + (int) (vertical_y2tics ? t->v_char : t->h_char);
1082 /* restore text to horizontal [we tested rotation above] */
1083 (void) (*t->text_angle) (0);
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);
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;
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;
1126 } else if (key->region == GPKEY_AUTO_EXTERIOR_LRTBC || key->region == GPKEY_AUTO_EXTERIOR_MARGIN) {
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;
1138 if (key->vpos == JUST_TOP) {
1139 /* align top first since tmargin may be manual */
1141 keybox.yt = plot_bounds.ytop;
1143 keybox.yt = (ysize + yoffset) * t->ymax - t->v_tic;
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;
1151 /* align bottom first since bmargin may be manual */
1153 keybox.yb = plot_bounds.ybot;
1155 keybox.yb = yoffset * t->ymax + t->v_tic;
1157 keybox.yt = keybox.yb + key_h;
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;
1171 if (key->hpos == LEFT) {
1172 /* align left first since lmargin may be manual */
1174 keybox.xl = plot_bounds.xleft;
1176 keybox.xl = xoffset * t->xmax + t->h_char;
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;
1184 /* align right first since rmargin may be manual */
1186 keybox.xr = plot_bounds.xright;
1188 keybox.xr = (xsize + xoffset) * t->xmax - t->h_char;
1190 keybox.xl = keybox.xr - key_w;
1197 map_position(&key->user_pos, &x, &y, "key");
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
1204 #define OK fprintf(stderr,"Line %i of %s is OK\n",__LINE__,__FILE__)
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);
1209 /* Here top, bottom, left, right refer to the alignment with respect to point. */
1211 if (key->hpos == CENTRE)
1212 keybox.xl -= key_w/2;
1213 else if (key->hpos == RIGHT)
1215 keybox.xr = keybox.xl + key_w;
1217 if (key->vpos == JUST_CENTRE)
1218 keybox.yt += key_h/2;
1219 else if (key->vpos == JUST_BOT)
1221 keybox.yb = keybox.yt - key_h;
1225 /* Set default clipping to the plot boundary */
1226 clip_area = &plot_bounds;
1235 struct arrow_def *arrow,
1239 double sx_d, sy_d, ex_d, ey_d;
1240 map_position_double(&arrow->start, &sx_d, &sy_d, "arrow");
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);
1251 map_position_double(&arrow->end, &ex_d, &ey_d, "arrow");
1257 /* FIXME HBB 20020225: this is shared with graph3d.c, so it shouldn't
1258 * be in this module */
1260 apply_head_properties(struct arrow_style_type *arrow_properties)
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 */
1267 struct position headsize = {0,0,0,0.,0.,0.};
1269 headsize.x = arrow_properties->head_length;
1270 headsize.scalex = arrow_properties->head_lengthunit;
1272 headsize.y = 1.0; /* any value, just avoid log y */
1273 map_position(&headsize, &x2, &itmp, "arrow");
1275 headsize.x = 0; /* measure length from zero */
1276 map_position(&headsize, &x1, &itmp, "arrow");
1278 curr_arrow_headangle = arrow_properties->head_angle;
1279 curr_arrow_headbackangle = arrow_properties->head_backangle;
1280 curr_arrow_headlength = x2 - x1;
1287 struct termentry *t = term;
1289 term_apply_lp_properties(&border_lp); /* border linetype */
1290 largest_polar_circle = 0;
1292 /* select first mapping */
1293 x_axis = FIRST_X_AXIS;
1294 y_axis = FIRST_Y_AXIS;
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);
1303 /* select second mapping */
1304 x_axis = SECOND_X_AXIS;
1305 y_axis = SECOND_Y_AXIS;
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);
1313 /* select first mapping */
1314 x_axis = FIRST_X_AXIS;
1315 y_axis = FIRST_Y_AXIS;
1317 /* RADIAL LINES FOR POLAR GRID */
1319 /* note that draw_clip_line takes unsigneds, but (fortunately)
1320 * clip_line takes signeds
1322 if (polar_grid_angle) {
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) */
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);
1338 draw_clip_line(ox, oy, map_x(largest_polar_circle * cos(theta)), map_y(largest_polar_circle * sin(theta)));
1343 place_arrows(int layer)
1345 struct arrow_def *this_arrow;
1346 BoundingBox *clip_save = clip_area;
1348 /* Allow arrows to run off the plot, so long as they are still on the canvas */
1349 if (term->flags & TERM_CAN_CLIP)
1352 clip_area = &canvas;
1354 for (this_arrow = first_arrow;
1356 this_arrow = this_arrow->next) {
1359 if (this_arrow->arrow_properties.layer != layer)
1361 get_arrow(this_arrow, &sx, &sy, &ex, &ey);
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);
1367 term_apply_lp_properties(&border_lp);
1368 clip_area = clip_save;
1372 place_labels(struct text_label *listhead, int layer, TBOOLEAN clip)
1374 struct text_label *this_label;
1377 if (term->pointsize)
1378 (*term->pointsize)(pointsize);
1380 for (this_label = listhead; this_label != NULL; this_label = this_label->next) {
1382 if (this_label->layer != layer)
1385 if (layer == LAYER_PLOTLABELS) {
1386 x = map_x(this_label->place.x);
1387 y = map_y(this_label->place.y);
1389 map_position(&this_label->place, &x, &y, "label");
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)))
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)))
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)))
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)))
1407 write_label(x, y, this_label);
1413 place_rectangles(struct object *listhead, int layer, int dimensions, BoundingBox *clip_area)
1415 t_object *this_object;
1416 t_rectangle *this_rect;
1417 double x1, y1, x2, y2;
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;
1428 if (this_object->object_type == OBJ_RECTANGLE)
1429 this_rect = &this_object->o.rectangle;
1433 if (this_object->layer != layer)
1436 if (this_rect->type == 1) {
1437 double width, height;
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) {
1444 map3d_position_double(&this_rect->center, &x1, &y1, "rect");
1445 map3d_position_r(&this_rect->extent, &junkw, &junkh, "rect");
1457 if (this_rect->extent.scalex == first_axes
1458 || this_rect->extent.scalex == second_axes)
1460 if (this_rect->extent.scaley == first_axes
1461 || this_rect->extent.scaley == second_axes)
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");
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)
1480 if (this_rect->bl.scaley == first_axes
1481 || this_rect->bl.scaley == second_axes)
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. */
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)
1506 if (w == 0 || h == 0)
1509 if (this_object->lp_properties.l_type == LT_DEFAULT)
1510 lpstyle = default_rectangle.lp_properties;
1512 lpstyle = this_object->lp_properties;
1513 if (lpstyle.l_width > 0)
1514 lpstyle.l_width = this_object->lp_properties.l_width;
1516 if (this_object->fillstyle.fillstyle == FS_DEFAULT)
1517 fillstyle = &default_rectangle.fillstyle;
1519 fillstyle = &this_object->fillstyle;
1521 term_apply_lp_properties(&lpstyle);
1522 style = style_from_fill(fillstyle);
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);
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);
1545 do_plot(struct curve_points *plots, int pcount)
1547 struct termentry *t = term;
1549 struct curve_points *this_plot = NULL;
1550 int xl = 0, yl = 0; /* avoid gcc -Wall warning */
1552 legend_key *key = &keyT;
1554 x_axis = FIRST_X_AXIS;
1555 y_axis = FIRST_Y_AXIS;
1557 /* Apply the desired viewport offsets. */
1558 if (Y_AXIS.min < Y_AXIS.max) {
1565 if (X_AXIS.min < X_AXIS.max) {
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!");
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().
1593 term_initialise(); /* may set xmax/ymax */
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.
1601 boundary(plots, pcount);
1604 if (is_plot_with_palette())
1607 /* Give a chance for rectangles to be behind everything else */
1608 place_rectangles(first_object, -1, 2, NULL);
1612 /* Sync point for epslatex text positioning */
1614 (term->layer)(TERM_LAYER_BACKTEXT);
1616 /* DRAW TICS AND GRID */
1617 if (grid_layer == 0 || grid_layer == -1)
1621 /* after grid so that axes linetypes are on top */
1622 x_axis = FIRST_X_AXIS;
1623 y_axis = FIRST_Y_AXIS; /* chose scaling */
1625 axis_draw_2d_zeroaxis(FIRST_X_AXIS,FIRST_Y_AXIS);
1626 axis_draw_2d_zeroaxis(FIRST_Y_AXIS,FIRST_X_AXIS);
1628 x_axis = SECOND_X_AXIS;
1629 y_axis = SECOND_Y_AXIS; /* chose scaling */
1631 axis_draw_2d_zeroaxis(SECOND_X_AXIS,SECOND_Y_AXIS);
1632 axis_draw_2d_zeroaxis(SECOND_Y_AXIS,SECOND_X_AXIS);
1634 /* DRAW PLOT BORDER */
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)) {
1646 map_position_r(&(axis_array[FIRST_Y_AXIS].label.offset),
1647 &tmpx, &tmpy, "doplot");
1649 x = ylabel_x + (t->v_char / 2);
1650 y = (plot_bounds.ytop + plot_bounds.ybot) / 2 + tmpy;
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);
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;
1662 write_multiline(x, y, axis_array[FIRST_Y_AXIS].label.text,
1664 axis_array[FIRST_Y_AXIS].label.font);
1666 reset_textcolor(&(axis_array[FIRST_Y_AXIS].label.textcolor),t);
1667 ignore_enhanced(FALSE);
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)) {
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;
1683 write_multiline(x, y, axis_array[SECOND_Y_AXIS].label.text,
1685 axis_array[SECOND_Y_AXIS].label.rotate,
1686 axis_array[SECOND_Y_AXIS].label.font);
1687 (*t->text_angle) (0);
1689 /* really bottom just, but we know number of lines */
1690 unsigned int x = y2label_x;
1691 unsigned int y = y2label_y;
1693 write_multiline(x, y, axis_array[SECOND_Y_AXIS].label.text,
1695 axis_array[SECOND_Y_AXIS].label.font);
1697 reset_textcolor(&(axis_array[SECOND_Y_AXIS].label.textcolor),t);
1698 ignore_enhanced(FALSE);
1702 if (axis_array[FIRST_X_AXIS].label.text) {
1705 map_position_r(&(axis_array[FIRST_X_AXIS].label.offset),
1706 &tmpx, &tmpy, "doplot");
1708 x = (plot_bounds.xright + plot_bounds.xleft) / 2 + tmpx;
1709 y = xlabel_y - t->v_char / 2; /* HBB */
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);
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;
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);
1737 if (axis_array[SECOND_X_AXIS].label.text) {
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);
1753 /* PLACE TIMEDATE */
1754 if (timelabel.text) {
1755 /* we worked out coordinates in boundary() */
1758 unsigned int x = time_x;
1759 unsigned int y = time_y;
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));
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);
1771 write_multiline(x, y, str, RIGHT, JUST_TOP, TEXT_VERTICAL, timelabel.font);
1772 (*t->text_angle) (0);
1774 y -= t->v_char / 2; /* HBB */
1775 if (timelabel_bottom)
1776 write_multiline(x, y, str, LEFT, JUST_BOT, 0, timelabel.font);
1778 write_multiline(x, y, str, LEFT, JUST_TOP, 0, timelabel.font);
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);
1788 /* And rectangles */
1789 place_rectangles( first_object, 0, 2, clip_area );
1792 place_labels( first_label, 0, FALSE );
1797 /* Sync point for epslatex text positioning */
1799 (term->layer)(TERM_LAYER_FRONTTEXT);
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;
1808 int center = (keybox.xl + keybox.xr) / 2;
1809 double extra_height = 0.0;
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;
1822 yl -= (int)(0.5 * key->height_fix * t->v_char);
1823 yl_ref = yl -= key_entry_height / 2; /* centralise the keys */
1826 if (key->box.l_type > LT_NODRAW) {
1827 BoundingBox *clip_save = clip_area;
1828 if (term->flags & TERM_CAN_CLIP)
1831 clip_area = &canvas;
1832 term_apply_lp_properties(&key->box);
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);
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;
1848 for (curve = 0; curve < pcount; this_plot = this_plot->next, curve++) {
1849 TBOOLEAN localkey = lkey; /* a local copy */
1851 /* set scaling for this plot's axes */
1852 x_axis = this_plot->x_axis;
1853 y_axis = this_plot->y_axis;
1855 term_apply_lp_properties(&(this_plot->lp_properties));
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);
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) {
1873 yl = yl - key_entry_height;
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;
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) {
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);
1892 yl = yl - key_entry_height;
1894 free_labels(this_plot->labels);
1895 this_plot->labels = NULL;
1896 this_plot->lp_properties = save_lp;
1901 if (this_plot->title && !*this_plot->title) {
1903 } else if (this_plot->plot_type == NODATA) {
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) {
1911 yl = keybox.yb + yl_ref + key_entry_height/2 - yl;
1912 do_key_sample(this_plot, key, this_plot->title, t, xl, yl);
1914 ignore_enhanced(FALSE);
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) {
1922 switch (this_plot->plot_style) {
1924 plot_impulses(this_plot, X_AXIS.term_zero, Y_AXIS.term_zero);
1927 plot_lines(this_plot);
1930 plot_steps(this_plot);
1933 plot_fsteps(this_plot);
1936 plot_histeps(this_plot);
1939 plot_points(this_plot);
1942 plot_lines(this_plot);
1943 plot_points(this_plot);
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);
1950 plot_dots(this_plot);
1955 plot_lines(this_plot);
1956 plot_bars(this_plot);
1957 plot_points(this_plot);
1962 plot_bars(this_plot);
1963 plot_points(this_plot);
1967 plot_boxes(this_plot, Y_AXIS.term_zero);
1970 #ifdef EAM_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);
1978 (term->linetype)(this_plot->lp_properties.l_type);
1979 plot_bars(this_plot);
1980 term_apply_lp_properties(&(this_plot->lp_properties));
1982 plot_boxes(this_plot, Y_AXIS.term_zero);
1987 plot_boxes(this_plot, Y_AXIS.term_zero);
1988 plot_bars(this_plot);
1992 if (this_plot->filledcurves_options.closeto == FILLEDCURVES_BETWEEN)
1993 plot_betweencurves(this_plot);
1995 plot_filledcurves(this_plot);
1996 if (this_plot->fill_properties.border_linetype == LT_NODRAW)
1998 if (this_plot->fill_properties.border_linetype != LT_UNDEFINED)
1999 (*t->linetype)(this_plot->fill_properties.border_linetype);
2000 plot_lines(this_plot);
2005 plot_vectors(this_plot);
2008 plot_f_bars(this_plot);
2011 plot_c_bars(this_plot);
2015 fprintf(stderr, "** warning: can't use pm3d for 2d plots -- please unset pm3d\n");
2018 #ifdef EAM_DATASTRINGS
2020 place_labels( this_plot->labels->next, LAYER_PLOTLABELS, TRUE);
2025 PLOT_IMAGE(this_plot, IC_PALETTE);
2029 PLOT_IMAGE(this_plot, IC_RGB);
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);
2045 yl = keybox.yb + yl_ref + key_entry_height/2 - yl;
2046 if (key_count >= key_rows) {
2051 yl = yl - key_entry_height;
2055 /* DRAW TICS AND GRID */
2056 if (grid_layer == 1)
2059 /* REDRAW PLOT BORDER */
2060 if (draw_border && border_layer == 1)
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);
2068 /* And rectangles */
2069 place_rectangles( first_object, 1, 2, clip_area );
2072 place_labels( first_label, 1, FALSE );
2074 #ifdef EAM_HISTOGRAMS
2075 /* PLACE HISTOGRAM TITLES */
2076 place_histogram_titles();
2082 /* Release the palette if we have used one (PostScript only?) */
2083 if (is_plot_with_palette() && term->previous_palette)
2084 term->previous_palette();
2091 * Plot the curves in IMPULSES style
2095 plot_impulses(struct curve_points *plot, int yaxis_x, int xaxis_y)
2099 struct termentry *t = term;
2101 for (i = 0; i < plot->p_count; i++) {
2102 switch (plot->points[i].type) {
2104 x = map_x(plot->points[i].x);
2105 y = map_y(plot->points[i].y);
2108 if (!inrange(plot->points[i].x, X_AXIS.min, X_AXIS.max))
2111 double clipped_y = plot->points[i].y;
2113 x = map_x(plot->points[i].x);
2114 cliptorange(clipped_y, Y_AXIS.min, Y_AXIS.max);
2115 y = map_y(clipped_y);
2119 default: /* just a safety */
2125 /* variable color read from data column */
2126 check_for_variable_color(plot, &plot->points[i]);
2129 (*t->move) (yaxis_x, xaxis_y);
2131 (*t->move) (x, xaxis_y);
2132 (*t->vector) (x, y);
2138 * Plot the curves in LINES style
2141 plot_lines(struct curve_points *plot)
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 */
2150 for (i = 0; i < plot->p_count; i++) {
2152 /* rgb variable - color read from data column */
2153 check_for_variable_color(plot, &plot->points[i]);
2155 switch (plot->points[i].type) {
2157 x = map_x(plot->points[i].x);
2158 y = map_y(plot->points[i].y);
2160 if (prev == INRANGE) {
2161 (*t->vector) (x, y);
2162 } else if (prev == OUTRANGE) {
2163 /* from outrange to inrange */
2167 edge_intersect(plot->points, i, &ex, &ey);
2168 (*t->move) (map_x(ex), map_y(ey));
2169 (*t->vector) (x, y);
2171 } else { /* prev == UNDEFINED */
2173 (*t->vector) (x, y);
2179 if (prev == INRANGE) {
2180 /* from inrange to outrange */
2182 edge_intersect(plot->points, i, &ex, &ey);
2183 (*t->vector) (map_x(ex), map_y(ey));
2185 } else if (prev == OUTRANGE) {
2186 /* from outrange to outrange */
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]));
2196 default: /* just a safety */
2201 prev = plot->points[i].type;
2205 /* plot_filledcurves:
2206 * Plot FILLED curves.
2207 * pm 8.9.2001 (main routine); pm 5.1.2002 (full support for options)
2210 /* finalize and draw the filled curve */
2212 finish_filled_curve(
2215 struct curve_points *plot)
2217 filledcurves_opts *filledcurves_options = &plot->filledcurves_options;
2221 if (points <= 0) return;
2222 /* add side (closing) points */
2223 switch (filledcurves_options->closeto) {
2224 case FILLEDCURVES_CLOSED:
2226 case FILLEDCURVES_X1:
2227 corners[points].x = corners[points-1].x;
2228 corners[points+1].x = corners[0].x;
2230 corners[points+1].y = axis_array[FIRST_Y_AXIS].term_lower;
2233 case FILLEDCURVES_X2:
2234 corners[points].x = corners[points-1].x;
2235 corners[points+1].x = corners[0].x;
2237 corners[points+1].y = axis_array[FIRST_Y_AXIS].term_upper;
2240 case FILLEDCURVES_Y1:
2241 corners[points].y = corners[points-1].y;
2242 corners[points+1].y = corners[0].y;
2244 corners[points+1].x = axis_array[FIRST_X_AXIS].term_lower;
2247 case FILLEDCURVES_Y2:
2248 corners[points].y = corners[points-1].y;
2249 corners[points+1].y = corners[0].y;
2251 corners[points+1].x = axis_array[FIRST_X_AXIS].term_upper;
2254 case FILLEDCURVES_ATX1:
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;
2264 case FILLEDCURVES_ATX2:
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;
2274 case FILLEDCURVES_ATY1:
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;
2284 case FILLEDCURVES_ATY2:
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;
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 */
2301 case FILLEDCURVES_BETWEEN:
2302 side = (corners[points].x > 0) ? 1 : -1;
2304 default: /* the polygon is closed by default */
2309 { /* for debugging purposes */
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");
2319 /* Check for request to fill only on one side of a bounding line */
2320 if (filledcurves_options->oneside > 0 && side < 0)
2322 if (filledcurves_options->oneside < 0 && side > 0)
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);
2332 plot_filledcurves(struct curve_points *plot)
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 */
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 */
2351 if (!t->filled_polygon) { /* filled polygons are not available */
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
2360 if (plot->plot_type == DATA)
2361 memcpy(&plot->filledcurves_options, &filledcurves_opts_data, sizeof(filledcurves_opts));
2363 memcpy(&plot->filledcurves_options, &filledcurves_opts_func, sizeof(filledcurves_opts));
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);
2375 case FILLEDCURVES_ATX2:
2376 MYNOMIN(plot->filledcurves_options.at,SECOND_X_AXIS);
2377 MYNOMAX(plot->filledcurves_options.at,SECOND_X_AXIS);
2379 case FILLEDCURVES_ATY1:
2380 MYNOMIN(plot->filledcurves_options.at,FIRST_Y_AXIS);
2381 MYNOMAX(plot->filledcurves_options.at,FIRST_Y_AXIS);
2383 case FILLEDCURVES_ATY2:
2384 MYNOMIN(plot->filledcurves_options.at,SECOND_Y_AXIS);
2385 MYNOMAX(plot->filledcurves_options.at,SECOND_Y_AXIS);
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);
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");
2402 switch (plot->points[i].type) {
2404 x = map_x(plot->points[i].x);
2405 y = map_y(plot->points[i].y);
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);
2415 corners[points].x = map_x(ex);
2416 corners[points++].y = map_y(ey);
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);
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);
2435 /* vector(map_x(ex),map_y(ey)); */
2436 corners[points].x = map_x(ex);
2437 corners[points++].y = map_y(ey);
2439 corners[points].x = x;
2440 corners[points++].y = y;
2442 } else if (!clip_lines1) {
2443 finish_filled_curve(points, corners, plot);
2446 corners[points].x = x;
2447 corners[points++].y = y;
2450 finish_filled_curve(points, corners, plot);
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);
2457 corners[points].x = x;
2458 corners[points++].y = y;
2460 } else { /* prev == UNDEFINED */
2461 finish_filled_curve(points, corners, plot);
2464 corners[points].x = x;
2465 corners[points++].y = y;
2467 corners[points].x = x;
2468 corners[points++].y = y;
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);
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);
2488 } else if (prev == OUTRANGE) {
2489 /* from outrange to outrange */
2491 if (two_edge_intersect(plot->points, i, lx, ly)) {
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]);
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;
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);
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]);
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;
2528 else if (clip_lines2) {
2529 if (two_edge_intersect(plot->points, i, lx, ly)) {
2530 finish_filled_curve(points, corners, plot);
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]);
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?
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);
2554 exit_edge = reentry_edge = first_entry = 0;
2558 default: /* just a safety */
2561 prev = plot->points[i].type;
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);
2571 finish_filled_curve(points, corners, plot);
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.
2580 fill_missing_corners(gpiPoint *corners, int *points, int exit, int reentry, int updown, int leftright)
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);
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);
2608 * Fill the area between two curves
2611 plot_betweencurves(struct curve_points *plot)
2613 double x1, x2, yl1, yu1, yl2, yu2;
2617 /* If terminal doesn't support filled polygons, approximate with bars */
2618 if (!term->filled_polygon) {
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.
2628 for (i = 0; i < plot->p_count-1; i++) {
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)
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;
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);
2651 fill_between(x1,yl1,yu1,x2,yl2,yu2,plot);
2658 double x1, double yl1, double yu1, double x2, double yl2, double yu2,
2659 struct curve_points *plot)
2661 double xmin, xmax, ymin, ymax, dx, dy1, dy2;
2665 struct { double x,y; } corners[8];
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)))
2675 /* Clip end segments. It would be nice to use edge_intersect() here, */
2676 /* but as currently written it cannot handle the second curve. */
2679 yl1 += (yl2-yl1) * (xmin - x1) / dx;
2680 yu1 += (yu2-yu1) * (xmin - x1) / dx;
2684 yl2 += (yl2-yl1) * (xmax - x2) / dx;
2685 yu2 += (yu2-yu1) * (xmax - x2) / dx;
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)
2695 if (yl1>ymax && yu1>ymax && yl2>ymax && yu2>ymax)
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);
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)); \
2713 INTERPOLATE( yu1, yu2, ymin );
2714 INTERPOLATE( yu1, yu2, ymax );
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);
2721 INTERPOLATE( yl1, yl2, ymin );
2722 INTERPOLATE( yl1, yl2, ymax );
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;
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);
2743 * Plot the curves in STEPS style
2746 plot_steps(struct curve_points *plot)
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 */
2756 for (i = 0; i < plot->p_count; i++) {
2757 switch (plot->points[i].type) {
2759 x = map_x(plot->points[i].x);
2760 y = map_y(plot->points[i].y);
2762 if (prev == INRANGE) {
2763 (*t->vector) (x, yprev);
2764 (*t->vector) (x, y);
2765 } else if (prev == OUTRANGE) {
2766 /* from outrange to inrange */
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);
2775 } else { /* prev == UNDEFINED */
2777 (*t->vector) (x, y);
2783 if (prev == INRANGE) {
2784 /* from inrange to outrange */
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));
2790 } else if (prev == OUTRANGE) {
2791 /* from outrange to outrange */
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]));
2802 default: /* just a safety */
2807 prev = plot->points[i].type;
2813 * Plot the curves in STEPS style by step on forward yvalue
2816 plot_fsteps(struct curve_points *plot)
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 */
2826 for (i = 0; i < plot->p_count; i++) {
2827 switch (plot->points[i].type) {
2829 x = map_x(plot->points[i].x);
2830 y = map_y(plot->points[i].y);
2832 if (prev == INRANGE) {
2833 (*t->vector) (xprev, y);
2834 (*t->vector) (x, y);
2835 } else if (prev == OUTRANGE) {
2836 /* from outrange to inrange */
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);
2845 } else { /* prev == UNDEFINED */
2847 (*t->vector) (x, y);
2853 if (prev == INRANGE) {
2854 /* from inrange to outrange */
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));
2860 } else if (prev == OUTRANGE) {
2861 /* from outrange to outrange */
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]));
2872 default: /* just a safety */
2877 prev = plot->points[i].type;
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;
2886 /* NOTE: I'd have made the comp.function 'static', but the HP-sUX gcc
2887 * bug seems to forbid that :-( */
2889 histeps_compare(SORTFUNC_ARGS p1, SORTFUNC_ARGS p2)
2891 double x1=histeps_current_plot->points[*(int *)p1].x;
2892 double x2=histeps_current_plot->points[*(int *)p2].x;
2902 * Plot the curves in HISTEPS style
2905 plot_histeps(struct curve_points *plot)
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 */
2914 /* preliminary count of points inside array */
2916 for (i = 0; i < plot->p_count; i++)
2917 if (plot->points[i].type == INRANGE || plot->points[i].type == OUTRANGE)
2920 return; /* cannot plot less than 2 points */
2922 gl = gp_alloc(goodcount * sizeof(int), "histeps valid point mapping");
2924 /* fill gl array with indexes of valid (non-undefined) points. */
2926 for (i = 0; i < plot->p_count; i++)
2927 if (plot->points[i].type == INRANGE || plot->points[i].type == OUTRANGE) {
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;
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). */
2943 y_null = GPMIN(Y_AXIS.min, Y_AXIS.max);
2947 x = (3.0 * plot->points[gl[0]].x - plot->points[gl[1]].x) / 2.0;
2952 (*t->move) (xl, yl);
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);
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);
2974 * Draw vertical line for the histeps routine.
2975 * Performs clipping.
2977 /* HBB 20010214: renamed parameters. xl vs. x1 is just _too_ easy to
2981 int *cur_x, int *cur_y, /* keeps track of "cursor" position */
2983 double y1, double y2) /* coordinates of vertical line */
2985 struct termentry *t = term;
2988 /* FIXME HBB 20010215: wouldn't it be simpler to call
2989 * draw_clip_line() instead? And in histeps_horizontal(), too, of
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))
2997 if ((x < X_AXIS.max) || (x > X_AXIS.min))
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))
3005 if (y1 < Y_AXIS.min)
3007 if (y1 > Y_AXIS.max)
3009 if (y2 < Y_AXIS.min)
3011 if (y2 > Y_AXIS.max)
3014 if ((y1 < Y_AXIS.max && y2 < Y_AXIS.max)
3015 || (y1 > Y_AXIS.min && y2 > Y_AXIS.min))
3018 if (y1 < Y_AXIS.max)
3020 if (y1 > Y_AXIS.min)
3022 if (y2 < Y_AXIS.max)
3024 if (y2 > Y_AXIS.min)
3031 if (y1m != *cur_y || xm != *cur_x)
3032 (*t->move) (xm, y1m);
3033 (*t->vector) (xm, y2m);
3041 * Draw horizontal line for the histeps routine.
3042 * Performs clipping.
3046 int *cur_x, int *cur_y, /* keeps track of "cursor" position */
3047 double x1, double x2,
3048 double y) /* coordinates of vertical line */
3050 struct termentry *t = term;
3053 /* HBB 20010215: reversed axes need special treatment, here: */
3055 if (Y_AXIS.min <= Y_AXIS.max) {
3056 if ((y < Y_AXIS.min) || (y > Y_AXIS.max))
3059 if ((y < Y_AXIS.max) || (y > Y_AXIS.min))
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))
3068 if (x1 < X_AXIS.min)
3070 if (x1 > X_AXIS.max)
3072 if (x2 < X_AXIS.min)
3074 if (x2 > X_AXIS.max)
3077 if ((x1 < X_AXIS.max && x2 < X_AXIS.max)
3078 || (x1 > X_AXIS.min && x2 > X_AXIS.min))
3081 if (x1 < X_AXIS.max)
3083 if (x1 > X_AXIS.min)
3085 if (x2 < X_AXIS.max)
3087 if (x2 > X_AXIS.min)
3094 if (x1m != *cur_x || ym != *cur_y)
3095 (*t->move) (x1m, ym);
3096 (*t->vector) (x2m, ym);
3105 * Plot the curves in ERRORBARS style
3106 * we just plot the bars; the points are plotted in plot_points
3109 plot_bars(struct curve_points *plot)
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 */
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 */
3125 /* Limitation: no boxes with x errorbars */
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)
3135 || (plot->plot_style == FILLEDCURVES) /* Only if term has no filled_polygon! */
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)
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;
3155 x += plot->histogram->start + 0.5;
3156 /* Calculate width also */
3157 halfwidth = (plot->points[i].xhigh - plot->points[i].xlow)
3158 / (2. * clustersize);
3161 if (!inrange(x, X_AXIS.min, X_AXIS.max))
3165 /* check to see if in yrange */
3166 y = plot->points[i].y;
3167 if (!inrange(y, Y_AXIS.min, Y_AXIS.max))
3171 /* find low and high points of bar, and check yrange */
3172 yhigh = plot->points[i].yhigh;
3173 ylow = plot->points[i].ylow;
3175 high_inrange = inrange(yhigh, Y_AXIS.min, Y_AXIS.max);
3176 low_inrange = inrange(ylow, Y_AXIS.min, Y_AXIS.max);
3178 /* compute the plot position of yhigh */
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);
3184 yhighM = map_y(Y_AXIS.min);
3186 /* compute the plot position of ylow */
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);
3192 ylowM = map_y(Y_AXIS.min);
3194 if (!high_inrange && !low_inrange && ylowM == yhighM)
3195 /* both out of range on the same side */
3198 /* find low and high points of bar, and check xrange */
3199 xhigh = plot->points[i].xhigh;
3200 xlow = plot->points[i].xlow;
3202 #ifdef EAM_HISTOGRAMS
3203 if (plot->plot_style == HISTOGRAMS) {
3204 xlowM = map_x(x-halfwidth);
3205 xhighM = map_x(x+halfwidth);
3208 high_inrange = inrange(xhigh, X_AXIS.min, X_AXIS.max);
3209 low_inrange = inrange(xlow, X_AXIS.min, X_AXIS.max);
3211 /* compute the plot position of xhigh */
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);
3217 xhighM = map_x(X_AXIS.min);
3219 /* compute the plot position of xlow */
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);
3225 xlowM = map_x(X_AXIS.min);
3227 if (!high_inrange && !low_inrange && xlowM == xhighM)
3228 /* both out of range on the same side */
3230 #ifdef EAM_HISTOGRAMS
3234 /* by here everything has been mapped */
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);
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
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);
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
3282 (*t->vector) (xhighM, yhighM);
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;
3294 /* draw the bottom tic */
3295 (*t->move) ((unsigned int) x1, (unsigned int) y1);
3296 (*t->vector) ((unsigned int) x2, (unsigned int) y2);
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 */
3307 } /* HBB 981130: see above */
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)) {
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)
3321 /* check to see if in yrange */
3322 y = plot->points[i].y;
3323 if (!inrange(y, Y_AXIS.min, Y_AXIS.max))
3327 /* find low and high points of bar, and check xrange */
3328 xhigh = plot->points[i].xhigh;
3329 xlow = plot->points[i].xlow;
3331 high_inrange = inrange(xhigh, X_AXIS.min, X_AXIS.max);
3332 low_inrange = inrange(xlow, X_AXIS.min, X_AXIS.max);
3334 /* compute the plot position of xhigh */
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);
3340 xhighM = map_x(X_AXIS.min);
3342 /* compute the plot position of xlow */
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);
3348 xlowM = map_x(X_AXIS.min);
3350 if (!high_inrange && !low_inrange && xlowM == xhighM)
3351 /* both out of range on the same side */
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));
3364 } /* if xerrorbars OR xyerrorbars OR xerrorlines OR xyerrorlines */
3368 * EAM Sep 2002 - Consolidate BOXES and FILLEDBOXES
3371 plot_boxes(struct curve_points *plot, int xaxis_y)
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 */
3379 #ifdef EAM_HISTOGRAMS
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)
3387 if (histogram_opts.type == HT_STACKED_IN_LAYERS && plot->histogram_sequence == 0)
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;
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;
3406 stack_count = newsize;
3411 for (i = 0; i < plot->p_count; i++) {
3413 switch (plot->points[i].type) {
3416 if (plot->points[i].z < 0.0) {
3417 /* need to auto-calc width */
3418 if (prev != UNDEFINED)
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;
3428 if (i < plot->p_count - 1) {
3429 if (plot->points[i + 1].type != UNDEFINED)
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;
3442 if (prev == UNDEFINED)
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;
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;
3456 cliptorange(temp_y, Y_AXIS.min, Y_AXIS.max);
3457 xaxis_y = map_y(temp_y);
3458 dyt = plot->points[i].yhigh;
3460 dyt = plot->points[i].y;
3463 #ifdef EAM_HISTOGRAMS
3464 if (plot->plot_style == HISTOGRAMS) {
3466 int histogram_linetype = i;
3467 if (plot->histogram->startcolor > 0)
3468 histogram_linetype += plot->histogram->startcolor;
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;
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;
3495 switch (histogram_opts.type) {
3496 case HT_STACKED_IN_TOWERS:
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);
3504 (*t->linetype)(histogram_linetype);
3505 plot->fill_properties.fillpattern = histogram_linetype;
3507 case HT_STACKED_IN_LAYERS:
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;
3514 dyb = stackheight[ix].ylow;
3515 dyt += stackheight[ix].ylow;
3516 stackheight[ix].ylow += plot->points[i].y;
3519 if ((Y_AXIS.min < Y_AXIS.max && dyb < Y_AXIS.min)
3520 || (Y_AXIS.max < Y_AXIS.min && 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))
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);
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))
3550 /* Variable color */
3551 if (plot->plot_style == BOXES) {
3552 check_for_variable_color(plot, &plot->points[i]);
3555 if ((plot->fill_properties.fillstyle != FS_EMPTY) && t->fillbox) {
3563 /* avoid negative width/height */
3573 style = style_from_fill(&plot->fill_properties);
3575 if (plot->lp_properties.use_palette && t->filled_polygon) {
3576 (*t->filled_polygon)(4, fill_corners(style,x,y,w-1,h-1));
3578 (*t->fillbox) (style, x, y, w, h);
3580 /* FIXME EAM - Is this still correct??? */
3581 if (strcmp(t->name, "fig") == 0) break;
3583 if (plot->fill_properties.border_linetype == LT_NODRAW)
3585 if (plot->fill_properties.border_linetype != LT_UNDEFINED)
3586 (*t->linetype)(plot->fill_properties.border_linetype);
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);
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);
3604 } /* case OUTRANGE, INRANGE */
3606 default: /* just a safety */
3611 } /* switch point-type */
3613 prev = plot->points[i].type;
3621 * Plot the curves in POINTSTYLE style
3624 plot_points(struct curve_points *plot)
3628 struct termentry *t = term;
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 */
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)) {
3641 check_for_variable_color(plot, &plot->points[i]);
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);
3653 * Plot the curves in DOTS style
3656 plot_dots(struct curve_points *plot)
3660 struct termentry *t = term;
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);
3674 * Plot the curves in VECTORS style
3677 plot_vectors(struct curve_points *plot)
3681 struct termentry *t = term;
3682 struct coordinate points[2];
3684 double lx[2], ly[2];
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));
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;
3695 if (points[0].type == UNDEFINED)
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]);
3703 if (inrange(points[1].x, X_AXIS.min, X_AXIS.max)
3704 && inrange(points[1].y, Y_AXIS.min, Y_AXIS.max)) {
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 */
3716 edge_intersect(points, 1, &ex, &ey);
3719 if (plot->arrow_properties.head & END_HEAD)
3720 (*t->arrow) (x1, y1, x2, y2, END_HEAD);
3722 (*t->arrow) (x1, y1, x2, y2, NOHEAD);
3727 points[1].type = OUTRANGE;
3728 if (points[0].type == INRANGE) {
3729 /* from inrange to outrange */
3731 x1 = map_x(points[0].x);
3732 y1 = map_y(points[0].y);
3733 edge_intersect(points, 1, &ex, &ey);
3736 if (plot->arrow_properties.head & BACKHEAD)
3737 (*t->arrow) (x2, y2, x1, y1, BACKHEAD);
3739 (*t->arrow) (x1, y1, x2, y2, NOHEAD);
3741 } else if (points[0].type == OUTRANGE) {
3742 /* from outrange to outrange */
3744 if (two_edge_intersect(points, 1, lx, ly)) {
3749 (*t->arrow) (x1, y1, x2, y2, NOHEAD);
3758 /* plot_f_bars() - finance bars */
3760 plot_f_bars(struct curve_points *plot)
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);
3770 for (i = 0; i < plot->p_count; i++) {
3771 /* undefined points don't count */
3772 if (plot->points[i].type == UNDEFINED)
3775 /* check to see if in xrange */
3776 x = plot->points[i].x;
3777 if (!inrange(x, X_AXIS.min, X_AXIS.max))
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;
3787 high_inrange = inrange(yhigh, Y_AXIS.min, Y_AXIS.max);
3788 low_inrange = inrange(ylow, Y_AXIS.min, Y_AXIS.max);
3790 /* compute the plot position of yhigh */
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);
3796 yhighM = map_y(Y_AXIS.min);
3798 /* compute the plot position of ylow */
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);
3804 ylowM = map_y(Y_AXIS.min);
3806 if (!high_inrange && !low_inrange && ylowM == yhighM)
3807 /* both out of range on the same side */
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));
3824 * Plot the curves in CANDLESTICSK style
3825 * we just plot the bars; the points are not plotted
3828 plot_c_bars(struct curve_points *plot)
3830 struct termentry *t = term;
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);
3840 for (i = 0; i < plot->p_count; i++) {
3841 /* undefined points don't count */
3842 if (plot->points[i].type == UNDEFINED)
3845 /* check to see if in xrange */
3846 x = plot->points[i].x;
3847 if (!inrange(x, X_AXIS.min, X_AXIS.max))
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;
3857 /* HBB 20010928: To make code match the documentation, ensure
3858 * yhigh is actually higher than ylow */
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);
3868 /* compute the plot position of yhigh */
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);
3875 yhighM = map_y(axis_array[y_axis].min);
3877 /* compute the plot position of ylow */
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);
3884 ylowM = map_y(axis_array[y_axis].min);
3886 if (!high_inrange && !low_inrange && ylowM == yhighM)
3887 /* both out of range on the same side */
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;
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;
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;
3907 dxr = boxwidth / 2.0;
3911 if (prev == UNDEFINED)
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);
3919 xhighM = map_x(dxr);
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);
3928 ymax = map_y(yopen); ymin = map_y(yclose);
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);
3938 if (plot->lp_properties.use_palette && t->filled_polygon)
3939 (*t->filled_polygon)(4, fill_corners(style,x,y,w,h));
3941 (*t->fillbox)(style, x, y, w, h);
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);
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);
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));
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.;
3967 (*t->move) (xlowM+d, yhighM);
3968 (*t->vector) (xhighM-d, yhighM);
3969 (*t->move) (xlowM+d, ylowM);
3970 (*t->vector) (xhighM-d, ylowM);
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);
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);
3991 prev = plot->points[i].type;
3997 * there are LOADS of == style double comparisons in here!
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.
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 */
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 */
4016 if (points[i].type == INRANGE) {
4017 /* swap points around so that ix/ix/iz are INRANGE and
4018 * ox/oy/oz are OUTRANGE
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
4030 * If more than one coord is -VERYLARGE, then can't ratio the "infinities"
4031 * so drop out by returning the INRANGE point.
4033 * Obviously, only need to test the OUTRANGE point (coordinates) */
4034 if (ox == -VERYLARGE || oy == -VERYLARGE) {
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;
4047 /* obviously oy is -VERYLARGE and ox != -VERYLARGE */
4052 * Can't have case (ix == ox && iy == oy) as one point
4053 * is INRANGE and one point is OUTRANGE.
4056 /* horizontal line */
4057 /* assume inrange(iy, Y_AXIS.min, Y_AXIS.max) */
4058 *ey = iy; /* == oy */
4060 if (inrange(X_AXIS.max, ix, ox)) {
4063 } else if (inrange(X_AXIS.min, ix, ox)) {
4067 graph_error("error in edge_intersect");
4070 } else if (ix == ox) {
4072 /* assume inrange(ix, X_AXIS.min, X_AXIS.max) */
4073 *ex = ix; /* == ox */
4075 if (inrange(Y_AXIS.max, iy, oy)) {
4078 } else if (inrange(Y_AXIS.min, iy, oy)) {
4082 graph_error("error in edge_intersect");
4086 /* slanted line of some kind */
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)) {
4094 return BOTTOM_EDGE; /* yes */
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)) {
4103 return TOP_EDGE; /* yes */
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)) {
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)) {
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
4136 /* single edge intersection algorithm for "steps" curves */
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.
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
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 */
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;
4158 if (points[i].type == INRANGE) { /* from OUTRANGE to INRANG */
4159 if (inrange(ay, Y_AXIS.min, Y_AXIS.max)) {
4161 cliptorange(ax, X_AXIS.min, X_AXIS.max);
4165 cliptorange(ay, Y_AXIS.min, Y_AXIS.max);
4168 } else { /* from INRANGE to OUTRANGE */
4169 if (inrange(bx, X_AXIS.min, X_AXIS.max)) {
4171 cliptorange(by, Y_AXIS.min, Y_AXIS.max);
4175 cliptorange(bx, X_AXIS.min, X_AXIS.max);
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.
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
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 */
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;
4205 if (points[i].type == INRANGE) { /* from OUTRANGE to INRANG */
4206 if (inrange(ax, X_AXIS.min, X_AXIS.max)) {
4208 cliptorange(ay, Y_AXIS.min, Y_AXIS.max);
4212 cliptorange(bx, X_AXIS.min, X_AXIS.max);
4215 } else { /* from INRANGE to OUTRANGE */
4216 if (inrange(by, Y_AXIS.min, Y_AXIS.max)) {
4218 cliptorange(bx, X_AXIS.min, X_AXIS.max);
4222 cliptorange(by, Y_AXIS.min, Y_AXIS.max);
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).
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
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 */
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;
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))
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);
4269 cliptorange(by, Y_AXIS.min, Y_AXIS.max);
4274 } else if (inrange(ay, Y_AXIS.min, Y_AXIS.max)) {
4275 /* cross plotspace in x-direction */
4281 } else if (inrange(ax, X_AXIS.min, X_AXIS.max)) {
4282 /* cross plotspace in y-direction */
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).
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
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 */
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;
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))
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);
4332 cliptorange(bx, X_AXIS.min, X_AXIS.max);
4337 } else if (inrange(by, Y_AXIS.min, Y_AXIS.max)) {
4338 /* cross plotspace in x-direction */
4344 } else if (inrange(ax, X_AXIS.min, X_AXIS.max)) {
4345 /* cross plotspace in y-direction */
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).
4365 static TBOOLEAN /* any intersection? */
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 */
4371 /* global X_AXIS.min, X_AXIS.max, Y_AXIS.min, X_AXIS.max */
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;
4379 double t_min, t_max;
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
4385 * If more than one coord is -VERYLARGE, then can't ratio the
4386 * "infinities" so drop out by returning FALSE */
4389 if (ix == -VERYLARGE)
4391 if (ox == -VERYLARGE)
4393 if (iy == -VERYLARGE)
4395 if (oy == -VERYLARGE)
4398 /* either doesn't pass through graph area *or* can't ratio
4399 * infinities to get a direction to draw line, so simply
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 */
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)) {
4429 if (oy == -VERYLARGE || iy == -VERYLARGE) {
4431 if (iy == -VERYLARGE) {
4432 /* swap points so ix/iy don't have a -VERYLARGE component */
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)) {
4454 * Special horizontal/vertical, etc. cases are checked and remaining
4455 * slant lines are checked separately.
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.
4465 * Can have case (ix == ox && iy == oy) as both points OUTRANGE
4467 if (ix == ox && iy == oy) {
4468 /* but as only define single outrange point, can't intersect graph area */
4472 /* line parallel to y axis */
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)) {
4479 if (inrange(Y_AXIS.min, iy, oy)) {
4490 /* already checked case (ix == ox && iy == oy) */
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)) {
4498 if (inrange(X_AXIS.min, ix, ox)) {
4508 /* nasty 2D slanted line in an xy plane */
4510 /* From here on, it's essentially the classical Cyrus-Beck, or
4511 * Liang-Barsky algorithm for line clipping to a rectangle */
4513 Solve parametric equation
4515 (ix, iy) + t (diff_x, diff_y)
4517 where 0.0 <= t <= 1.0 and
4523 t[0] = (X_AXIS.min - ix) / (ox - ix);
4524 t[1] = (X_AXIS.max - ix) / (ox - ix);
4531 t[2] = (Y_AXIS.min - iy) / (oy - iy);
4532 t[3] = (Y_AXIS.max - iy) / (oy - iy);
4539 t_min = GPMAX(GPMAX(t[0], t[2]), 0.0);
4540 t_max = GPMIN(GPMIN(t[1], t[3]), 1.0);
4545 lx[0] = ix + t_min * (ox - ix);
4546 ly[0] = iy + t_min * (oy - iy);
4548 lx[1] = ix + t_max * (ox - ix);
4549 ly[1] = iy + t_max * (oy - iy);
4552 * Can only have 0 or 2 intersection points -- only need test one coord
4554 /* FIXME: this is UGLY. Need an 'almost_inrange()' function */
4556 (X_AXIS.min - 1e-5 * (X_AXIS.max - X_AXIS.min)),
4557 (X_AXIS.max + 1e-5 * (X_AXIS.max - X_AXIS.min)))
4559 (Y_AXIS.min - 1e-5 * (Y_AXIS.max - Y_AXIS.min)),
4560 (Y_AXIS.max + 1e-5 * (Y_AXIS.max - Y_AXIS.min))))
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.
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)
4579 double dx1, dx2, dy1, dy2;
4581 /* If there are no bounding lines in effect, don't bother */
4582 if (!filledcurves_options->oneside)
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;
4592 *ex = filledcurves_options->at;
4593 *ey = points[i-1].y + dy1 * dx1 / (dx1-dx2);
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;
4603 *ex = points[i-1].x + dx1 * dy1 / (dy1-dy2);
4604 *ey = filledcurves_options->at;
4608 case FILLEDCURVES_ATXY:
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 */
4621 /* display a x-axis ticmark - called by gen_ticks */
4622 /* also uses global tic_start, tic_direction, tic_text and tic_just */
4628 struct lp_style_type grid) /* linetype or -2 for no grid */
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);
4635 (void) axis; /* avoid "unused parameter" warning */
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);
4644 int tmpgx, tmpgy, gx, gy;
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) {
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) */
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);
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);
4674 if (keybox.yt < plot_bounds.ytop) {
4675 (*t->move) (x, keybox.yt);
4676 (*t->vector) (x, plot_bounds.ytop);
4679 (*t->move) (x, plot_bounds.ybot);
4680 (*t->vector) (x, plot_bounds.ytop);
4683 term_apply_lp_properties(&border_lp); /* border linetype */
4685 /* we precomputed tic posn and text posn in global vars */
4687 (*t->move) (x, tic_start);
4688 (*t->vector) (x, tic_start + ticsize);
4690 if (tic_mirror >= 0) {
4691 (*t->move) (x, tic_mirror);
4692 (*t->vector) (x, tic_mirror - ticsize);
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 */
4709 /* display a y-axis ticmark - called by gen_ticks */
4710 /* also uses global tic_start, tic_direction, tic_text and tic_just */
4716 struct lp_style_type grid) /* linetype or -2 */
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);
4723 (void) axis; /* avoid "unused parameter" warning */
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);
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) {
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) */
4743 clip_vector(map_x(x), map_y(y));
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);
4753 if (keybox.xr < plot_bounds.xright) {
4754 (*t->move) (keybox.xr, y);
4755 (*t->vector) (plot_bounds.xright, y);
4758 (*t->move) (plot_bounds.xleft, y);
4759 (*t->vector) (plot_bounds.xright, y);
4762 term_apply_lp_properties(&border_lp); /* border linetype */
4764 /* we precomputed tic posn and text posn */
4766 (*t->move) (tic_start, y);
4767 (*t->vector) (tic_start + ticsize, y);
4769 if (tic_mirror >= 0) {
4770 (*t->move) (tic_mirror, y);
4771 (*t->vector) (tic_mirror - ticsize, y);
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 */
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
4793 label_width(const char *str, int *lines)
4795 char *lab = NULL, *s, *e;
4799 lab = gp_alloc(strlen(str) + 2, "in label_width");
4803 while ((e = (char *) strchr(s, '\n')) != NULL) {
4805 len = estimate_strlen(s); /* = e-s ? */
4808 if (len || l || *str == '\n')
4812 /* lines = NULL => not interested - div */
4821 /*{{{ map_position, wrapper, which maps double to int */
4824 struct position *pos,
4829 map_position_double(pos, &xx, &yy, what);
4836 /*{{{ map_position_double */
4838 map_position_double(
4839 struct position *pos,
4840 double *x, double *y,
4843 switch (pos->scalex) {
4846 double xx = axis_log_value_checked(FIRST_X_AXIS, pos->x, what);
4847 *x = AXIS_MAP(FIRST_X_AXIS, xx);
4852 double xx = axis_log_value_checked(SECOND_X_AXIS, pos->x, what);
4853 *x = AXIS_MAP(SECOND_X_AXIS, xx);
4858 *x = plot_bounds.xleft + pos->x * (plot_bounds.xright - plot_bounds.xleft);
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);
4871 register struct termentry *t = term;
4872 *x = pos->x * t->h_char;
4876 switch (pos->scaley) {
4879 double yy = axis_log_value_checked(FIRST_Y_AXIS, pos->y, what);
4880 *y = AXIS_MAP(FIRST_Y_AXIS, yy);
4885 double yy = axis_log_value_checked(SECOND_Y_AXIS, pos->y, what);
4886 *y = AXIS_MAP(SECOND_Y_AXIS, yy);
4891 *y = plot_bounds.ybot + pos->y * (plot_bounds.ytop - plot_bounds.ybot);
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);
4904 register struct termentry *t = term;
4905 *y = pos->y * t->v_char;
4915 /*{{{ map_position_r */
4918 struct position *pos,
4919 double *x, double *y,
4922 switch (pos->scalex) {
4925 double xx = axis_log_value_checked(FIRST_X_AXIS, pos->x, what);
4926 *x = xx * axis_array[FIRST_X_AXIS].term_scale;
4931 double xx = axis_log_value_checked(SECOND_X_AXIS, pos->x, what);
4932 *x = xx * axis_array[SECOND_X_AXIS].term_scale;
4937 *x = pos->x * (plot_bounds.xright - plot_bounds.xleft);
4942 struct termentry *t = term;
4943 *x = pos->x * (t->xmax - 1);
4948 register struct termentry *t = term;
4949 *x = pos->x * t->h_char;
4953 switch (pos->scaley) {
4956 double yy = axis_log_value_checked(FIRST_Y_AXIS, pos->y, what);
4957 *y = yy * axis_array[FIRST_Y_AXIS].term_scale;
4962 double yy = axis_log_value_checked(SECOND_Y_AXIS, pos->y, what);
4963 *y = yy * axis_array[SECOND_Y_AXIS].term_scale;
4968 *y = pos->y * (plot_bounds.ytop - plot_bounds.ybot);
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);
4981 register struct termentry *t = term;
4982 *y = pos->y * t->v_char;
4994 term_apply_lp_properties(&border_lp); /* border linetype */
4995 if (border_complete)
4997 (*term->move) (plot_bounds.xleft, plot_bounds.ytop);
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);
5008 (*term->move) (plot_bounds.xleft, plot_bounds.ybot);
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);
5020 (*term->move) (plot_bounds.xright, plot_bounds.ybot);
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);
5032 (*term->move) (plot_bounds.xright, plot_bounds.ytop);
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);
5044 (*term->move) (plot_bounds.xleft, plot_bounds.ytop);
5047 if (border_complete)
5052 #ifdef EAM_HISTOGRAMS
5054 init_histogram(struct histogram_style *histogram, char *title)
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;
5069 free_histlist(struct histogram_style *hist)
5073 if (hist->title.text)
5074 free(hist->title.text);
5076 free_histlist(hist->next);
5083 place_histogram_titles()
5085 histogram_style *hist = &histogram_opts;
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,
5092 x = map_x((hist->start + hist->end) / 2.);
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);
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
5115 struct curve_points *this_plot,
5118 struct termentry *t,
5121 /* Clip key box against canvas */
5122 BoundingBox *clip_save = clip_area;
5123 if (term->flags & TERM_CAN_CLIP)
5126 clip_area = &canvas;
5128 /* Draw key text in black */
5129 (*t->linetype)(LT_BLACK);
5131 if (key->just == GPKEY_LEFT) {
5132 write_multiline(xl + key_text_left, yl, title, LEFT, JUST_TOP, 0, NULL);
5134 if ((*t->justify_text) (RIGHT)) {
5135 write_multiline(xl + key_text_right, yl, title, RIGHT, JUST_TOP, 0, NULL);
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);
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);
5150 /* draw sample depending on bits set in plot_style */
5151 if (this_plot->plot_style & PLOT_STYLE_HAS_FILL
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;
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));
5165 (*t->fillbox)(style,x,y,w,h);
5168 if (fs->fillstyle != FS_EMPTY && fs->border_linetype != LT_UNDEFINED)
5169 (*t->linetype)(fs->border_linetype);
5170 if (fs->border_linetype != LT_NODRAW) {
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);
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);
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);
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);
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);
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)
5218 /* Restore previous clipping area */
5219 clip_area = clip_save;
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.
5230 style_from_fill(struct fill_style_type *fs)
5234 switch( fs->fillstyle ) {
5236 fillpar = fs->filldensity;
5237 style = ((fillpar & 0xfff) << 4) + FS_SOLID;
5240 fillpar = fs->fillpattern;
5241 style = ((fillpar & 0xfff) << 4) + FS_PATTERN;
5244 /* solid fill with background color */
5254 * The equivalent of t->fillbox() except that it uses PM3D colors instead
5255 * of plain line types
5258 fill_corners(int style, unsigned int x, unsigned int y, unsigned int w, unsigned int h)
5260 static gpiPoint corner[4];
5262 corner[0].style = style;
5277 check_for_variable_color(struct curve_points *plot, struct coordinate *point)
5279 if ((plot->lp_properties.pm3d_color.value < 0.0)
5280 && (plot->lp_properties.pm3d_color.type == TC_RGB)) {
5281 set_rgbcolor(point->yhigh);
5283 } else if (plot->lp_properties.pm3d_color.type == TC_Z) {
5284 set_color( cb2gray(point->yhigh) );
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);
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)
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
5309 /* hyperplane_between_points:
5310 * Compute the hyperplane representation of a line passing
5311 * between two points.
5314 hyperplane_between_points(double *p1, double *p2, double *w, double *b)
5316 w[0] = p1[1] - p2[1];
5317 w[1] = p2[0] - p1[0];
5318 *b = -(w[0]*p1[0] + w[1]*p1[1]);
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.
5333 plot_image_or_update_axes(void *plot, t_imagecolor pixel_planes, TBOOLEAN project_points, TBOOLEAN update_axes)
5336 struct coordinate GPHUGE *points;
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};
5351 if (project_points) {
5352 points = ((struct surface_points *)plot)->iso_crvs->points;
5353 p_count = ((struct surface_points *)plot)->iso_crvs->p_count;
5355 points = ((struct curve_points *)plot)->points;
5356 p_count = ((struct curve_points *)plot)->p_count;
5360 fprintf(stderr, ERROR_NOTICE("No points (visible or invisible) to plot.\n\n"));
5365 fprintf(stderr, ERROR_NOTICE("Image grid must be at least 4 points (2 x 2).\n\n"));
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.
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
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]);
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;
5390 hyperplane_between_points(p_start_corner, p_end_corner, w_hyp, &b_hyp);
5392 for (K = p_count, i=1; i < p_count; i++) {
5394 if (project_points) {
5395 map3d_xy_double(points[i].x, points[i].y, points[i].z, &p[0], &p[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.
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];
5413 /* The first point on the opposite side of the hyperplane is the
5414 * candidate for the first point of the second scan line.
5416 if ((w_hyp[0]*p[0] + w_hyp[1]*p[1] + b_hyp) < 0) {
5424 fprintf(stderr, ERROR_NOTICE("Image grid must be at least 2 x 2.\n\n"));
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);
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]);
5439 p_mid_corner[0] = points[K-1].x;
5440 p_mid_corner[1] = points[K-1].y;
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);
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);
5465 /* Check if the pixel grid is orthogonal and oriented with axes.
5466 * If so, then can use efficient terminal image routines.
5468 {TBOOLEAN rectangular_image = FALSE;
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])) ) ) {
5476 /* If the terminal does not have image support indicate so,
5477 * just once. Then, use polygons to construct pixels.
5480 rectangular_image = TRUE;
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;
5493 if (make_palette() || !term->set_color) {
5494 fprintf(stderr, ERROR_NOTICE("Unable to make palette or set terminal color.\n\n"));
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;
5507 if (rectangular_image) {
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.)
5513 int line_length, i_delta_pixel, i_delta_line, i_start;
5514 int pixel_1_1, pixel_M_N;
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);
5524 /* 3D plots do not use the term_scale mechanism AXIS_SETSCALE(). */
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).
5533 if (fabs(delta_x_grid[0]) > fabs(delta_x_grid[1])) {
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);
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);
5545 /* Assign enough memory for the maximum image size. */
5548 /* If doing color, multiply size by three for RGB triples. */
5549 if (pixel_planes == IC_RGB) {
5553 image = (coordval *) gp_alloc(array_size*sizeof(image[0]),"image");
5555 /* Place points into image array based upon the arrangement of point indices and
5556 * the visibility of pixels.
5558 if (image != NULL) {
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.)
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;
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;
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++) {
5583 double x, y, z, x_low, x_high, y_low, y_high, z_low, z_high;
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;
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.
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
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]) ) )
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) ) )
5617 #define USE_CLIP_POINTS 0
5619 if ( !clip_points ||
5624 if (pixel_1_1 < 0) {
5625 /* First visible point. */
5626 pixel_1_1 = i_image;
5629 line_pixel_count = 1;
5631 if (line_pixel_count == 0)
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."));
5640 pixel_M_N = i_image;
5642 if (pixel_planes == IC_PALETTE) {
5643 image[i_sub_image++] = cb2gray( points[i_image].CRD_COLOR );
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 );
5652 i_image += i_delta_pixel;
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."));
5661 line_pixel_count = 0;
5662 i_image += i_delta_line;
5667 if ( (M > 0) && (N > 0) ) {
5669 /* The information collected to this point is:
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)
5678 /* One of the delta values in each direction is zero, so add. */
5679 if (project_points) {
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);
5690 map3d_xy_double(view_port_x[1], view_port_y[1], view_port_z[1], &x, &y);
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]);
5704 if ( (pixel_planes == IC_PALETTE) || (pixel_planes == IC_RGB) )
5705 (*term->image) (M, N, image, corners, pixel_planes);
5707 fprintf(stderr, ERROR_NOTICE("Invalid pixel color planes specified.\n\n"));
5710 free ((void *)image);
5713 fprintf(stderr, ERROR_NOTICE("Could not allocate memory for image."));
5717 } else { /* !rectangular_image */
5719 if (pixel_planes != IC_RGB) {
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];
5725 if (!term->filled_polygon)
5726 int_error(NO_CARET, "This terminal does not support filled polygons");
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);
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;
5746 for (j=0; j < L; j++) {
5748 double x_line_start, y_line_start, z_line_start;
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;
5754 for (i=0; i < K; i++) {
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. */
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;
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;
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];
5789 if (pixel_in_view || view_in_pixel) {
5791 int N_corners = 0; /* Number of corners. */
5792 gpiPoint corners[5]; /* At most 5 corners. */
5794 corners[0].style = FS_DEFAULT;
5796 if (pixel_in_view) {
5797 if (corner_in_range[0] && corner_in_range[1] && corner_in_range[2] && corner_in_range[3]) {
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,
5806 corners[i_corners].x = x;
5807 corners[i_corners].y = y;
5809 corners[i_corners].x = map_x(p_corners[i_corners].x);
5810 corners[i_corners].y = map_y(p_corners[i_corners].y);
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;
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..
5831 if (N_corners >= 3) {
5832 set_color( cb2gray(points[i_image].CRD_COLOR) );
5833 (*term->filled_polygon) (N_corners, corners);
5841 fprintf(stdout, ERROR_NOTICE("Color boxes cannot handle RGB components.\n\n"));