1 /* GNUPLOT - boundary.c */
2 
3 /*[
4  * Copyright 1986 - 1993, 1998, 2004   Thomas Williams, Colin Kelley
5  *
6  * Permission to use, copy, and distribute this software and its
7  * documentation for any purpose with or without fee is hereby granted,
8  * provided that the above copyright notice appear in all copies and
9  * that both that copyright notice and this permission notice appear
10  * in supporting documentation.
11  *
12  * Permission to modify the software is granted, but not the right to
13  * distribute the complete modified source code.  Modifications are to
14  * be distributed as patches to the released version.  Permission to
15  * distribute binaries produced by compiling modified sources is granted,
16  * provided you
17  *   1. distribute the corresponding source modifications from the
18  *    released version in the form of a patch file along with the binaries,
19  *   2. add special version identification to distinguish your version
20  *    in addition to the base release version number,
21  *   3. provide your name and address as the primary contact for the
22  *    support of your modified version, and
23  *   4. retain our contact information in regard to use of the base
24  *    software.
25  * Permission to distribute the released version of the source code along
26  * with corresponding source modifications in the form of a patch file is
27  * granted with same provisions 2 through 4 for binary distributions.
28  *
29  * This software is provided "as is" without express or implied warranty
30  * to the extent permitted by applicable law.
31 ]*/
32 
33 #include "graphics.h"
34 #include "boundary.h"
35 #include "alloc.h"
36 #include "axis.h"
37 #include "misc.h"
38 #include "pm3d.h"	/* for is_plot_with_palette */
39 
40 #define ERRORBARTIC GPMAX((t->h_tic/2),1)
41 
42 /*{{{  local variables */
43 static int xlablin, x2lablin, ylablin, y2lablin, titlelin, xticlin, x2ticlin;
44 
45 /*{{{  local and global variables */
46 static int key_sample_width;	/* width of line sample */
47 static int key_sample_height;	/* sample itself; does not scale with "set key spacing" */
48 static int key_sample_left;	/* offset from x for left of line sample */
49 static int key_sample_right;	/* offset from x for right of line sample */
50 static int key_text_left;	/* offset from x for left-justified text */
51 static int key_text_right;	/* offset from x for right-justified text */
52 static int key_size_left;	/* size of left bit of key (text or sample, depends on key->reverse) */
53 static int key_size_right;	/* size of right part of key (including padding) */
54 static int key_xleft;		/* Amount of space on the left required by the key */
55 static int max_ptitl_len = 0;	/* max length of plot-titles (keys) */
56 static int ptitl_cnt;		/* count keys with len > 0  */
57 
58 static int key_width;		/* calculate once, then everyone uses it */
59 static int key_height;		/* ditto */
60 static int key_title_height;	/* nominal number of lines * character height */
61 static int key_title_extra;	/* allow room for subscript/superscript */
62 static int time_y, time_x;
63 
64 int title_x, title_y;		/* Used by boundary and by 2D graphics */
65 
66 /*
67  * These quantities are needed in do_plot() e.g. for histogtram title layout
68  */
69 int key_entry_height;		/* bigger of t->v_char, t->v_tic */
70 int key_point_offset;		/* offset from x for point sample */
71 int ylabel_x, y2label_x, xlabel_y, x2label_y;
72 int x2label_yoffset;
73 int ylabel_y, y2label_y, xtic_y, x2tic_y, ytic_x, y2tic_x;
74 int key_rows;
75 int key_cols;
76 int key_count;
77 
78 static int key_col_wth, yl_ref;
79 static int xl, yl;
80 
81 /*{{{  boundary() */
82 /* borders of plotting area
83  * computed once on every call to do_plot
84  *
85  * The order in which things are done has become critical:
86  *  plot_bounds.ytop depends on title, x2label
87  *  plot_bounds.ybot depends on key, if "under"
88  *  once we have these, we can setup the y1 and y2 tics and the
89  *  only then can we calculate plot_bounds.xleft and plot_bounds.xright
90  *  plot_bounds.xright depends also on key RIGHT
91  *  then we can do x and x2 tics
92  *
93  * For set size ratio ..., everything depends on everything else...
94  * not really a lot we can do about that, so we lose if the plot has to
95  * be reduced vertically. But the chances are the
96  * change will not be very big, so the number of tics will not
97  * change dramatically.
98  *
99  * Margin computation redone by Dick Crawford (rccrawford@lanl.gov) 4/98
100  */
101 
102 void
boundary(struct curve_points * plots,int count)103 boundary(struct curve_points *plots, int count)
104 {
105     int yticlin = 0, y2ticlin = 0;
106     legend_key *key = &keyT;
107 
108     struct termentry *t = term;
109     int can_rotate = (*t->text_angle) (TEXT_VERTICAL);
110 
111     int xtic_textheight=0;	/* height of xtic labels */
112     int x2tic_textheight=0;	/* height of x2tic labels */
113     int title_textheight=0;	/* height of title */
114     int xlabel_textheight=0;	/* height of xlabel */
115     int x2label_textheight=0;	/* height of x2label */
116     int ylabel_textwidth=0;	/* width of (rotated) ylabel */
117     int y2label_textwidth=0;	/* width of (rotated) y2label */
118     int timelabel_textwidth=0;	/* width of timestamp */
119     int timelabel_textheight=0;	/* height of timestamp */
120     int ytic_textwidth=0;	/* width of ytic labels */
121     int y2tic_textwidth=0;	/* width of y2tic labels */
122     int x2tic_height=0;		/* 0 for tic_in or no x2tics, ticscale*v_tic otherwise */
123     int xtic_textwidth=0;	/* amount by which the xtic label protrude to the right */
124     int xtic_height=0;
125     int ytic_width=0;
126     int y2tic_width=0;
127     int ttic_textheight=0;	/* vertical clearance for ttics */
128 
129     /* figure out which rotatable items are to be rotated
130      * (ylabel and y2label are rotated if possible) */
131     int vertical_timelabel = can_rotate ? timelabel.rotate : 0;
132     int vertical_xtics  = can_rotate ? axis_array[FIRST_X_AXIS].tic_rotate : 0;
133     int vertical_x2tics = can_rotate ? axis_array[SECOND_X_AXIS].tic_rotate : 0;
134     int vertical_ytics  = can_rotate ? axis_array[FIRST_Y_AXIS].tic_rotate : 0;
135     int vertical_y2tics = can_rotate ? axis_array[SECOND_Y_AXIS].tic_rotate : 0;
136 
137     TBOOLEAN shift_labels_to_border = FALSE;
138 
139     xticlin = ylablin = y2lablin = xlablin = x2lablin = titlelin = 0;
140 
141     /*{{{  count lines in labels and tics */
142     if (title.text)
143 	label_width(title.text, &titlelin);
144     if (axis_array[FIRST_X_AXIS].label.text)
145 	label_width(axis_array[FIRST_X_AXIS].label.text, &xlablin);
146 
147     /* This should go *inside* label_width(), but it messes up the key title */
148     /* Imperfect check for subscripts or superscripts */
149     if ((term->flags & TERM_ENHANCED_TEXT) && axis_array[FIRST_X_AXIS].label.text
150 	&& strpbrk(axis_array[FIRST_X_AXIS].label.text, "_^"))
151 	    xlablin++;
152 
153     if (axis_array[SECOND_X_AXIS].label.text)
154 	label_width(axis_array[SECOND_X_AXIS].label.text, &x2lablin);
155     if (axis_array[FIRST_Y_AXIS].label.text)
156 	label_width(axis_array[FIRST_Y_AXIS].label.text, &ylablin);
157     if (axis_array[SECOND_Y_AXIS].label.text)
158 	label_width(axis_array[SECOND_Y_AXIS].label.text, &y2lablin);
159 
160     if (axis_array[FIRST_X_AXIS].ticmode) {
161 	label_width(axis_array[FIRST_X_AXIS].formatstring, &xticlin);
162 	/* Reserve room for user tic labels even if format of autoticks is "" */
163 	if (xticlin == 0 && axis_array[FIRST_X_AXIS].ticdef.def.user)
164 	    xticlin = 1;
165     }
166 
167     if (axis_array[SECOND_X_AXIS].ticmode)
168 	label_width(axis_array[SECOND_X_AXIS].formatstring, &x2ticlin);
169     if (axis_array[FIRST_Y_AXIS].ticmode)
170 	label_width(axis_array[FIRST_Y_AXIS].formatstring, &yticlin);
171     if (axis_array[SECOND_Y_AXIS].ticmode)
172 	label_width(axis_array[SECOND_Y_AXIS].formatstring, &y2ticlin);
173     /*}}} */
174 
175     /*{{{  preliminary plot_bounds.ytop  calculation */
176 
177     /*     first compute heights of things to be written in the margin */
178 
179 
180     /* Title placement has been reworked for 5.4
181      * NOTE: title_textheight is _not_ the height of the title!
182      * It is the amount of space reserved for the title above the plot.
183      * A negative offset greater than the number of title lines means
184      * that the title will appear inside the boundary and no extra space
185      * needs to be reserved for it above the plot.
186      */
187     title_textheight = 0;
188     if (titlelin) {
189 	title_textheight = t->v_char;	/* Gap of one normal line height */
190 	if (title.font)
191 	    t->set_font(title.font);
192 	title_y = titlelin * t->v_char;
193 	if ((titlelin + title.offset.y) > 0)
194 	    title_textheight += titlelin * t->v_char;
195 	if (title.font)
196 	    t->set_font("");
197 	title_y += 0.5 * t->v_char;	/* Approximate same placement as version 5.2 */
198     }
199 
200     /* Extra space at the top for spiderplot axis label */
201     if (spiderplot)
202 	title_textheight += 1.5 * t->v_char;
203 
204     /* x2label */
205     if (x2lablin) {
206 	double tmpx, tmpy;
207 	map_position_r(&(axis_array[SECOND_X_AXIS].label.offset),
208 			&tmpx, &tmpy, "x2label");
209 	if (axis_array[SECOND_X_AXIS].label.font)
210 	    t->set_font(axis_array[SECOND_X_AXIS].label.font);
211 	x2label_textheight = (int) (x2lablin * t->v_char);
212 	x2label_yoffset = tmpy;
213 	if (axis_array[SECOND_X_AXIS].label.font)
214 	    t->set_font("");
215     } else
216 	x2label_textheight = 0;
217 
218     /* tic labels */
219     if (axis_array[SECOND_X_AXIS].ticmode & TICS_ON_BORDER) {
220 	/* ought to consider tics on axes if axis near border */
221 	x2tic_textheight = (int) (x2ticlin * t->v_char);
222     } else
223 	x2tic_textheight = 0;
224 
225     /* tics */
226     if (axis_array[SECOND_X_AXIS].ticmode & TICS_ON_BORDER) {
227 	x2tic_height = t->v_tic * axis_array[SECOND_X_AXIS].ticscale;
228 	if (axis_array[SECOND_X_AXIS].tic_in)
229 	    x2tic_height = -x2tic_height;
230     } else
231 	x2tic_height = 0;
232 
233     /* Polar (theta) tic labels need space at top and bottom of plot */
234     if (THETA_AXIS.ticmode) {
235 	/* FIXME:  Really 5% of polar grid radius, but we don't know that yet */
236 	ttic_textheight = 2. * t->v_char;
237     }
238 
239     /* timestamp */
240     if (timelabel.text) {
241 	int timelin;
242 	timelabel_textwidth = label_width(timelabel.text, &timelin);
243 	if (vertical_timelabel) {
244 	    timelabel_textheight = timelabel_textwidth * t->v_char;
245 	    timelabel_textwidth = (timelin + 1.5) * t->h_char;
246 	    timelabel.place.y = 0;
247 	} else {
248 	    timelabel_textheight = timelin * t->v_char;
249 	    timelabel_textwidth = timelabel_textwidth * t->h_char;
250 	    /* save textheight for use in do_key_bounds() */
251 	    timelabel.place.y = timelabel_textheight;
252 	}
253     }
254 
255     /* ylabel placement */
256     if (axis_array[FIRST_Y_AXIS].label.text) {
257 	if (can_rotate && axis_array[FIRST_Y_AXIS].label.rotate != 0) {
258 	    ylabel_textwidth = ylablin * t->v_char;
259 	} else {
260 	    /* Trying to estimate this length caused more problems than it solved.
261 	     * For one thing it comes out wrong for text passed to TeX terminals.
262 	     * Assume the common case is roughly 3 character widths and let the
263 	     * user adjust lmargin and offset for longer non-rotated ylabels.
264 	     */
265 	    ylabel_textwidth = 3 * t->h_char;
266 	}
267     }
268 
269     /* y2label placement */
270     if (axis_array[SECOND_Y_AXIS].label.text) {
271 	if (can_rotate && axis_array[SECOND_Y_AXIS].label.rotate != 0) {
272 	    y2label_textwidth = y2lablin * t->v_char;
273 	    if (!axis_array[SECOND_Y_AXIS].ticmode)
274 		y2label_textwidth += 0.5 * t->v_char;
275 	} else {
276 	    /* See above. Estimating true text length causes problems */
277 	    y2label_textwidth = 3 * t->h_char;
278 	}
279     }
280 
281     /* compute plot_bounds.ytop from the various components
282      *     unless tmargin is explicitly specified
283      */
284 
285     plot_bounds.ytop = (int) (0.5 + (ysize + yoffset) * (t->ymax-1));
286 
287     /* Sanity check top and bottom margins, in case the user got confused */
288     if (bmargin.scalex == screen && tmargin.scalex == screen)
289 	if (bmargin.x > tmargin.x) {
290 	    double tmp = bmargin.x;
291 	    bmargin.x = tmargin.x;
292 	    tmargin.x = tmp;
293 	}
294 
295     if (tmargin.scalex == screen) {
296 	/* Specified as absolute position on the canvas */
297 	plot_bounds.ytop = (tmargin.x) * (float)(t->ymax-1);
298     } else if (tmargin.x >=0) {
299 	/* Specified in terms of character height */
300 	plot_bounds.ytop -= (int)(tmargin.x * (float)t->v_char + 0.5);
301     } else {
302 	/* Auto-calculation of space required */
303 	int top_margin = title_textheight;
304 	if (x2label_textheight + x2label_yoffset > 0)
305 	    top_margin += x2label_textheight;
306 
307 	if (timelabel_textheight > top_margin && !timelabel_bottom && !vertical_timelabel)
308 	    top_margin = timelabel_textheight;
309 
310 	top_margin += x2tic_textheight;
311 	top_margin += t->v_char;
312 	if (x2tic_height > 0)
313 	    top_margin += x2tic_height;
314 	top_margin += ttic_textheight;
315 
316 	plot_bounds.ytop -= top_margin;
317 	if (plot_bounds.ytop == (int)(0.5 + (ysize + yoffset) * (t->ymax-1))) {
318 	    /* make room for the end of rotated ytics or y2tics */
319 	    plot_bounds.ytop -= (int) (t->h_char * 2);
320 	}
321     }
322 
323     /*  end of preliminary plot_bounds.ytop calculation }}} */
324 
325 
326     /*{{{  preliminary plot_bounds.xleft, needed for "under" */
327     if (lmargin.scalex == screen)
328 	plot_bounds.xleft = lmargin.x * (float)t->xmax;
329     else
330 	plot_bounds.xleft = xoffset * t->xmax
331 			  + t->h_char * (lmargin.x >= 0 ? lmargin.x : 1);
332     /*}}} */
333 
334 
335     /*{{{  tentative plot_bounds.xright, needed for "under" */
336     if (rmargin.scalex == screen)
337 	plot_bounds.xright = rmargin.x * (float)(t->xmax - 1);
338     else
339 	plot_bounds.xright = (xsize + xoffset) * (t->xmax - 1)
340 			   - t->h_char * (rmargin.x >= 0 ? rmargin.x : 2);
341     /*}}} */
342 
343 
344     /*{{{  preliminary plot_bounds.ybot calculation
345      *     first compute heights of labels and tics */
346 
347     /* tic labels */
348     shift_labels_to_border = FALSE;
349     if (axis_array[FIRST_X_AXIS].ticmode & TICS_ON_AXIS) {
350 	/* FIXME: This test for how close the axis is to the border does not match */
351 	/*        the tests in axis_output_tics(), and assumes FIRST_Y_AXIS.       */
352 	if (!inrange(0.0, axis_array[FIRST_Y_AXIS].min, axis_array[FIRST_Y_AXIS].max))
353 	    shift_labels_to_border = TRUE;
354 	if (0.05 > fabs( axis_array[FIRST_Y_AXIS].min
355 		/ (axis_array[FIRST_Y_AXIS].max - axis_array[FIRST_Y_AXIS].min)))
356 	    shift_labels_to_border = TRUE;
357     }
358     if ((axis_array[FIRST_X_AXIS].ticmode & TICS_ON_BORDER)
359     ||  shift_labels_to_border) {
360 	xtic_textheight = (int) (t->v_char * (xticlin + 1));
361     } else
362 	xtic_textheight =  0;
363 
364     /* tics */
365     if (!axis_array[FIRST_X_AXIS].tic_in
366 	&& ((axis_array[FIRST_X_AXIS].ticmode & TICS_ON_BORDER)
367 	    || ((axis_array[SECOND_X_AXIS].ticmode & TICS_MIRROR)
368 		&& (axis_array[SECOND_X_AXIS].ticmode & TICS_ON_BORDER))))
369 	xtic_height = (int) (t->v_tic * axis_array[FIRST_X_AXIS].ticscale);
370     else
371 	xtic_height = 0;
372 
373     /* xlabel */
374     if (xlablin) {
375 	double tmpx, tmpy;
376 	map_position_r(&(axis_array[FIRST_X_AXIS].label.offset),
377 		       &tmpx, &tmpy, "boundary");
378 	/* offset is subtracted because if > 0, the margin is smaller */
379 	/* textheight is inflated by 0.2 to allow descenders to clear bottom of canvas */
380 	xlabel_textheight = (((float)xlablin + 0.2) * t->v_char - tmpy);
381 	if (!axis_array[FIRST_X_AXIS].ticmode)
382 	    xlabel_textheight += 0.5 * t->v_char;
383     } else
384 	xlabel_textheight = 0;
385 
386     /* compute plot_bounds.ybot from the various components
387      *     unless bmargin is explicitly specified  */
388 
389     plot_bounds.ybot = yoffset * (float)t->ymax;
390 
391     if (bmargin.scalex == screen) {
392 	/* Absolute position for bottom of plot */
393 	plot_bounds.ybot = bmargin.x * (float)t->ymax;
394     } else if (bmargin.x >= 0) {
395 	/* Position based on specified character height */
396 	plot_bounds.ybot += bmargin.x * (float)t->v_char + 0.5;
397     } else {
398 	plot_bounds.ybot += xtic_height + xtic_textheight;
399 	if (xlabel_textheight > 0)
400 	    plot_bounds.ybot += xlabel_textheight;
401 	if (!vertical_timelabel && timelabel_bottom && timelabel_textheight > 0)
402 	    plot_bounds.ybot += timelabel_textheight;
403 	if (plot_bounds.ybot == (int) (t->ymax * yoffset)) {
404 	    /* make room for the end of rotated ytics or y2tics */
405 	    plot_bounds.ybot += (int) (t->h_char * 2);
406 	}
407 	if (spiderplot) /* Extra space at the bottom for spiderplot axis label */
408 	    plot_bounds.ybot += 2 * t->h_char;
409 	/* Last chance for better estimate of space required for ttic labels */
410 	/* It is too late to go back and adjust positions relative to ytop */
411 	if (ttic_textheight > 0) {
412 	    ttic_textheight = 0.05 * (plot_bounds.ytop - plot_bounds.ybot);
413 	    plot_bounds.ybot += ttic_textheight;
414 	}
415     }
416 
417     /*  end of preliminary plot_bounds.ybot calculation }}} */
418 
419     /* Determine the size and position of the key box */
420     if (key->visible) {
421 	/* Count max_len key and number keys with len > 0 */
422 	max_ptitl_len = find_maxl_keys(plots, count, &ptitl_cnt);
423 	do_key_layout(key);
424     }
425 
426     /* Adjust range of dependent axes y and y2 */
427     if (nonlinear(&axis_array[FIRST_Y_AXIS]))
428 	extend_primary_ticrange(&axis_array[FIRST_Y_AXIS]);
429     if (nonlinear(&axis_array[SECOND_Y_AXIS]))
430 	extend_primary_ticrange(&axis_array[SECOND_Y_AXIS]);
431     setup_tics(&axis_array[FIRST_Y_AXIS], 20);
432     setup_tics(&axis_array[SECOND_Y_AXIS], 20);
433 
434     /* Adjust color axis limits if necessary. */
435     if (is_plot_with_palette()) {
436 	axis_checked_extend_empty_range(COLOR_AXIS, "All points of color axis undefined.");
437 	if (color_box.where != SMCOLOR_BOX_NO)
438 	    setup_tics(&axis_array[COLOR_AXIS], 20);
439     }
440 
441     /*{{{  recompute plot_bounds.xleft based on widths of ytics, ylabel etc
442        unless it has been explicitly set by lmargin */
443 
444     /* tic labels */
445     shift_labels_to_border = FALSE;
446     if (axis_array[FIRST_Y_AXIS].ticmode & TICS_ON_AXIS) {
447 	/* FIXME: This test for how close the axis is to the border does not match */
448 	/*        the tests in axis_output_tics(), and assumes FIRST_X_AXIS.       */
449 	if (!inrange(0.0, axis_array[FIRST_X_AXIS].min, axis_array[FIRST_X_AXIS].max))
450 	    shift_labels_to_border = TRUE;
451 	if (0.1 > fabs( axis_array[FIRST_X_AXIS].min
452 	       /  (axis_array[FIRST_X_AXIS].max - axis_array[FIRST_X_AXIS].min)))
453 	    shift_labels_to_border = TRUE;
454     }
455 
456     if ((axis_array[FIRST_Y_AXIS].ticmode & TICS_ON_BORDER)
457     ||  shift_labels_to_border) {
458 	if (vertical_ytics)
459 	    /* HBB: we will later add some white space as part of this, so
460 	     * reserve two more rows (one above, one below the text ...).
461 	     * Same will be done to similar calc.'s elsewhere */
462 	    ytic_textwidth = (int) (t->v_char * (yticlin + 2));
463 	else {
464 	    widest_tic_strlen = 0;	/* reset the global variable ... */
465 	    /* get gen_tics to call widest_tic_callback with all labels
466 	     * the latter sets widest_tic_strlen to the length of the widest
467 	     * one ought to consider tics on axis if axis near border...
468 	     */
469 	    gen_tics(&axis_array[FIRST_Y_AXIS], widest_tic_callback);
470 
471 	    ytic_textwidth = (int) (t->h_char * (widest_tic_strlen + 2));
472 	}
473     } else if (axis_array[FIRST_Y_AXIS].label.text) {
474 	/* substitutes for extra space added to left of ytix labels */
475 	ytic_textwidth = 2 * (t->h_char);
476     } else {
477 	ytic_textwidth = 0;
478     }
479 
480     /* tics */
481     if (!axis_array[FIRST_Y_AXIS].tic_in
482 	&& ((axis_array[FIRST_Y_AXIS].ticmode & TICS_ON_BORDER)
483 	    || ((axis_array[SECOND_Y_AXIS].ticmode & TICS_MIRROR)
484 		&& (axis_array[SECOND_Y_AXIS].ticmode & TICS_ON_BORDER))))
485 	ytic_width = (int) (t->h_tic * axis_array[FIRST_Y_AXIS].ticscale);
486     else
487 	ytic_width = 0;
488 
489     if (lmargin.x < 0) {
490 	/* Auto-calculation */
491 	int space_to_left = key_xleft;
492 
493 	if (space_to_left < timelabel_textwidth && vertical_timelabel)
494 	    space_to_left = timelabel_textwidth;
495 	if (space_to_left < ylabel_textwidth)
496 	    space_to_left = ylabel_textwidth;
497 	plot_bounds.xleft = xoffset * t->xmax;
498 	plot_bounds.xleft += space_to_left;
499 	plot_bounds.xleft += ytic_width + ytic_textwidth;
500 
501 	if (plot_bounds.xleft - ytic_width - ytic_textwidth < 0)
502 	    plot_bounds.xleft = ytic_width + ytic_textwidth;
503 	if (plot_bounds.xleft == t->xmax * xoffset)
504 	    plot_bounds.xleft += t->h_char * 2;
505 	/* DBT 12-3-98  extra margin just in case */
506 	plot_bounds.xleft += 0.5 * t->h_char;
507     }
508     /* Note: we took care of explicit 'set lmargin foo' at line 492 */
509 
510     /*  end of plot_bounds.xleft calculation }}} */
511 
512     /*{{{  recompute plot_bounds.xright based on widest y2tic. y2labels, key "outside"
513        unless it has been explicitly set by rmargin */
514 
515     /* tic labels */
516     if (axis_array[SECOND_Y_AXIS].ticmode & TICS_ON_BORDER) {
517 	if (vertical_y2tics)
518 	    y2tic_textwidth = (int) (t->v_char * (y2ticlin + 2));
519 	else {
520 	    widest_tic_strlen = 0;	/* reset the global variable ... */
521 	    /* get gen_tics to call widest_tic_callback with all labels
522 	     * the latter sets widest_tic_strlen to the length of the widest
523 	     * one ought to consider tics on axis if axis near border...
524 	     */
525 	    gen_tics(&axis_array[SECOND_Y_AXIS], widest_tic_callback);
526 
527 	    y2tic_textwidth = (int) (t->h_char * (widest_tic_strlen + 2));
528 	}
529     } else {
530 	y2tic_textwidth = 0;
531     }
532 
533     /* EAM May 2009
534      * Check to see if any xtic labels are so long that they extend beyond
535      * the right boundary of the plot. If so, allow extra room in the margin.
536      * If the labels are too long to fit even with a big margin, too bad.
537      */
538     if (axis_array[FIRST_X_AXIS].ticdef.def.user) {
539 	struct ticmark *tic = axis_array[FIRST_X_AXIS].ticdef.def.user;
540 	int maxrightlabel = plot_bounds.xright;
541 
542 	/* We don't really know the plot layout yet, but try for an estimate */
543 	axis_set_scale_and_range(&axis_array[FIRST_X_AXIS], plot_bounds.xleft, plot_bounds.xright);
544 
545 	while (tic) {
546 	    if (tic->label) {
547 		double xx;
548 		int length = estimate_strlen(tic->label)
549 			   * cos(DEG2RAD * (double)(axis_array[FIRST_X_AXIS].tic_rotate))
550 			   * term->h_char;
551 
552 		if (inrange(tic->position,
553 		    axis_array[FIRST_X_AXIS].set_min,
554 		    axis_array[FIRST_X_AXIS].set_max)) {
555 			xx = axis_log_value_checked(FIRST_X_AXIS, tic->position, "xtic");
556 			xx = map_x(xx);
557 			xx += (axis_array[FIRST_X_AXIS].tic_rotate) ? length : length /2;
558 			if (maxrightlabel < xx)
559 			    maxrightlabel = xx;
560 		}
561 	    }
562 	    tic = tic->next;
563 	}
564 	xtic_textwidth = maxrightlabel - plot_bounds.xright;
565 	if (xtic_textwidth > term->xmax/4) {
566 	    xtic_textwidth = term->xmax/4;
567 	    int_warn(NO_CARET, "difficulty making room for xtic labels");
568 	}
569     }
570 
571     /* tics */
572     if (!axis_array[SECOND_Y_AXIS].tic_in
573 	&& ((axis_array[SECOND_Y_AXIS].ticmode & TICS_ON_BORDER)
574 	    || ((axis_array[FIRST_Y_AXIS].ticmode & TICS_MIRROR)
575 		&& (axis_array[FIRST_Y_AXIS].ticmode & TICS_ON_BORDER))))
576 	y2tic_width = (int) (t->h_tic * axis_array[SECOND_Y_AXIS].ticscale);
577     else
578 	y2tic_width = 0;
579 
580     /* Make room for the color box if needed. */
581     if (rmargin.scalex != screen) {
582 	if (is_plot_with_colorbox()) {
583 #define COLORBOX_SCALE 0.100
584 #define WIDEST_COLORBOX_TICTEXT 3
585 	    if ((color_box.where != SMCOLOR_BOX_NO) && (color_box.where != SMCOLOR_BOX_USER)) {
586 		plot_bounds.xright -= (int) (plot_bounds.xright-plot_bounds.xleft)*COLORBOX_SCALE;
587 		plot_bounds.xright -= (int) ((t->h_char) * WIDEST_COLORBOX_TICTEXT);
588 	    }
589 	    color_box.xoffset = 0;
590 	}
591 
592 	if (rmargin.x < 0) {
593 	    color_box.xoffset = plot_bounds.xright;
594 	    plot_bounds.xright -= y2tic_width + y2tic_textwidth;
595 	    if (y2label_textwidth > 0)
596 		plot_bounds.xright -= y2label_textwidth;
597 
598 	    if (plot_bounds.xright > (xsize+xoffset)*(t->xmax-1) - (t->h_char * 2))
599 		plot_bounds.xright = (xsize+xoffset)*(t->xmax-1) - (t->h_char * 2);
600 
601 	    color_box.xoffset -= plot_bounds.xright;
602 	    /* EAM 2009 - protruding xtic labels */
603 	    if (term->xmax - plot_bounds.xright < xtic_textwidth)
604 		plot_bounds.xright = term->xmax - xtic_textwidth;
605 	    /* DBT 12-3-98  extra margin just in case */
606 	    plot_bounds.xright -= 1.0 * t->h_char;
607 	}
608 	/* Note: we took care of explicit 'set rmargin foo' at line 502 */
609     }
610 
611     /*  end of plot_bounds.xright calculation }}} */
612 
613 
614     /* Set up x and x2 tics */
615     /* we should base the guide on the width of the xtics, but we cannot
616      * use widest_tics until tics are set up. Bit of a downer - let us
617      * assume tics are 5 characters wide
618      */
619     setup_tics(&axis_array[FIRST_X_AXIS], 20);
620     setup_tics(&axis_array[SECOND_X_AXIS], 20);
621 
622     /* Make sure that if polar grid is shown on a cartesian axis plot */
623     /* the rtics match up with the primary x tics.                    */
624     if (R_AXIS.ticmode && (polar || raxis)) {
625 	if (bad_axis_range(&R_AXIS) || (!polar && R_AXIS.min != 0)) {
626 	    set_explicit_range(&R_AXIS, 0.0, X_AXIS.max);
627 	    R_AXIS.min = 0;
628 	    R_AXIS.max = axis_array[FIRST_X_AXIS].max;
629 	    int_warn(NO_CARET, "resetting rrange");
630 	}
631 	setup_tics(&axis_array[POLAR_AXIS], 10);
632     }
633 
634 
635     /* Modify the bounding box to fit the aspect ratio, if any was given */
636     if (aspect_ratio != 0.0) {
637 	double current_aspect_ratio;
638 	double current, required;
639 
640 	if (aspect_ratio < 0 && (X_AXIS.max - X_AXIS.min) != 0.0) {
641 	    current_aspect_ratio = -aspect_ratio
642 		* fabs((Y_AXIS.max - Y_AXIS.min) / (X_AXIS.max - X_AXIS.min));
643 	} else
644 	    current_aspect_ratio = aspect_ratio;
645 
646 	if (current_aspect_ratio < 0.005 || current_aspect_ratio > 2000.0)
647 	    int_warn(NO_CARET, "extreme aspect ratio");
648 
649 	current = ((double) (plot_bounds.ytop - plot_bounds.ybot))
650 		/ ((double) (plot_bounds.xright - plot_bounds.xleft));
651 	required = (current_aspect_ratio * t->v_tic) / t->h_tic;
652 
653 	/* Fixed borders take precedence over centering */
654 	if (current > required) {
655 	    /* too tall */
656 	    int old_height = plot_bounds.ytop - plot_bounds.ybot;
657 	    int new_height = required * (plot_bounds.xright - plot_bounds.xleft);
658 	    if (bmargin.scalex == screen)
659 		plot_bounds.ytop = plot_bounds.ybot + new_height;
660 	    else if (tmargin.scalex == screen)
661 		plot_bounds.ybot = plot_bounds.ytop - new_height;
662 	    else {
663 		plot_bounds.ybot += (old_height - new_height) / 2;
664 		plot_bounds.ytop -= (old_height - new_height) / 2;
665 	    }
666 
667 	} else {
668 	    /* too wide */
669 	    int old_width = plot_bounds.xright - plot_bounds.xleft;
670 	    int new_width = (plot_bounds.ytop - plot_bounds.ybot) / required;
671 	    if (lmargin.scalex == screen)
672 		plot_bounds.xright = plot_bounds.xleft + new_width;
673 	    else if (rmargin.scalex == screen)
674 		plot_bounds.xleft = plot_bounds.xright - new_width;
675 	    else {
676 		plot_bounds.xleft += (old_width - new_width) / 2;
677 		plot_bounds.xright -= (old_width - new_width) / 2;
678 	    }
679 	}
680     }
681 
682     /*  Calculate space needed for tic label rotation.
683      *  If [tb]margin is auto, move the plot boundary.
684      *  Otherwise use textheight to adjust placement of various titles.
685      */
686 
687     if (axis_array[SECOND_X_AXIS].ticmode & TICS_ON_BORDER && vertical_x2tics) {
688 	/* Assuming left justified tic labels. Correction below if they aren't */
689 	double projection = sin((double)axis_array[SECOND_X_AXIS].tic_rotate*DEG2RAD);
690 	if (axis_array[SECOND_X_AXIS].tic_pos == RIGHT)
691 	    projection *= -1;
692 	else if (axis_array[SECOND_X_AXIS].tic_pos == CENTRE)
693 	    projection = 0.5*fabs(projection);
694 	widest_tic_strlen = 0;		/* reset the global variable ... */
695 	gen_tics(&axis_array[SECOND_X_AXIS], widest_tic_callback);
696 	if (tmargin.x < 0) /* Undo original estimate */
697 	    plot_bounds.ytop += x2tic_textheight;
698 	/* Adjust spacing for rotation */
699 	if (projection > 0.0)
700 	    x2tic_textheight += (int) (t->h_char * (widest_tic_strlen)) * projection;
701 	if (tmargin.x < 0)
702 	    plot_bounds.ytop -= x2tic_textheight;
703     }
704     if (axis_array[FIRST_X_AXIS].ticmode & TICS_ON_BORDER && vertical_xtics) {
705 	double projection;
706 	/* This adjustment will happen again in axis_output_tics but we need it now */
707 	if (axis_array[FIRST_X_AXIS].tic_rotate == TEXT_VERTICAL
708 	&& !axis_array[FIRST_X_AXIS].manual_justify)
709 	    axis_array[FIRST_X_AXIS].tic_pos = RIGHT;
710 	if (axis_array[FIRST_X_AXIS].tic_rotate == 90)
711 	    projection = -1.0;
712 	else if (axis_array[FIRST_X_AXIS].tic_rotate == TEXT_VERTICAL)
713 	    projection = -1.0;
714 	else
715 	    projection = -sin((double)axis_array[FIRST_X_AXIS].tic_rotate*DEG2RAD);
716 	if (axis_array[FIRST_X_AXIS].tic_pos == RIGHT)
717 	    projection *= -1;
718 	widest_tic_strlen = 0;		/* reset the global variable ... */
719 	gen_tics(&axis_array[FIRST_X_AXIS], widest_tic_callback);
720 
721 	if (bmargin.x < 0)
722 	    plot_bounds.ybot -= xtic_textheight;
723 	if (projection > 0.0)
724 	    xtic_textheight = (int) (t->h_char * widest_tic_strlen) * projection
725 			    + t->v_char;
726 	if (bmargin.x < 0)
727 	    plot_bounds.ybot += xtic_textheight;
728     }
729 
730     /*
731      * Notwithstanding all these fancy calculations,
732      * plot_bounds.ytop must always be above plot_bounds.ybot
733      */
734     if (plot_bounds.ytop < plot_bounds.ybot) {
735 	int i = plot_bounds.ytop;
736 
737 	plot_bounds.ytop = plot_bounds.ybot;
738 	plot_bounds.ybot = i;
739 	FPRINTF((stderr,"boundary: Big problems! plot_bounds.ybot > plot_bounds.ytop\n"));
740     }
741 
742     /*  compute coordinates for axis labels, title etc */
743 
744     x2label_y = plot_bounds.ytop + x2label_textheight;
745     x2label_y += 0.5 * t->v_char;
746     if (x2label_textheight + x2label_yoffset >= 0) {
747 	x2label_y += 1.5 * x2tic_textheight;
748 	/* Adjust for the tics themselves */
749 	if (x2tic_height > 0)
750 	    x2label_y += x2tic_height;
751     }
752 
753     title_x = (plot_bounds.xleft + plot_bounds.xright) / 2;
754 
755     /* title_y was previously set to the actual title height.
756      * Further corrections to this placement only if it is above the plot
757      */
758     title_y += plot_bounds.ytop;
759     if (titlelin + title.offset.y > 0) {
760 	title_y += x2tic_textheight;
761 	title_y += ttic_textheight;
762 	if (x2label_y + x2label_yoffset > plot_bounds.ytop)
763 	    title_y += x2label_textheight;
764 	if (x2tic_height > 0)
765 	    title_y += x2tic_height;
766     }
767 
768     /* Shift upward by 0.2 line to allow for descenders in xlabel text */
769     xlabel_y = plot_bounds.ybot - xtic_height - xtic_textheight - xlabel_textheight
770 	+ ((float)xlablin+0.2) * t->v_char;
771     xlabel_y -= ttic_textheight;
772     ylabel_x = plot_bounds.xleft - ytic_width - ytic_textwidth;
773     ylabel_x -= ylabel_textwidth/2;
774 
775     y2label_x = plot_bounds.xright + y2tic_width + y2tic_textwidth;
776     y2label_x += y2label_textwidth/2;
777 
778     /* Nov 2016  - simplify placement of timestamp
779      * Stamp the same place on the page regardless of plot margins
780      */
781     if (vertical_timelabel) {
782 	time_x = 1.5 * term->h_char;
783 	if (timelabel_bottom)
784 	    time_y = term->v_char;
785 	else
786 	    time_y = term->ymax - term->v_char;
787     } else {
788 	time_x = 1.0 * term->h_char;
789 	if (timelabel_bottom)
790 	    time_y = timelabel_textheight - 0.5 * term->v_char;
791 	else
792 	    time_y = term->ymax;
793     }
794 
795     xtic_y = plot_bounds.ybot - xtic_height
796 	- (int) (vertical_xtics ? t->h_char : t->v_char);
797 
798     x2tic_y = plot_bounds.ytop + (x2tic_height > 0 ? x2tic_height : 0)
799 	+ (vertical_x2tics ? (int) t->h_char : t->v_char);
800 
801     ytic_x = plot_bounds.xleft - ytic_width
802 	- (vertical_ytics
803 	   ? (ytic_textwidth - (int) t->v_char)
804 	   : (int) t->h_char);
805 
806     y2tic_x = plot_bounds.xright + y2tic_width
807 	+ (int) (vertical_y2tics ? t->v_char : t->h_char);
808 
809     /* restore text to horizontal [we tested rotation above] */
810     (void) (*t->text_angle) (0);
811 
812     /* needed for map_position() below */
813     axis_set_scale_and_range(&axis_array[FIRST_X_AXIS], plot_bounds.xleft, plot_bounds.xright);
814     axis_set_scale_and_range(&axis_array[SECOND_X_AXIS], plot_bounds.xleft, plot_bounds.xright);
815     axis_set_scale_and_range(&axis_array[FIRST_Y_AXIS], plot_bounds.ybot, plot_bounds.ytop);
816     axis_set_scale_and_range(&axis_array[SECOND_Y_AXIS], plot_bounds.ybot, plot_bounds.ytop);
817 
818     /* Calculate limiting bounds of the key */
819     do_key_bounds(key);
820 
821 
822     /* Set default clipping to the plot boundary */
823     clip_area = &plot_bounds;
824 
825     /* Sanity checks */
826     if (plot_bounds.xright < plot_bounds.xleft
827     ||  plot_bounds.ytop   < plot_bounds.ybot)
828 	int_warn(NO_CARET, "Terminal canvas area too small to hold plot."
829 			"\n\t    Check plot boundary and font sizes.");
830 
831 }
832 
833 /*}}} */
834 
835 void
do_key_bounds(legend_key * key)836 do_key_bounds(legend_key *key)
837 {
838     struct termentry *t = term;
839 
840     key_height = key_title_height + key_title_extra
841 		+ key_rows * key_entry_height + key->height_fix * key_entry_height;
842     key_width = key_col_wth * key_cols;
843 
844     /* Key inside plot boundaries */
845     if (key->region == GPKEY_AUTO_INTERIOR_LRTBC
846 	|| (key->region == GPKEY_AUTO_EXTERIOR_LRTBC && key->vpos == JUST_CENTRE && key->hpos == CENTRE)) {
847 	if (key->vpos == JUST_TOP) {
848 	    key->bounds.ytop = plot_bounds.ytop - t->v_tic;
849 	    key->bounds.ybot = key->bounds.ytop - key_height;
850 	} else if (key->vpos == JUST_BOT) {
851 	    key->bounds.ybot = plot_bounds.ybot + t->v_tic;
852 	    key->bounds.ytop = key->bounds.ybot + key_height;
853 	} else /* (key->vpos == JUST_CENTRE) */ {
854 	    key->bounds.ybot = ((plot_bounds.ybot + plot_bounds.ytop) - key_height) / 2;
855 	    key->bounds.ytop = ((plot_bounds.ybot + plot_bounds.ytop) + key_height) / 2;
856 	}
857 	if (key->hpos == LEFT) {
858 	    key->bounds.xleft = plot_bounds.xleft + t->h_char;
859 	    key->bounds.xright = key->bounds.xleft + key_width;
860 	} else if (key->hpos == RIGHT) {
861 	    key->bounds.xright = plot_bounds.xright - t->h_char;
862 	    key->bounds.xleft = key->bounds.xright - key_width;
863 	} else /* (key->hpos == CENTER) */ {
864 	    key->bounds.xleft = ((plot_bounds.xright + plot_bounds.xleft) - key_width) / 2;
865 	    key->bounds.xright = ((plot_bounds.xright + plot_bounds.xleft) + key_width) / 2;
866 	}
867 
868     /* Key outside plot boundaries */
869     } else if (key->region == GPKEY_AUTO_EXTERIOR_LRTBC || key->region == GPKEY_AUTO_EXTERIOR_MARGIN) {
870 
871 	/* Vertical alignment */
872 	if (key->margin == GPKEY_TMARGIN) {
873 	    /* align top first since tmargin may be manual */
874 	    key->bounds.ytop = (ysize + yoffset) * t->ymax - t->v_tic;
875 	    key->bounds.ybot = key->bounds.ytop - key_height;
876 	} else if (key->margin == GPKEY_BMARGIN) {
877 	    /* align bottom first since bmargin may be manual */
878 	    key->bounds.ybot = yoffset * t->ymax + t->v_tic;
879 	    if (timelabel.rotate == 0 && timelabel_bottom && timelabel.place.y > 0)
880 		key->bounds.ybot += (int)(timelabel.place.y);
881 	    key->bounds.ytop = key->bounds.ybot + key_height;
882 	} else {
883 	    if (key->vpos == JUST_TOP) {
884 		/* align top first since tmargin may be manual */
885 		key->bounds.ytop = plot_bounds.ytop;
886 		key->bounds.ybot = key->bounds.ytop - key_height;
887 	    } else if (key->vpos == JUST_CENTRE) {
888 		key->bounds.ybot = ((plot_bounds.ybot + plot_bounds.ytop) - key_height) / 2;
889 		key->bounds.ytop = ((plot_bounds.ybot + plot_bounds.ytop) + key_height) / 2;
890 	    } else {
891 		/* align bottom first since bmargin may be manual */
892 		key->bounds.ybot = plot_bounds.ybot;
893 		key->bounds.ytop = key->bounds.ybot + key_height;
894 	    }
895 	}
896 
897 	/* Horizontal alignment */
898 	if (key->margin == GPKEY_LMARGIN) {
899 	    /* align left first since lmargin may be manual */
900 	    key->bounds.xleft = xoffset * t->xmax + t->h_char;
901 	    key->bounds.xright = key->bounds.xleft + key_width;
902 	} else if (key->margin == GPKEY_RMARGIN) {
903 	    /* align right first since rmargin may be manual */
904 	    key->bounds.xright = (xsize + xoffset) * (t->xmax-1) - t->h_char;
905 	    key->bounds.xleft = key->bounds.xright - key_width;
906 	} else {
907 	    if (key->hpos == LEFT) {
908 		/* align left first since lmargin may be manual */
909 		key->bounds.xleft = plot_bounds.xleft;
910 		key->bounds.xright = key->bounds.xleft + key_width;
911 	    } else if (key->hpos == CENTRE) {
912 		key->bounds.xleft  = ((plot_bounds.xright + plot_bounds.xleft) - key_width) / 2;
913 		key->bounds.xright = ((plot_bounds.xright + plot_bounds.xleft) + key_width) / 2;
914 	    } else {
915 		/* align right first since rmargin may be manual */
916 		key->bounds.xright = plot_bounds.xright;
917 		key->bounds.xleft = key->bounds.xright - key_width;
918 	    }
919 	}
920 
921     /* Key at explicit position specified by user */
922     } else {
923 	int x, y;
924 
925 	/* FIXME!!!
926 	 * pm 22.1.2002: if key->user_pos.scalex or scaley == first_axes or second_axes,
927 	 * then the graph scaling is not yet known and the box is positioned incorrectly;
928 	 * you must do "replot" to avoid the wrong plot ... bad luck if output does not
929 	 * go to screen
930 	 */
931 	map_position(&key->user_pos, &x, &y, "key");
932 
933 	/* Here top, bottom, left, right refer to the alignment with respect to point. */
934 	key->bounds.xleft = x;
935 	if (key->hpos == CENTRE)
936 	    key->bounds.xleft -= key_width/2;
937 	else if (key->hpos == RIGHT)
938 	    key->bounds.xleft -= key_width;
939 	key->bounds.xright = key->bounds.xleft + key_width;
940 	key->bounds.ytop = y;
941 	if (key->vpos == JUST_CENTRE)
942 	    key->bounds.ytop += key_height/2;
943 	else if (key->vpos == JUST_BOT)
944 	    key->bounds.ytop += key_height;
945 	key->bounds.ybot = key->bounds.ytop - key_height;
946     }
947 }
948 
949 /* Calculate positioning of components that make up the key box */
950 void
do_key_layout(legend_key * key)951 do_key_layout(legend_key *key)
952 {
953     struct termentry *t = term;
954     TBOOLEAN key_panic = FALSE;
955 
956     /* If there is a separate font for the key, use it for space calculations.	*/
957     if (key->font)
958 	t->set_font(key->font);
959 
960     /* Is it OK to initialize these here rather than in do_plot? */
961     key_count = 0;
962     key_xleft = 0;
963     xl = yl = 0;
964 
965     if (key->swidth >= 0) {
966 	key_sample_width = key->swidth * t->h_char + t->h_tic;
967     } else {
968 	key_sample_width = 0;
969     }
970 
971     key_sample_height = GPMAX( 1.25 * t->v_tic, t->v_char );
972     key_entry_height = key_sample_height * key->vert_factor;
973     /* HBB 20020122: safeguard to prevent division by zero later */
974     if (key_entry_height == 0)
975 	key_entry_height = 1;
976 
977     /* Key title length and height */
978     key_title_height = 0;
979     key_title_extra = 0;
980     if (key->title.text) {
981 	int ytheight;
982 	(void) label_width(key->title.text, &ytheight);
983 	if (key->title.font)
984 	    t->set_font(key->title.font);
985 	key_title_height = ytheight * t->v_char;
986 	if (key->title.font)
987 	    t->set_font("");
988 	if ((*key->title.text) && (t->flags & TERM_ENHANCED_TEXT)
989 	&&  (strchr(key->title.text,'^') || strchr(key->title.text,'_')))
990 	    key_title_extra = t->v_char;
991     }
992 
993     if (key->reverse) {
994 	key_sample_left = -key_sample_width;
995 	key_sample_right = 0;
996 	/* if key width is being used, adjust right-justified text */
997 	key_text_left = t->h_char;
998 	key_text_right = t->h_char * (max_ptitl_len + 1 + key->width_fix);
999 	key_size_left = t->h_char - key_sample_left; /* sample left is -ve */
1000 	key_size_right = key_text_right;
1001     } else {
1002 	key_sample_left = 0;
1003 	key_sample_right = key_sample_width;
1004 	/* if key width is being used, adjust left-justified text */
1005 	key_text_left = -(int) (t->h_char
1006 				* (max_ptitl_len + 1 + key->width_fix));
1007 	key_text_right = -(int) t->h_char;
1008 	key_size_left = -key_text_left;
1009 	key_size_right = key_sample_right + t->h_char;
1010     }
1011     key_point_offset = (key_sample_left + key_sample_right) / 2;
1012 
1013     /* advance width for cols */
1014     key_col_wth = key_size_left + key_size_right;
1015 
1016     key_rows = ptitl_cnt;
1017     key_cols = 1;
1018 
1019     /* calculate rows and cols for key */
1020 
1021     if (key->stack_dir == GPKEY_HORIZONTAL) {
1022 	/* maximise no cols, limited by label-length */
1023 	key_cols = (int) (plot_bounds.xright - plot_bounds.xleft) / key_col_wth;
1024 	if (key->maxcols > 0 && key_cols > key->maxcols)
1025 	    key_cols = key->maxcols;
1026 	/* EAM Dec 2004 - Rather than turn off the key, try to squeeze */
1027 	if (key_cols == 0) {
1028 	    key_cols = 1;
1029 	    key_panic = TRUE;
1030 	    key_col_wth = (plot_bounds.xright - plot_bounds.xleft);
1031 	}
1032 	key_rows = (ptitl_cnt + key_cols - 1) / key_cols;
1033 	/* now calculate actual no cols depending on no rows */
1034 	key_cols = (key_rows == 0) ? 1 : (ptitl_cnt + key_rows - 1) / key_rows;
1035 	if (key_cols == 0) {
1036 	    key_cols = 1;
1037 	}
1038     } else {
1039 	/* maximise no rows, limited by plot_bounds.ytop-plot_bounds.ybot */
1040 	int i = (plot_bounds.ytop - plot_bounds.ybot - key->height_fix * key_entry_height
1041 		    - key_title_height - key_title_extra)
1042 		/ key_entry_height;
1043 	if (key->maxrows > 0 && i > key->maxrows)
1044 	    i = key->maxrows;
1045 
1046 	if (i == 0) {
1047 	    i = 1;
1048 	    key_panic = TRUE;
1049 	}
1050 	if (ptitl_cnt > i) {
1051 	    key_cols = (ptitl_cnt + i - 1) / i;
1052 	    /* now calculate actual no rows depending on no cols */
1053 	    if (key_cols == 0) {
1054 		key_cols = 1;
1055 		key_panic = TRUE;
1056 	    }
1057 	    key_rows = (ptitl_cnt + key_cols - 1) / key_cols;
1058 #if (0)
1059 	} else {
1060 	    /* This was a work-around for a spiderplot bug. No longer needed? */
1061 	    if (key_rows == 0)
1062 		key_rows = i;
1063 #endif
1064 	}
1065     }
1066 
1067     /* If the key title is wider than the contents, try to make room for it */
1068     if (key->title.text) {
1069 	int ytlen = label_width(key->title.text, NULL) - key->swidth + 2;
1070 	if (key->title.font)
1071 	    t->set_font(key->title.font);
1072 	ytlen *= t->h_char;
1073 	if (ytlen > key_cols * key_col_wth)
1074 	    key_col_wth = ytlen / key_cols;
1075 	if (key->title.font)
1076 	    t->set_font("");
1077     }
1078 
1079     /* Adjust for outside key, leave manually set margins alone */
1080     if ((key->region == GPKEY_AUTO_EXTERIOR_LRTBC && (key->vpos != JUST_CENTRE || key->hpos != CENTRE))
1081 	|| key->region == GPKEY_AUTO_EXTERIOR_MARGIN) {
1082 	int more = 0;
1083 	if (key->margin == GPKEY_BMARGIN && bmargin.x < 0) {
1084 	    more = key_rows * key_entry_height + key_title_height + key_title_extra
1085 		    + key->height_fix * key_entry_height;
1086 	    if (plot_bounds.ybot + more > plot_bounds.ytop)
1087 		key_panic = TRUE;
1088 	    else
1089 		plot_bounds.ybot += more;
1090 	} else if (key->margin == GPKEY_TMARGIN && tmargin.x < 0) {
1091 	    more = key_rows * key_entry_height + key_title_height + key_title_extra
1092 		    + key->height_fix * key_entry_height;
1093 	    if (plot_bounds.ytop - more < plot_bounds.ybot)
1094 		key_panic = TRUE;
1095 	    else
1096 		plot_bounds.ytop -= more;
1097 	} else if (key->margin == GPKEY_LMARGIN && lmargin.x < 0) {
1098 	    more = key_col_wth * key_cols;
1099 	    if (plot_bounds.xleft + more > plot_bounds.xright)
1100 		key_panic = TRUE;
1101 	    else
1102 		key_xleft = more;
1103 	    plot_bounds.xleft += key_xleft;
1104 	} else if (key->margin == GPKEY_RMARGIN && rmargin.x < 0) {
1105 	    more = key_col_wth * key_cols;
1106 	    if (plot_bounds.xright - more < plot_bounds.xleft)
1107 		key_panic = TRUE;
1108 	    else
1109 		plot_bounds.xright -= more;
1110 	}
1111     }
1112 
1113     /* Restore default font */
1114     if (key->font)
1115 	t->set_font("");
1116 
1117     /* warn if we had to punt on key size calculations */
1118     if (key_panic)
1119 	int_warn(NO_CARET, "Warning - difficulty fitting plot titles into key");
1120 }
1121 
1122 int
find_maxl_keys(struct curve_points * plots,int count,int * kcnt)1123 find_maxl_keys(struct curve_points *plots, int count, int *kcnt)
1124 {
1125     int mlen, len, curve, cnt;
1126     int previous_plot_style = 0;
1127     struct curve_points *this_plot;
1128 
1129     mlen = cnt = 0;
1130     this_plot = plots;
1131     for (curve = 0; curve < count; this_plot = this_plot->next, curve++) {
1132 
1133 	if (this_plot->plot_style == PARALLELPLOT)
1134 	    continue;
1135 
1136 	if (this_plot->title && !this_plot->title_is_suppressed
1137 	&&  !this_plot->title_position) {
1138 	    if (this_plot->plot_style == SPIDERPLOT && this_plot->plot_type != KEYENTRY)
1139 		; /* Nothing */
1140 	    else {
1141 		ignore_enhanced(this_plot->title_no_enhanced);
1142 		len = estimate_strlen(this_plot->title);
1143 		if (len != 0) {
1144 		    cnt++;
1145 		    if (len > mlen)
1146 			mlen = len;
1147 		}
1148 		ignore_enhanced(FALSE);
1149 	    }
1150 	}
1151 
1152 	/* Check for new histogram here and save space for divider */
1153 	if (this_plot->plot_style == HISTOGRAMS
1154 	&&  previous_plot_style == HISTOGRAMS
1155 	&&  this_plot->histogram_sequence == 0 && cnt > 1)
1156 	    cnt++;
1157 
1158 	/* Check for column-stacked histogram with key entries.
1159 	 * Same thing for spiderplots.
1160 	 * This is needed for 'plot ... using col:key(1)'
1161 	 */
1162 	if (this_plot->labels &&
1163 	    (this_plot->plot_style == HISTOGRAMS || this_plot->plot_style == SPIDERPLOT)) {
1164 	    text_label *key_entry = this_plot->labels->next;
1165 	    for (; key_entry; key_entry=key_entry->next) {
1166 		cnt++;
1167 		len = key_entry->text ? estimate_strlen(key_entry->text) : 0;
1168 		if (len > mlen)
1169 		    mlen = len;
1170 	    }
1171 	}
1172 	previous_plot_style = this_plot->plot_style;
1173     }
1174 
1175     if (kcnt != NULL)
1176 	*kcnt = cnt;
1177     return (mlen);
1178 }
1179 
1180 /*
1181  * Make the key sample code a subroutine so that it can eventually be
1182  * shared by the 3d code also. As of now the two code sections are not
1183  * very parallel.  EAM Nov 2003
1184  */
1185 
1186 void
do_key_sample(struct curve_points * this_plot,legend_key * key,char * title,coordval var_color)1187 do_key_sample(
1188     struct curve_points *this_plot,
1189     legend_key *key,
1190     char *title,
1191     coordval var_color)
1192 {
1193     struct termentry *t = term;
1194     int xl_save = xl;
1195     int yl_save = yl;
1196 
1197     /* Clip key box against canvas */
1198     BoundingBox *clip_save = clip_area;
1199     if (term->flags & TERM_CAN_CLIP)
1200 	clip_area = NULL;
1201     else
1202 	clip_area = &canvas;
1203 
1204     /* If the plot this title belongs to specified a non-standard place */
1205     /* for the key sample to appear, use that to override xl, yl.       */
1206     if (this_plot->title_position && this_plot->title_position->scalex != character) {
1207 	map_position(this_plot->title_position, &xl, &yl, "key sample");
1208 	xl -=  (key->just == GPKEY_LEFT) ? key_text_left : key_text_right;
1209     }
1210 
1211     (*t->layer)(TERM_LAYER_BEGIN_KEYSAMPLE);
1212 
1213     if (key->textcolor.type == TC_VARIABLE)
1214 	/* Draw key text in same color as plot */
1215 	;
1216     else if (key->textcolor.type != TC_DEFAULT)
1217 	/* Draw key text in same color as key title */
1218 	apply_pm3dcolor(&key->textcolor);
1219     else
1220 	/* Draw key text in black */
1221 	(*t->linetype)(LT_BLACK);
1222 
1223     if (this_plot->title_is_automated && (t->flags & TERM_IS_LATEX)) {
1224 	title = texify_title(title, this_plot->plot_type);
1225     }
1226 
1227     if (key->just == GPKEY_LEFT) {
1228 	write_multiline(xl + key_text_left, yl, title, LEFT, JUST_CENTRE, 0, key->font);
1229     } else {
1230 	if ((*t->justify_text) (RIGHT)) {
1231 	    write_multiline(xl + key_text_right, yl, title, RIGHT, JUST_CENTRE, 0, key->font);
1232 	} else {
1233 	    int x = xl + key_text_right - t->h_char * estimate_strlen(title);
1234 	    if (key->region == GPKEY_AUTO_EXTERIOR_LRTBC ||	/* HBB 990327 */
1235 		key->region == GPKEY_AUTO_EXTERIOR_MARGIN ||
1236 		inrange((x), (plot_bounds.xleft), (plot_bounds.xright)))
1237 		write_multiline(x, yl, title, LEFT, JUST_CENTRE, 0, key->font);
1238 	}
1239     }
1240 
1241     /* Draw sample in same style and color as the corresponding plot  */
1242     /* The variable color case uses the color of the first data point */
1243     if (!check_for_variable_color(this_plot, &var_color))
1244 	term_apply_lp_properties(&this_plot->lp_properties);
1245 
1246     /* draw sample depending on bits set in plot_style */
1247     if (this_plot->plot_style & PLOT_STYLE_HAS_FILL && t->fillbox) {
1248 	struct fill_style_type *fs = &this_plot->fill_properties;
1249 	int style = style_from_fill(fs);
1250 	int x = xl + key_sample_left;
1251 	int y = yl - key_sample_height/4;
1252 	int w = key_sample_right - key_sample_left;
1253 	int h = key_sample_height/2;
1254 
1255 	if (this_plot->plot_style == CIRCLES && w > 0) {
1256 	    do_arc(xl + key_point_offset, yl, key_sample_height/4, 0., 360., style, FALSE);
1257 	    /* Retrace the border if the style requests it */
1258 	    if (need_fill_border(fs)) {
1259 	        do_arc(xl + key_point_offset, yl, key_sample_height/4, 0., 360., 0, FALSE);
1260 	    }
1261 
1262 	} else if (this_plot->plot_style == ELLIPSES && w > 0) {
1263 	    t_ellipse *key_ellipse = (t_ellipse *) gp_alloc(sizeof(t_ellipse),
1264 	        "cute little ellipse for the key sample");
1265 	    key_ellipse->center.x = xl + key_point_offset;
1266 	    key_ellipse->center.y = yl;
1267 	    key_ellipse->extent.x = w * 2/3;
1268 	    key_ellipse->extent.y = h;
1269 	    key_ellipse->orientation = 0.0;
1270 	    /* already in term coords, no need to map */
1271 	    do_ellipse(2, key_ellipse, style, FALSE);
1272 	    /* Retrace the border if the style requests it */
1273 	    if (need_fill_border(fs)) {
1274 		do_ellipse(2, key_ellipse, 0, FALSE);
1275 	    }
1276 	    free(key_ellipse);
1277 
1278 	} else if (w > 0) {    /* All other plot types with fill */
1279 	    if (style != FS_EMPTY)
1280 		(*t->fillbox)(style,x,y,w,h);
1281 
1282 	    /* need_fill_border will set the border linetype, but candlesticks don't want it */
1283 	    if ((this_plot->plot_style == CANDLESTICKS && fs->border_color.type == TC_LT
1284 							&& fs->border_color.lt == LT_NODRAW)
1285 	    ||   style == FS_EMPTY
1286 	    ||   need_fill_border(fs)) {
1287 		newpath();
1288 		draw_clip_line( xl + key_sample_left,  yl - key_sample_height/4,
1289 				xl + key_sample_right, yl - key_sample_height/4);
1290 		draw_clip_line( xl + key_sample_right, yl - key_sample_height/4,
1291 				xl + key_sample_right, yl + key_sample_height/4);
1292 		draw_clip_line( xl + key_sample_right, yl + key_sample_height/4,
1293 				xl + key_sample_left,  yl + key_sample_height/4);
1294 		draw_clip_line( xl + key_sample_left,  yl + key_sample_height/4,
1295 				xl + key_sample_left,  yl - key_sample_height/4);
1296 		closepath();
1297 	    }
1298 	    if (fs->fillstyle != FS_EMPTY && fs->fillstyle != FS_DEFAULT
1299 	    && !(fs->border_color.type == TC_LT && fs->border_color.lt == LT_NODRAW)) {
1300 		/* need_fill_border() might have changed our original linetype */
1301 		term_apply_lp_properties(&this_plot->lp_properties);
1302 	    }
1303 	}
1304 
1305     } else if ((this_plot->plot_style & PLOT_STYLE_HAS_VECTOR) && t->arrow) {
1306 	double x1 = xl + key_sample_left;
1307 	double y1 = yl;
1308 	double x2 = xl + key_sample_right;
1309 	double y2 = yl;
1310 	apply_head_properties(&(this_plot->arrow_properties));
1311 	draw_clip_arrow(x1, y1, x2, y2,
1312 			this_plot->arrow_properties.head);
1313 
1314     } else if (this_plot->lp_properties.l_type == LT_NODRAW) {
1315 	;
1316 
1317     } else if ((this_plot->plot_style & PLOT_STYLE_HAS_ERRORBAR) && this_plot->plot_type != FUNC) {
1318 	/* errors for data plots only */
1319 	if ((bar_lp.flags & LP_ERRORBAR_SET) != 0)
1320 	    term_apply_lp_properties(&bar_lp);
1321 	draw_clip_line(xl + key_sample_left, yl, xl + key_sample_right, yl);
1322 	/* Even if error bars are dotted, the end lines are always solid */
1323 	if ((bar_lp.flags & LP_ERRORBAR_SET) != 0)
1324 	    term->dashtype(DASHTYPE_SOLID,NULL);
1325 
1326     } else if ((this_plot->plot_style & PLOT_STYLE_HAS_LINE)) {
1327 	draw_clip_line(xl + key_sample_left, yl, xl + key_sample_right, yl);
1328     }
1329 
1330     if ((this_plot->plot_type == DATA || this_plot->plot_type == KEYENTRY)
1331 	&& (this_plot->plot_style & PLOT_STYLE_HAS_ERRORBAR)
1332 	&& (this_plot->plot_style != CANDLESTICKS)
1333 	&& (bar_size > 0.0)) {
1334 	draw_clip_line( xl + key_sample_left, yl + ERRORBARTIC,
1335 			xl + key_sample_left, yl - ERRORBARTIC);
1336 	draw_clip_line( xl + key_sample_right, yl + ERRORBARTIC,
1337 			xl + key_sample_right, yl - ERRORBARTIC);
1338     }
1339 
1340     /* oops - doing the point sample now would break the postscript
1341      * terminal for example, which changes current line style
1342      * when drawing a point, but does not restore it. We must wait to
1343      * draw the point sample at the end of do_plot (comment KEY SAMPLES).
1344      */
1345 
1346     (*t->layer)(TERM_LAYER_END_KEYSAMPLE);
1347 
1348     /* Restore original linetype for the main plot if we changed it */
1349     if (this_plot->plot_type != FUNC
1350     && (this_plot->plot_style & PLOT_STYLE_HAS_ERRORBAR)
1351     && (bar_lp.flags & LP_ERRORBAR_SET) != 0) {
1352 	term_apply_lp_properties(&this_plot->lp_properties);
1353     }
1354 
1355     /* Restore previous clipping area */
1356     clip_area = clip_save;
1357     xl = xl_save;
1358     yl = yl_save;
1359 }
1360 
1361 void
do_key_sample_point(struct curve_points * this_plot,legend_key * key)1362 do_key_sample_point(
1363     struct curve_points *this_plot,
1364     legend_key *key)
1365 {
1366     struct termentry *t = term;
1367     int xl_save = xl;
1368     int yl_save = yl;
1369 
1370     /* If the plot this title belongs to specified a non-standard place
1371      * for the key sample to appear, use that to override xl, yl.
1372      * For "at end|beg" do nothing at all.
1373      */
1374     if (this_plot->title_position) {
1375 	if (this_plot->title_position->scalex == character)
1376 	    return;
1377 	map_position(this_plot->title_position, &xl, &yl, "key sample");
1378 	xl -=  (key->just == GPKEY_LEFT) ? key_text_left : key_text_right;
1379     }
1380 
1381     (t->layer)(TERM_LAYER_BEGIN_KEYSAMPLE);
1382 
1383     if (this_plot->plot_style == LINESPOINTS
1384 	 &&  this_plot->lp_properties.p_interval < 0) {
1385 	t_colorspec background_fill = BACKGROUND_COLORSPEC;
1386 	(*t->set_color)(&background_fill);
1387 	(*t->pointsize)(pointsize * pointintervalbox);
1388 	(*t->point)(xl + key_point_offset, yl, 6);
1389 	term_apply_lp_properties(&this_plot->lp_properties);
1390     }
1391 
1392     if (this_plot->plot_style == BOXPLOT) {
1393 	;	/* Don't draw a sample point in the key */
1394 
1395     } else if (this_plot->plot_style == DOTS) {
1396 	if (on_page(xl + key_point_offset, yl))
1397 	    (*t->point) (xl + key_point_offset, yl, -1);
1398 
1399     } else if (this_plot->plot_style & PLOT_STYLE_HAS_POINT) {
1400 	if (this_plot->lp_properties.p_size == PTSZ_VARIABLE)
1401 	    (*t->pointsize)(pointsize);
1402 	if (on_page(xl + key_point_offset, yl)) {
1403 	    if (this_plot->lp_properties.p_type == PT_CHARACTER) {
1404 		if (this_plot->labels->textcolor.type != TC_DEFAULT)
1405 		    apply_pm3dcolor(&(this_plot->labels->textcolor));
1406 		(*t->put_text) (xl + key_point_offset, yl,
1407 				this_plot->lp_properties.p_char);
1408 		apply_pm3dcolor(&(this_plot->lp_properties.pm3d_color));
1409 	    } else {
1410 		(*t->point) (xl + key_point_offset, yl,
1411 				this_plot->lp_properties.p_type);
1412 	    }
1413 	}
1414 
1415     } else if (this_plot->plot_style == LABELPOINTS) {
1416 	struct text_label *label = this_plot->labels;
1417 	if (label->lp_properties.flags & LP_SHOW_POINTS) {
1418 	    term_apply_lp_properties(&label->lp_properties);
1419 	    (*t->point) (xl + key_point_offset, yl, label->lp_properties.p_type);
1420 	}
1421     }
1422 
1423     xl = xl_save;
1424     yl = yl_save;
1425     (t->layer)(TERM_LAYER_END_KEYSAMPLE);
1426 }
1427 
1428 /* Graph legend is now optionally done in two passes. The first pass calculates	*/
1429 /* and reserves the necessary space.  Next the individual plots in the graph 	*/
1430 /* are drawn. Then the reserved space for the legend is blanked out, and 	*/
1431 /* finally the second pass through this code draws the legend.			*/
1432 void
draw_key(legend_key * key,TBOOLEAN key_pass)1433 draw_key(legend_key *key, TBOOLEAN key_pass)
1434 {
1435     struct termentry *t = term;
1436 
1437     (t->layer)(TERM_LAYER_KEYBOX);
1438 
1439     /* In two-pass mode (set key opaque) we blank out the key box after	*/
1440     /* the graph is drawn and then redo the key in the blank area.	*/
1441     if (key_pass && t->fillbox && !(t->flags & TERM_NULL_SET_COLOR)) {
1442 	t_colorspec background_fill = BACKGROUND_COLORSPEC;
1443 	(*t->set_color)(&background_fill);
1444 	(*t->fillbox)(FS_OPAQUE, key->bounds.xleft, key->bounds.ybot,
1445 		key_width, key_height);
1446     }
1447 
1448     if (key->title.text) {
1449 	int title_anchor;
1450 	if (key->title.pos == CENTRE)
1451 		title_anchor = (key->bounds.xleft + key->bounds.xright) / 2;
1452 	else if (key->title.pos == RIGHT)
1453 		title_anchor = key->bounds.xright - term->h_char;
1454 	else
1455 		title_anchor = key->bounds.xleft + term->h_char;
1456 
1457 	/* Only draw the title once */
1458 	if (key_pass || !key->front) {
1459 	    write_label(title_anchor,
1460 			key->bounds.ytop - (key_title_extra + key_entry_height)/2,
1461 			&key->title);
1462 	    (*t->linetype)(LT_BLACK);
1463 	}
1464     }
1465 
1466     if (key->box.l_type > LT_NODRAW) {
1467 	BoundingBox *clip_save = clip_area;
1468 	if (term->flags & TERM_CAN_CLIP)
1469 	    clip_area = NULL;
1470 	else
1471 	    clip_area = &canvas;
1472 	term_apply_lp_properties(&key->box);
1473 	newpath();
1474 	draw_clip_line(key->bounds.xleft, key->bounds.ybot, key->bounds.xleft, key->bounds.ytop);
1475 	draw_clip_line(key->bounds.xleft, key->bounds.ytop, key->bounds.xright, key->bounds.ytop);
1476 	draw_clip_line(key->bounds.xright, key->bounds.ytop, key->bounds.xright, key->bounds.ybot);
1477 	draw_clip_line(key->bounds.xright, key->bounds.ybot, key->bounds.xleft, key->bounds.ybot);
1478 	closepath();
1479 	/* draw a horizontal line between key title and first entry */
1480 	if (key->title.text)
1481 	    draw_clip_line( key->bounds.xleft,
1482 	    		    key->bounds.ytop - (key_title_height + key_title_extra),
1483 			    key->bounds.xright,
1484 	    		    key->bounds.ytop - (key_title_height + key_title_extra));
1485 	clip_area = clip_save;
1486     }
1487 
1488     yl_ref = key->bounds.ytop - (key_title_height + key_title_extra);
1489     yl_ref -= ((key->height_fix + 1) * key_entry_height) / 2;
1490     xl = key->bounds.xleft + key_size_left;
1491     yl = yl_ref;
1492 }
1493 
1494 /*
1495  * This routine draws the plot title, the axis labels, and an optional time stamp.
1496  */
1497 void
draw_titles()1498 draw_titles()
1499 {
1500     struct termentry *t = term;
1501 
1502     /* YLABEL */
1503     if (axis_array[FIRST_Y_AXIS].label.text) {
1504 	int x = ylabel_x;
1505 	int y = (plot_bounds.ytop + plot_bounds.ybot) / 2;
1506 	/* There has been much argument about the optimal ylabel position */
1507 	x += t->h_char / 4.;
1508 	write_label(x, y, &(axis_array[FIRST_Y_AXIS].label));
1509 	reset_textcolor(&(axis_array[FIRST_Y_AXIS].label.textcolor));
1510     }
1511 
1512     /* Y2LABEL */
1513     if (axis_array[SECOND_Y_AXIS].label.text) {
1514 	int x = y2label_x;
1515 	int y = (plot_bounds.ytop + plot_bounds.ybot) / 2;
1516 	write_label(x, y, &(axis_array[SECOND_Y_AXIS].label));
1517 	reset_textcolor(&(axis_array[SECOND_Y_AXIS].label.textcolor));
1518     }
1519 
1520     /* XLABEL */
1521     if (axis_array[FIRST_X_AXIS].label.text) {
1522 	struct text_label *label = &axis_array[FIRST_X_AXIS].label;
1523 	double tmpx, tmpy;
1524 	int x, y;
1525 	map_position_r(&(label->offset), &tmpx, &tmpy, "xlabel");
1526 
1527 	x = (plot_bounds.xright + plot_bounds.xleft) / 2;
1528 	y = xlabel_y - t->v_char / 2;
1529 	y -= tmpy;	/* xlabel_y already contained tmpy */
1530 
1531 	write_label(x, y, label);
1532 	reset_textcolor(&(label->textcolor));
1533     }
1534 
1535     /* X2LABEL */
1536     if (axis_array[SECOND_X_AXIS].label.text) {
1537 	int x, y;
1538 	/* we worked out y-coordinate in boundary() */
1539 	x = (plot_bounds.xright + plot_bounds.xleft) / 2;
1540 	y = x2label_y - t->v_char / 2;
1541 	write_label(x, y, &(axis_array[SECOND_X_AXIS].label));
1542 	reset_textcolor(&(axis_array[SECOND_X_AXIS].label.textcolor));
1543     }
1544 
1545     /* RLABEL */
1546     if (axis_array[POLAR_AXIS].label.text) {
1547 	int x, y;
1548 
1549 	/* This assumes we always have a horizontal R axis */
1550 	x = map_x(polar_radius(R_AXIS.max) / 2.0);
1551 	y = map_y(0.0) + t->v_char;
1552 	write_label(x, y, &(axis_array[POLAR_AXIS].label));
1553 	reset_textcolor(&(axis_array[POLAR_AXIS].label.textcolor));
1554     }
1555 
1556     /* PLACE TIMELABEL */
1557     if (timelabel.text)
1558 	do_timelabel(time_x, time_y);
1559 }
1560 
1561 /* advance current position in the key in preparation for next key entry */
1562 void
advance_key(TBOOLEAN only_invert)1563 advance_key(TBOOLEAN only_invert)
1564 {
1565     legend_key *key = &keyT;
1566 
1567     if (key->invert)
1568         yl = key->bounds.ybot + yl_ref + key_entry_height/2 - yl;
1569     if (only_invert)
1570 	return;
1571     if (key_count >= key_rows) {
1572         yl = yl_ref;
1573         xl += key_col_wth;
1574         key_count = 0;
1575     } else
1576         yl = yl - key_entry_height;
1577 }
1578 
1579 /* stupid test used in only one place but it refers to our local variables */
1580 TBOOLEAN
at_left_of_key()1581 at_left_of_key()
1582 {
1583     return (yl == yl_ref);
1584 }
1585