1 /*
2  * $Id: plotter.c,v 1.8 2000/11/22 19:33:01 danny Exp $
3  *
4  * This file contains the heart of libsciplot - the scientific plotting
5  * library layered on top of GNU plotutils.
6  */
7 #include "config.h"
8 
9 #ifdef	HAVE_LIBPLOT
10 #ifndef	HAVE_LIBSCIPLOT
11 
12 #include <stdlib.h>
13 #include <sciplot.h>
14 #include <sciplotI.h>
15 
16 #ifdef	HAVE_STRING_H
17 #include <string.h>
18 #endif
19 
20 #include <limits.h>
21 #include "graph.h"
22 
23 /* #include "sys-defines.h"	*/
24 #include "plot.h"
25 
26 /* FIX ME */
27 #define	progname	"GNU Plotutils libsciplot"
28 
29 /* This file contains the point plotter half of GNU graph.  The point
30    plotter could easily be linked with other software.  It translates a
31    sequence of points, regarded as defining a polyline or a sequence of
32    polylines, to a sequence of libplot calls.  There is support for
33    multigraphing, i.e. producing a plot consisting of more than a single
34    graph.  Each graph may be drawn from more than one file, i.e., input
35    stream, and each input stream may provide more than a single polyline.
36 
37    A `point' is a structure.  Each point structure contains the following
38    fields:
39 
40       x and y coordinates of the point
41       a `have_x_errorbar' flag (true or false)
42       a `have_y_errorbar' flag (true or false)
43       xmin and xmax (meaningful only if have_x_errorbar is set)
44       ymin and ymax (meaningful only if have_y_errorbar is set)
45       a `pendown' flag
46 
47       a symbol type (a small integer, interpreted as a marker type)
48       a symbol size (a fraction of the size of the plotting box)
49       a symbol font name (relevant only for symbol types >= 32)
50       a linemode (a small integer)
51       a linewidth (a fraction of the size of the display device)
52       a polyline fill-fraction (in the interval [0,1], <0 means no fill)
53       a use_color flag (true or false)
54 
55    The point plotter constructs a polyline from each successive run of
56    points that have the pendown flag set.  It assumes that the final seven
57    fields are assumed to be the same for each point in such a run, i.e., it
58    takes their values from the first point of the run.  At the location of
59    each point on the polyline, the appropriate marker symbol, if any, will
60    be plotted.  Symbol types greater than or equal to 32 are interpreted as
61    single characters to be plotted, rather than symbols.
62 
63    Points without the pendown flag set cause the polyline to be broken, and
64    a new one to begin, before the symbol (if any) is plotted.
65 
66    The plotter supports five basic linemodes: 1 through 5.  The
67    interpretation of `linemode' depends on the polyline's use_color flag.
68 
69 	   linemode		If monochrome 		If color
70 
71 	       1		solid			red
72 	       2		dotted			green
73 	       3		dotdashed		blue
74 	       4		shortdashed		magenta
75 	       5		longdashed		cyan
76 
77    In the monochrome case, the pattern simply repeats: 6,7,8,9,10 are
78    equivalent to 1,2,3,4,5, etc.  In the colored case, the sequence of
79    colors also repeats.  But linemodes 1,2,3,4,5 are drawn solid, while
80    6,7,8,9,10 are drawn dotted, 11,12,13,14,15 are drawn dotdashed, etc.
81    So there are 25 distinct colored linemodes, and 5 distinct monochrome
82    (black) ones.
83 
84    The color of a symbol will be the same as the color of the polyline on
85    which it is plotted.
86 
87    linemodes -1, -2, etc. have a special interpretation.  They are
88    `disconnected' linemodes: no polyline will appear, but if color is
89    being used, the color of the plotted symbols (if any) will be
90    linemode-dependent.  -1,-2,-3,-4,5 signify red,green,blue,magenta,cyan
91    (the same sequence as for 1,2,3,4,5); thereafter the sequence repeats.
92 
93    linemode 0 is special (for backward compatibility).  No line is drawn;
94    symbol #1 (a point) will be used.  So using linemode 0 is the same as
95    using linemode -1, symbol 1.
96 
97    The point plotter is invoked by calling the following, in order.
98 
99    new_multigrapher() creates a new point plotter.
100    begin_graph()
101    set_graph_parameters() initializes global structures used by
102    	the draw_frame_of_graph() and plot_point() routines.  These include
103 	the structures that specify the linear transformation from user
104 	coordinates to the coordinates used by libplot, and structures
105 	that specify the style of the plot frame.
106    draw_frame_of_graph() plots the graph frame.  [Optional.]
107    plot_point() uses libplot routines to plot a single point, together
108    	with (possibly)	a line extending to it from the last point, and
109 	a symbol. [Alternatively, plot_point_array() can be used, to plot
110 	an array of points.]
111    end_graph()
112    ..
113    [The begin_graph()..end_graph() block can be repeated indefinitely
114     if desired, to create a multigraph.  set_graph_parameters() allows
115     for repositioning of later graphs.]
116    ..
117    delete_multigrapher() deletes the point plotter.
118 
119    There is also a function end_polyline_and_flush(), which is useful for
120    real-time display. */
121 
122 #define FUZZ 0.000001		/* bd. on floating pt. roundoff error */
123 
124 #define NEAR_EQUALITY(a, b, scale) (fabs((a) - (b)) < (FUZZ * fabs(scale)))
125 
126 typedef unsigned int outcode;	/* for Cohen-Sutherland clipper */
127 enum { TOP = 0x1, BOTTOM = 0x2, RIGHT = 0x4, LEFT = 0x8 };
128 enum { ACCEPTED = 0x1, CLIPPED_FIRST = 0x2, CLIPPED_SECOND = 0x4 };
129 
130 #define TRIAL_NUMBER_OF_TICK_INTERVALS 5
131 #define MAX_NUM_SUBTICKS 29	/* max num. of linearly spaced subticks */
132 #define RELATIVE_SUBTICK_SIZE 0.4 /* subtick_size / tick_size */
133 /* if a log axis spans >5.0 orders of magnitude, don't plot log subsubticks */
134 #define MAX_DECADES_WITH_LOG_SUBSUBTICKS 5.0
135 
136 /* inter-tick spacing types, returned by scale1() and spacing_type() */
137 #define S_ONE 0
138 #define S_TWO 1
139 #define S_FIVE 2
140 #define S_TWO_FIVE 3	/* we don't use this one, but user may request it */
141 #define S_UNKNOWN -2
142 
143 /* valid graph axis layout types; A_LOG2, anyone? */
144 #define A_LINEAR 0
145 #define A_LOG10 1
146 
147 /* Affine transformation macros */
148 
149 /* X Scale: convert from user x value to normalized x coordinate (floating
150    point, 0.0 to 1.0). */
151 #define XS(x) (((x) - multigrapher->x_trans.input_min)/multigrapher->x_trans.input_range)
152 /* X Reflect: map [0,1] to [1,0], if that's called for */
153 #define XR(x) (multigrapher->x_trans.reverse ? 1.0 - (x) : (x))
154 /* X Squeeze: map [0,1] range for normalized x coordinate into a smaller
155    interval, the x range for the plotting area within the graphics display */
156 #define XSQ(x) (multigrapher->x_trans.squeezed_min + (x) * multigrapher->x_trans.squeezed_range)
157 /* X Plot: convert from normalized x coordinate to floating point libplot
158    coordinate. */
159 #define XP(x) (multigrapher->x_trans.output_min + (x) * multigrapher->x_trans.output_range)
160 /* X Value: convert from user x value (floating point) to floating point
161    libplot coordinate. */
162 #define XV(x) XP(XSQ(XR(XS(x))))
163 /* X Normalize: the same, but do not perform reflection if any.  (We use
164    this for plotting of axes and their labels.) */
165 #define XN(y) XP(XSQ(XS(y)))
166 
167 /* Y Scale: convert from user y value to normalized y coordinate (floating
168    point, 0.0 to 1.0). */
169 #define YS(y) (((y) - multigrapher->y_trans.input_min)/multigrapher->y_trans.input_range)
170 /* Y Reflect: map [0,1] to [1,0], if that's called for */
171 #define YR(y) (multigrapher->y_trans.reverse ? 1.0 - (y) : (y))
172 /* Y Squeeze: map [0,1] range for normalized y coordinate into a smaller
173    interval, the y range for the plotting area within the graphics display */
174 #define YSQ(y) (multigrapher->y_trans.squeezed_min + (y) * multigrapher->y_trans.squeezed_range)
175 /* Y Plot: convert from normalized y coordinate to floating point libplot
176    coordinate. */
177 #define YP(y) (multigrapher->y_trans.output_min + (y) * multigrapher->y_trans.output_range)
178 /* Y Value: convert from user y value (floating point) to floating point
179    libplot coordinate.  (We use this for plotting of points.) */
180 #define YV(y) YP(YSQ(YR(YS(y))))
181 /* Y Normalize: the same, but do not perform reflection if any.  (We use
182    this for plotting of axes and their labels.) */
183 #define YN(y) YP(YSQ(YS(y)))
184 
185 /* Size Scale: convert distances, or sizes, from normalized coors to
186    libplot coordinates.  (Used for tick, symbol, and font sizes.)  The min
187    should really be precomputed. */
188 #define SS(x) \
189 (DMIN(multigrapher->x_trans.output_range * multigrapher->x_trans.squeezed_range, \
190 multigrapher->y_trans.output_range * multigrapher->y_trans.squeezed_range) * (x))
191 
192 /* forward references */
193 static int clip_line ____P((Multigrapher *multigrapher, double *x0_p, double *y0_p, double *x1_p, double *y1_p));
194 static int spacing_type ____P((double spacing));
195 static outcode compute_outcode ____P((Multigrapher *multigrapher, double x, double y, bool tolerant));
196 static void plot_abscissa_log_subsubtick ____P((Multigrapher *multigrapher, double xval));
197 static void plot_errorbar ____P((Multigrapher *multigrapher, const Point *p));
198 static void plot_ordinate_log_subsubtick ____P((Multigrapher *multigrapher, double xval));
199 static void prepare_axis ____P((Axis *axisp, Transform *trans, double min, double max, double spacing, double subsubtick_spacing, bool user_specified_subsubticks, bool round_to_next_tick, bool log_axis, bool reverse_axis));
200 static void print_tick_label ____P((char *labelbuf, const Axis *axis, const Transform *transform, double val));
201 static void scale1 ____P((double min, double max, double *tick_spacing, int *tick_spacing_type));
202 static void set_line_style ____P((Multigrapher *multigrapher, int style, bool use_color));
203 static void transpose_portmanteau ____P((int *val));
204 
205 
206 static void
transpose(Multigrapher * mg)207 transpose(Multigrapher *mg)
208 {
209 }
210 
211 
212 /*
213  * sp_default_tick_label() is the function for tick labels originally
214  *	provided with Plotutils. In SciPlot it is only the default case.
215  */
216 static void
sp_default_tick_label(char * labelbuf,const Axis * axis,const Transform * transform,double val)217 sp_default_tick_label(char *labelbuf, const Axis *axis, const Transform *transform, double val)
218 {
219   int prec;
220   char *eloc, *ptr;
221   char labelbuf_tmp[64], incrbuf[64];
222   double spacing;
223   bool big_exponents;
224   double min, max;
225 
226   /* two possibilities: large/small exponent magnitudes */
227 
228   min = (axis->type == A_LOG10
229 	 ? pow (10.0, transform->input_min) : transform->input_min);
230   max = (axis->type == A_LOG10
231 	 ? pow (10.0, transform->input_max) : transform->input_max);
232 
233   big_exponents = (((min != 0.0 && fabs (log10 (fabs (min))) >= 4.0)
234 		    || (max != 0.0 && fabs (log10 (fabs (max))) >= 4.0))
235 		   ? true : false);
236 
237   if (big_exponents)
238     /* large exponents, rewrite as foo x 10^bar, using escape sequences */
239     {
240       char *src = labelbuf_tmp, *dst = labelbuf;
241       int exponent;
242       char floatbuf[64];
243       char *fptr = floatbuf;
244       double prefactor;
245 
246       sprintf (labelbuf_tmp, "%e", val);
247       if ((eloc = strchr (labelbuf_tmp, (int)'e')) == NULL)
248 	return;
249 
250       if (axis->type == A_LOG10 && !axis->user_specified_subsubticks)
251 	/* a hack: this must be a power of 10, so just print "10^bar" */
252 	{
253 	  sscanf (++eloc, "%d", &exponent);
254 	  sprintf (dst, "10\\sp%d\\ep", exponent);
255 	  return;
256 	}
257 
258       /* special case: zero prints as `0', not 0.0x10^whatever */
259       if (val == 0.0)
260 	{
261 	  *dst++ = '0';
262 	  *dst = '\0';
263 	  return;
264 	}
265 
266       while (src < eloc)
267 	*fptr++ = *src++;
268       *fptr = '\0';
269       sscanf (floatbuf, "%lf", &prefactor); /* get foo */
270       sscanf (++src, "%d", &exponent); /* get bar */
271 
272       spacing = (axis->type == A_LINEAR
273 		 ? axis->tick_spacing
274 		 : axis->subsubtick_spacing); /* user-specified, for log axis*/
275       sprintf (incrbuf, "%f",
276 	       spacing / pow (10.0, (double)exponent));
277       ptr = strchr (incrbuf, (int)'.');
278       prec = 0;
279       if (ptr != NULL)
280 	{
281 	  int count = 0;
282 
283 	  while (*(++ptr))
284 	    {
285 	      count++;
286 	      if (*ptr != '0')
287 		prec = count;
288 	    }
289 	}
290 
291       /* \sp ... \ep is start_superscript ... end_superscript, and \r6 is
292 	 right-shift by 1/6 em.  \mu is the `times' character. */
293       sprintf (dst, "%.*f\\r6\\mu10\\sp%d\\ep",
294 	       prec, prefactor, exponent);
295 
296       return;
297     }
298 
299   else	/* small-size exponent magnitudes */
300     {
301       if (axis->type == A_LOG10 && !axis->user_specified_subsubticks)
302 	/* a hack: this must be a (small) power of 10, so we'll just use
303 	   %g format (same as %f, no trailing zeroes) */
304 	{
305 	  sprintf (labelbuf, "%.9g", val);
306 	  return;
307 	}
308 
309       /* always use no. of digits of precision present in increment */
310       spacing = (axis->type == A_LINEAR
311 		 ? axis->tick_spacing
312 		 : axis->subsubtick_spacing); /* user-specified, for log axis*/
313       sprintf (incrbuf, "%.9f", spacing);
314       ptr = strchr (incrbuf, (int)'.');
315       prec = 0;
316       if (ptr != NULL)
317 	{
318 	  int count = 0;
319 
320 	  while (*(++ptr))
321 	    {
322 	      count++;
323 	      if (*ptr != '0')
324 		prec = count;
325 	    }
326 	}
327       sprintf (labelbuf, "%.*f", prec, val);
328       return;
329     }
330 }
331 
332 /*
333  * print_tick_label() prints a label on an axis tick.  The format depends
334  * on whether the axis is a log axis or a linear axis; also, the magnitude
335  * of the axis labels.
336  *
337  * SciPlot added functionality : configurable.
338  */
339 
340 static void
print_tick_label(char * labelbuf,const Axis * axis,const Transform * transform,double val)341 print_tick_label (char *labelbuf, const Axis *axis, const Transform *transform, double val)
342 {
343 	struct tm tm;
344 	time_t	t = (time_t) val;
345 	char		*s;
346 
347 	switch (axis->tick_type) {
348 	case SP_TICK_PRINTF:
349 		sprintf(labelbuf, axis->tick_format, val);
350 		break;
351 	case SP_TICK_STRFTIME:
352 		localtime_r(&t, &tm);
353 		strftime(labelbuf, 128, axis->tick_format, &tm);
354 		break;
355 	case SP_TICK_CUSTOM:
356 		s = axis->xlate_tick(val);
357 		strcpy(labelbuf, s);
358 		free(s);		/* FIX ME */
359 		break;
360 	case SP_TICK_DEFAULT:
361 	default:
362 		sp_default_tick_label(labelbuf, axis, transform, val);
363 	}
364 }
365 
366 /* Algorithm SCALE1, for selecting an inter-tick spacing that will yield a
367  * good-looking linear-format axis.  The spacing is always 1.0, 2.0, or 5.0
368  * times a power of ten.
369  *
370  * Reference: Lewart, C. R., "Algorithms SCALE1, SCALE2, and
371  *	SCALE3 for Determination of Scales on Computer Generated
372  *	Plots", Communications of the ACM, 10 (1973), 639-640.
373  *      Also cited as ACM Algorithm 463.
374  *
375  * We call this routine even when the axis is logarithmic rather than
376  * linear.  In that case the arguments are the logs of the actual
377  * arguments, so that it computes an optimal inter-tick factor rather than
378  * an optimal inter-tick spacing.
379  */
380 
381 static void
scale1(double min,double max,double * tick_spacing,int * tick_spacing_type)382 scale1(double min,		/* Data min */
383 	double max,		/* Data max */
384 	double *tick_spacing,	/* Inter-tick spacing */
385 	int *tick_spacing_type)	/* Inter-tick spacing type (0, 1, or 2,
386 				   i.e. S_ONE, S_TWO, or S_FIVE) */
387 {
388   int k;
389   double nal;
390   double a, b;
391 
392   /* valid interval lengths */
393   static const double vint[] =
394     {
395       1.0, 2.0, 5.0, 10.0
396     };
397 
398   /* Corresponding breakpoints.  The published algorithm uses geometric
399      means, i.e. sqrt(2), sqrt(10), sqrt(50), but using sqrt(10)=3.16...
400      will (if nticks=5, as we choose it to be) cause intervals of length
401      1.5 to yield an inter-tick distance of 0.2 rather than 0.5.  So we
402      could reduce it to 2.95.  Similarly we could reduce sqrt(50) to 6.95
403      so that intervals of length 3.5 will yield an inter-tick distance of
404      1.0 rather than 0.5. */
405   static const double sqr[] =
406     {
407       M_SQRT2, 3.16228, 7.07107
408     };
409 
410   /* compute trial inter-tick interval length */
411   a = (max - min) / TRIAL_NUMBER_OF_TICK_INTERVALS;
412   a *= (max > min) ? 1.0 : -1.0; /* paranoia, max>min always */
413   if (a <= 0.0)
414     {
415       fprintf(stderr, "%s: error: bad trial inter-tick spacing %g\n",
416 	      progname, a);
417       exit (EXIT_FAILURE);
418     }
419   nal = floor(log10(a));
420   b = a * pow (10.0, -nal);	/* 1.0 <= b < 10.0 */
421 
422   /* round to closest permissible inter-tick interval length */
423   k = 0;
424   do
425     {
426       if (b < sqr[k])
427 	break;
428       k++;
429     }
430   while (k < 3);
431 
432   *tick_spacing = (max > min ? 1.0 : -1.0) * vint[k] * pow (10.0, nal);
433   /* for increment type, 0,1,2 means 1,2,5 times a power of 10 */
434   *tick_spacing_type = (k == 3 ? 0 : k);
435   return;
436 }
437 
438 /* Determine whether an inter-tick spacing (in practice, one specified by
439    the user) is 1.0, 2.0, or 5.0 times a power of 10. */
440 static int
spacing_type(double incr)441 spacing_type (double incr)
442 {
443   int i;
444   int i_tenpower = (int)(floor(log10(incr)));
445   double tenpower = 1.0;
446   bool neg_power = false;
447 
448   if (i_tenpower < 0)
449     {
450       neg_power = true;
451       i_tenpower = -i_tenpower;
452     }
453 
454   for (i = 0; i < i_tenpower; i++)
455     tenpower *= 10;
456   if (neg_power)
457     tenpower = 1.0 / tenpower;
458 
459   if (NEAR_EQUALITY(incr, tenpower, tenpower))
460     return S_ONE;
461   else if (NEAR_EQUALITY(incr, 2 * tenpower, tenpower))
462     return S_TWO;
463   else if (NEAR_EQUALITY(incr, 2.5 * tenpower, tenpower))
464     return S_TWO_FIVE;
465   else if (NEAR_EQUALITY(incr, 5 * tenpower, tenpower))
466     return S_FIVE;
467   else
468     return S_UNKNOWN;
469 }
470 
471 /* prepare_axis() fills in the Axis structure for an axis, and some of
472  * the linear transformation variables in the Transform structure also.
473  */
474 
475 static void
prepare_axis(Axis * axisp,Transform * trans,double min,double max,double spacing,double subsubtick_spacing,bool user_specified_subsubticks,bool round_to_next_tick,bool log_axis,bool reverse_axis)476 prepare_axis(Axis *axisp,
477 		Transform *trans,
478 		double min,
479 		double max,
480 		double spacing,
481 		double subsubtick_spacing,
482 		bool user_specified_subsubticks,	/* i.e. linear ticks on a log axis */
483 		bool round_to_next_tick,		/* round limits to the next tick mark */
484 		bool log_axis,				/* log axis */
485 		bool reverse_axis)			/* will reverse min, max */
486 {
487   double range;
488   int tick_spacing_type = 0;
489   double tick_spacing, lin_subtick_spacing;
490   int min_tick_count, max_tick_count;
491   int min_lin_subtick_count, max_lin_subtick_count;
492   bool have_lin_subticks;
493 
494   if (min > max)
495     /* paranoia, max < min is swapped at top level */
496     {
497       fprintf(stderr, "%s: error: min > max for axis, not allowed\n",
498 	      progname);
499       exit (EXIT_FAILURE);
500     }
501 
502   if (min == max)		/* expand in a clever way */
503     {
504       max = floor (max + 1.0);
505       min = ceil (min - 1.0);
506     }
507 
508   if (log_axis)		/* log axis, data are stored in logarithmic form */
509     /* compute a tick spacing; user can't specify it */
510     {
511       scale1 (min, max, &tick_spacing, &tick_spacing_type);
512       if (tick_spacing <= 1.0)
513 	{
514 	  tick_spacing = 1.0;
515 	  tick_spacing_type = S_ONE;
516 	}
517     }
518   else				/* linear axis */
519     {
520       if (spacing == 0.0)	/* i.e., not specified by user */
521 	scale1 (min, max, &tick_spacing, &tick_spacing_type);
522       else			/* luser is boss, don't use SCALE1 */
523 	{
524 	  tick_spacing = spacing;
525 	  tick_spacing_type = spacing_type (spacing);
526 	}
527     }
528 
529   range = max - min;		/* range is not negative */
530 
531   if (round_to_next_tick)	/* expand both limits to next tick */
532     {
533       if (user_specified_subsubticks)
534 	/* Special Case.  If user specified the `spacing' argument to -x or
535 	   -y on a logarithmic axis, our usual tick-generating and
536 	   tick-plotting algorithms are disabled.  So we don't bother with
537 	   min_tick_count or several other fields of the axis struct;
538 	   instead we just compute a new (rounded) max, min, and range.
539 	   Since most data are stored as logs, this is complicated. */
540 	{
541 	  double true_min = pow (10.0, min), true_max = pow (10.0, max);
542 	  double true_range = true_max - true_min;
543 	  int min_count, max_count;
544 
545 	  min_count = (int)(floor ((true_min + FUZZ * true_range)
546 				   / subsubtick_spacing));
547 	  max_count = (int)(ceil ((true_max - FUZZ * true_range)
548 				  / subsubtick_spacing));
549 	  /* avoid core dump, do *not* reduce minimum to zero! */
550 	  if (min_count > 0)
551 	    min = log10 (min_count * subsubtick_spacing);
552 	  max = log10 (max_count * subsubtick_spacing);
553 	  range = max - min;
554 	  min_tick_count = max_tick_count = 0; /* keep gcc happy */
555 	}
556       else	/* normal `expand limits to next tick' case */
557 	{
558 	  min_tick_count = (int)(floor((min + FUZZ * range)/ tick_spacing));
559 	  max_tick_count = (int)(ceil((max - FUZZ * range)/ tick_spacing));
560 	  /* max_tick_count > min_tick_count always */
561 	  /* tickval = tick_spacing * count,
562 	     for all count in [min_count,max_count]; must have >=2 ticks */
563 	  min = tick_spacing * min_tick_count;
564 	  max = tick_spacing * max_tick_count;
565 	  range = max - min;
566 	}
567     }
568   else		/* don't expand limits to next tick */
569     {
570       min_tick_count = (int)(ceil((min - FUZZ * range)/ tick_spacing));
571       max_tick_count = (int)(floor((max + FUZZ * range)/ tick_spacing));
572       /* max_tick_count <= min_tick_count is possible */
573       /* tickval = incr * count,
574 	 for all count in [min_count,max_count]; can have 0,1,2,3... ticks */
575     }
576 
577   /* Allow 5 subticks per tick if S_FIVE or S_TWO_FIVE, 2 if S_TWO.  Case
578      S_ONE is special; we try 10, 5, and 2 in succession */
579   switch (tick_spacing_type)
580     {
581     case S_FIVE:
582     case S_TWO_FIVE:
583       lin_subtick_spacing = tick_spacing / 5;
584       break;
585     case S_TWO:
586       lin_subtick_spacing = tick_spacing / 2;
587       break;
588     case S_ONE:
589       lin_subtick_spacing = tick_spacing / 10;
590       min_lin_subtick_count = (int)(ceil((min - FUZZ * range)/ lin_subtick_spacing));
591       max_lin_subtick_count = (int)(floor((max + FUZZ * range)/ lin_subtick_spacing));
592       if (max_lin_subtick_count - min_lin_subtick_count > MAX_NUM_SUBTICKS)
593 	{
594 	  lin_subtick_spacing = tick_spacing / 5;
595 	  min_lin_subtick_count = (int)(ceil((min - FUZZ * range)/ lin_subtick_spacing));
596 	  max_lin_subtick_count = (int)(floor((max + FUZZ * range)/ lin_subtick_spacing));
597 	  if (max_lin_subtick_count - min_lin_subtick_count > MAX_NUM_SUBTICKS)
598 	    lin_subtick_spacing = tick_spacing / 2;
599 	}
600       break;
601     default:
602       /* in default case, i.e. S_UNKNOWN, we won't plot linear subticks */
603       lin_subtick_spacing = tick_spacing; /* not actually needed, since not plotted */
604       break;
605     }
606 
607   /* smallest possible inter-subtick factor for a log axis is 10.0 */
608   if (log_axis && lin_subtick_spacing <= 1.0)
609     lin_subtick_spacing = 1.0;
610 
611   min_lin_subtick_count = (int)(ceil((min - FUZZ * range)/ lin_subtick_spacing));
612   max_lin_subtick_count = (int)(floor((max + FUZZ * range)/ lin_subtick_spacing));
613   have_lin_subticks
614     = ((tick_spacing_type != S_UNKNOWN /* S_UNKNOWN -> no subticks */
615 	&& (max_lin_subtick_count - min_lin_subtick_count) <= MAX_NUM_SUBTICKS)
616        ? true : false);
617 
618   /* fill in parameters for axis-specific affine transformation */
619   trans->input_min = min;
620   trans->input_max = max;
621   trans->input_range = range;	/* precomputed for speed */
622   trans->reverse = reverse_axis;
623 
624   /* fill in axis-specific plot frame variables */
625   axisp->max_label_width = 0.0;
626   axisp->type = log_axis ? A_LOG10 : A_LINEAR;
627   axisp->tick_spacing = tick_spacing;
628   axisp->min_tick_count = min_tick_count;
629   axisp->max_tick_count = max_tick_count;
630   axisp->have_lin_subticks = have_lin_subticks;
631   axisp->lin_subtick_spacing = lin_subtick_spacing;
632   axisp->min_lin_subtick_count = min_lin_subtick_count;
633   axisp->max_lin_subtick_count = max_lin_subtick_count;
634   axisp->user_specified_subsubticks = user_specified_subsubticks;
635   axisp->subsubtick_spacing = subsubtick_spacing;
636   axisp->labelled_ticks = 0;	/* updated during drawing of frame */
637 
638   if (log_axis)		/* logarithmic axis */
639     /* do we have special logarithmic subsubticks, and should we label them? */
640     {
641       if (max - min <=
642 	  MAX_DECADES_WITH_LOG_SUBSUBTICKS + FUZZ)
643 	/* not too many orders of magnitude, so plot normal log subsubticks */
644 	axisp->have_normal_subsubticks = true;
645       else
646 	/* too many orders of magnitude, don't plot log subsubticks */
647 	axisp->have_normal_subsubticks = false;
648     }
649   else				/* linear axes don't have log subsubticks */
650     axisp->have_normal_subsubticks = false;
651 }
652 
653 /* The following routines [new_multigrapher(), begin_graph(),
654  * set_graph_parameters(), draw_frame_of_graph(), plot_point(),
655  * end_graph(), delete_multigrapher()] are the basic routines of the
656  * point-plotter .  See descriptions at the head of this file.  */
657 
658 /* Create a new Multigrapher.  The arguments, after the first, are the
659    libplot Plotter parameters that the `graph' user can set on the command
660    line. */
661 
662 Multigrapher *
new_multigrapher(const char * display_type,const char * bg_color,const char * bitmap_size,const char * max_line_length,const char * meta_portable,const char * page_size,const char * rotation_angle,int save_screen)663 new_multigrapher(const char *display_type,
664 	const char *bg_color,
665 	const char *bitmap_size,
666 	const char *max_line_length,
667 	const char *meta_portable,
668 	const char *page_size,
669 	const char *rotation_angle,
670 	int save_screen)
671 {
672   plPlotterParams *plotter_params;
673   plPlotter *plotter;
674   Multigrapher *multigrapher;
675 
676   multigrapher = (Multigrapher *)malloc (sizeof (Multigrapher));
677 
678   /* set Plotter parameters */
679   plotter_params = pl_newplparams ();
680   pl_setplparam (plotter_params, "BG_COLOR", (void *)bg_color);
681   pl_setplparam (plotter_params, "BITMAPSIZE", (void *)bitmap_size);
682   pl_setplparam (plotter_params, "MAX_LINE_LENGTH", (void *)max_line_length);
683   pl_setplparam (plotter_params, "META_PORTABLE", (void *)meta_portable);
684   pl_setplparam (plotter_params, "PAGESIZE", (void *)page_size);
685   pl_setplparam (plotter_params, "ROTATION", (void *)rotation_angle);
686 
687   /* create Plotter and open it */
688   plotter = pl_newpl_r (display_type, NULL, stdout, stderr, plotter_params);
689   if (plotter == (plPlotter *)NULL)
690     return (Multigrapher *)NULL;
691   pl_deleteplparams (plotter_params);
692   multigrapher->plotter = plotter;
693   if (pl_openpl_r (plotter) < 0)
694     return (Multigrapher *)NULL;
695   multigrapher->bg_color = bg_color;
696 
697   /* if called for, erase it; set up the user->device coor map */
698   if (!save_screen || bg_color)
699     pl_erase_r (plotter);
700   pl_fspace_r (plotter, 0.0, 0.0, (double)PLOT_SIZE, (double)PLOT_SIZE);
701 
702   return multigrapher;
703 }
704 
705 int
delete_multigrapher(Multigrapher * multigrapher)706 delete_multigrapher (Multigrapher *multigrapher)
707 {
708   int retval;
709 
710   retval = pl_closepl_r (multigrapher->plotter);
711   if (retval >= 0)
712     retval = pl_deletepl_r (multigrapher->plotter);
713 
714   free (multigrapher);
715   return retval;
716 }
717 
718 static void
sp_xy_begin_graph(Multigrapher * multigrapher,double scale,double trans_x,double trans_y)719 sp_xy_begin_graph(Multigrapher *multigrapher, double scale, double trans_x, double trans_y)
720 {
721   pl_savestate_r (multigrapher->plotter);
722   pl_fconcat_r (multigrapher->plotter,
723 		scale, 0.0, 0.0, scale,
724 		trans_x * PLOT_SIZE, trans_y * PLOT_SIZE);
725 }
726 
727 static void
sp_xy_end_graph(Multigrapher * multigrapher)728 sp_xy_end_graph (Multigrapher *multigrapher)
729 {
730   pl_restorestate_r (multigrapher->plotter);
731 }
732 
733 static void
OtherAxisLoc(Multigrapher * multigrapher)734 OtherAxisLoc(Multigrapher *multigrapher)
735 {
736   /* fill in fields in Axis structs dealing with location of other axis */
737   if (multigrapher->grid_spec != AXES_AT_ORIGIN)
738     /* Normal case */
739     {
740       /* axes are at left/bottom */
741       multigrapher->x_axis.other_axis_loc = multigrapher->x_trans.input_min;
742       multigrapher->y_axis.other_axis_loc = multigrapher->y_trans.input_min;
743       /* secondary axes (used only if --switch-axis-end is specified) */
744       multigrapher->x_axis.alt_other_axis_loc = multigrapher->x_trans.input_max;
745       multigrapher->y_axis.alt_other_axis_loc = multigrapher->y_trans.input_max;
746     }
747   else
748     /* Special case: grid type #4, AXES_AT_ORIGIN */
749     {
750       /* In this case (grid type #4), we don't allow the user to move the
751          axis position by using the --switch-axis-end option.  Each axis is
752          at the value 0 (the origin) if the value 0 is between the limits
753          of the opposing axis.  Otherwise, the position is at the end
754          closer to the value of 0. */
755       multigrapher->x_axis.other_axis_loc
756 	= (multigrapher->x_trans.input_min * multigrapher->x_trans.input_max <= 0.0) ? 0.0 :
757 	  (multigrapher->x_trans.input_min > 0.0 ? multigrapher->x_trans.input_min : multigrapher->x_trans.input_max);
758       multigrapher->y_axis.other_axis_loc
759 	= (multigrapher->y_trans.input_min * multigrapher->y_trans.input_max <= 0.0) ? 0.0 :
760 	  (multigrapher->y_trans.input_min > 0.0 ? multigrapher->y_trans.input_min : multigrapher->y_trans.input_max);
761       /* secondary axes are the same */
762       multigrapher->x_axis.alt_other_axis_loc = multigrapher->x_axis.other_axis_loc;
763       multigrapher->y_axis.alt_other_axis_loc = multigrapher->y_axis.other_axis_loc;
764       multigrapher->x_axis.switch_axis_end = (((multigrapher->x_trans.input_max - multigrapher->x_axis.other_axis_loc)
765 				 < (multigrapher->x_axis.other_axis_loc - multigrapher->x_trans.input_min))
766 				? true : false);
767       multigrapher->y_axis.switch_axis_end = (((multigrapher->y_trans.input_max - multigrapher->y_axis.other_axis_loc)
768 				 < (multigrapher->y_axis.other_axis_loc - multigrapher->y_trans.input_min))
769 				? true : false);
770     }
771 }
772 
773 void
set_graph_parameters(Multigrapher * multigrapher,double frame_line_width,const char * frame_color,const char * title,const char * title_font_name,double title_font_size,double tick_size,grid_type grid_spec,double x_min,double x_max,double x_spacing,double y_min,double y_max,double y_spacing,int spec_x_spacing,int spec_y_spacing,double width,double height,double up,double right,const char * x_font_name,double x_font_size,const char * x_label,const char * y_font_name,double y_font_size,const char * y_label,int no_rotate_y_label,int log_axis,int round_to_next_tick,int switch_axis_end,int omit_ticks,int clip_mode,double blankout_fraction,int transpose_axes)774 set_graph_parameters (Multigrapher *multigrapher,
775 		double frame_line_width,	/* fractional width of lines in the frame */
776 		const char *frame_color,	/* color for frame (and plot if no -C option) */
777 		const char *title,		/* graph title */
778 		const char *title_font_name,	/* font for graph title (string) */
779 		double title_font_size,		/* font size for graph title  */
780 		double tick_size,		/* fractional size of ticks */
781 		grid_type grid_spec,		/* gridstyle (and tickstyle) spec */
782 		double x_min,
783 		double x_max,
784 		double x_spacing,
785 		double y_min,
786 		double y_max,
787 		double y_spacing,
788 		int spec_x_spacing,
789 		int spec_y_spacing,
790 		double width,
791 		double height,
792 		double up,
793 		double right,
794 		const char *x_font_name,
795 		double x_font_size,
796 		const char *x_label,
797 		const char *y_font_name,
798 		double y_font_size,
799 		const char *y_label,
800 		int no_rotate_y_label,
801 		int log_axis,			/* whether axes should be logarithmic */
802 		int round_to_next_tick,		/* round limits to the next tick mark */
803 		int switch_axis_end,		/* put axes at right/top, not left/bottom? */
804 		int omit_ticks,			/* omit all ticks, tick labels from an axis? */
805 		int clip_mode,			/* clip mode = 0, 1, or 2 */
806 		double blankout_fraction,	/* 1.0 means blank out whole box before plot */
807 		int transpose_axes)
808 {
809   double x_subsubtick_spacing = 0.0, y_subsubtick_spacing = 0.0;
810   /* local portmanteau variables */
811   int reverse_axis = 0;		/* min > max on an axis? */
812   int user_specified_subsubticks = 0; /* i.e. linear ticks on a log axis? */
813 
814   if (log_axis & X_AXIS)
815     {
816       if (spec_x_spacing)
817 	/* spacing is handled specially for log axes */
818 	{
819 	  spec_x_spacing = false;
820 	  user_specified_subsubticks |= X_AXIS;
821 	  x_subsubtick_spacing = x_spacing;
822 	}
823     }
824 
825   if (log_axis & Y_AXIS)
826     {
827       if (spec_y_spacing)
828 	{
829 	  /* spacing is handled specially for log axes */
830 	  spec_y_spacing = false;
831 	  user_specified_subsubticks |= Y_AXIS;
832 	  y_subsubtick_spacing = y_spacing;
833 	}
834     }
835 
836   /* check for reversed axes (min > max) */
837   if (x_max < x_min)
838     {
839       reverse_axis |= X_AXIS;
840       {
841 	double temp;
842 
843 	temp = x_min;
844 	x_min = x_max;
845 	x_max = temp;
846       }
847     }
848   if (x_max == x_min)
849     {
850       fprintf (stderr,
851 	       "%s: separating identical upper and lower x limits\n",
852 	       progname);
853       /* separate them */
854       x_max += 1.0;
855       x_min -= 1.0;
856     }
857   /* check for reversed axes (min > max) */
858   if (y_max < y_min)
859     {
860       reverse_axis |= Y_AXIS;
861       {
862 	double temp;
863 
864 	temp = y_min;
865 	y_min = y_max;
866 	y_max = temp;
867       }
868     }
869   if (y_max == y_min)
870     {
871       fprintf (stderr,
872 	       "%s: separating identical upper and lower y limits\n",
873 	       progname);
874       /* separate them */
875       y_max += 1.0;
876       y_min -= 1.0;
877     }
878 
879   /* At this point, min < max for each axis, if the user specified the two
880      limits on an axis; reverse_axis portmanteau variable keeps track of
881      whether either axis was discovered to be reversed. */
882 
883   /* silently accept negative spacing as equivalent as positive */
884   if (spec_x_spacing)
885     {
886       if (x_spacing == 0.0)
887 	{
888 	  fprintf (stderr,
889 		   "%s: error: zero spacing between ticks on an axis\n",
890 		   progname);
891 	  exit (EXIT_FAILURE);
892 	}
893       x_spacing = fabs (x_spacing);
894     }
895   if (spec_y_spacing)
896     {
897       if (y_spacing == 0.0)
898 	{
899 	  fprintf (stderr,
900 		   "%s: error: zero spacing between ticks on an axis\n",
901 		   progname);
902 	  exit (EXIT_FAILURE);
903 	}
904       y_spacing = fabs (y_spacing);
905     }
906 
907   /* now transpose the two axes (i.e. their portmanteau variables, labels,
908      limits etc.) if transpose_axes was set */
909   if (transpose_axes)
910     {
911       const char *temp_string;
912       double temp_double;
913 
914       transpose_portmanteau (&log_axis);
915       transpose_portmanteau (&round_to_next_tick);
916       transpose_portmanteau (&switch_axis_end);
917       transpose_portmanteau (&omit_ticks);
918 
919       transpose_portmanteau (&reverse_axis);
920       transpose_portmanteau (&user_specified_subsubticks);
921 
922       temp_string = x_label;
923       x_label = y_label;
924       y_label = temp_string;
925 
926       temp_double = x_min;
927       x_min = y_min;
928       y_min = temp_double;
929 
930       temp_double = x_max;
931       x_max = y_max;
932       y_max = temp_double;
933 
934       temp_double = x_spacing;
935       x_spacing = y_spacing;
936       y_spacing = temp_double;
937 
938       temp_double = x_subsubtick_spacing;
939       x_subsubtick_spacing = y_subsubtick_spacing;
940       y_subsubtick_spacing = temp_double;
941     }
942 
943   /* fill in the Multigrapher struct */
944 
945   multigrapher->frame_line_width = frame_line_width;
946   multigrapher->frame_color = strdup(frame_color);
947   multigrapher->no_rotate_y_label = no_rotate_y_label;
948   multigrapher->blankout_fraction = blankout_fraction;
949 
950   if (title != NULL)
951     multigrapher->title = strdup (title);
952   else
953     multigrapher->title = NULL;
954   if (title_font_name != NULL)
955       multigrapher->title_font_name = strdup (title_font_name);
956   else
957     multigrapher->title_font_name = NULL;
958   multigrapher->title_font_size = title_font_size;
959   multigrapher->tick_size = tick_size;
960   multigrapher->subtick_size = RELATIVE_SUBTICK_SIZE * tick_size;
961   multigrapher->grid_spec = grid_spec;
962   multigrapher->clip_mode = clip_mode;
963 
964   multigrapher->x_axis.font_size = x_font_size;
965   multigrapher->y_axis.font_size = y_font_size;
966   multigrapher->x_axis.font_name = x_font_name;
967   multigrapher->y_axis.font_name = y_font_name;
968 
969   multigrapher->x_axis.switch_axis_end = (bool)(switch_axis_end & X_AXIS);
970   multigrapher->y_axis.switch_axis_end = (bool)(switch_axis_end & Y_AXIS);
971 
972   multigrapher->x_axis.omit_ticks = (bool)(omit_ticks & X_AXIS);
973   multigrapher->y_axis.omit_ticks = (bool)(omit_ticks & Y_AXIS);
974 
975   multigrapher->x_min = x_min;
976   multigrapher->x_max = x_max;
977   multigrapher->x_spacing = x_spacing;
978   multigrapher->x_subsubtick_spacing = x_subsubtick_spacing;
979 
980   multigrapher->y_min = y_min;
981   multigrapher->y_max = y_max;
982   multigrapher->y_spacing = y_spacing;
983   multigrapher->y_subsubtick_spacing = y_subsubtick_spacing;
984 
985   multigrapher->user_specified_subsubticks = user_specified_subsubticks;
986   multigrapher->round_to_next_tick = round_to_next_tick;
987   multigrapher->log_axis = log_axis;
988   multigrapher->reverse_axis = reverse_axis;
989 
990   /* fill in the Transform and Axis elements for each coordinate */
991   prepare_axis (&multigrapher->x_axis, &multigrapher->x_trans,
992 		x_min, x_max, x_spacing,
993 		x_subsubtick_spacing,
994 		(bool)(user_specified_subsubticks & X_AXIS),
995 		(bool)(round_to_next_tick & X_AXIS),
996 		(bool)(log_axis & X_AXIS),
997 		(bool)(reverse_axis & X_AXIS));
998   prepare_axis (&multigrapher->y_axis, &multigrapher->y_trans,
999 		y_min, y_max, y_spacing,
1000 		y_subsubtick_spacing,
1001 		(bool)(user_specified_subsubticks & Y_AXIS),
1002 		(bool)(round_to_next_tick & Y_AXIS),
1003 		(bool)(log_axis & Y_AXIS),
1004 		(bool)(reverse_axis & Y_AXIS));
1005 
1006   /* fill in additional parameters in the two Transform structures */
1007   multigrapher->x_trans.squeezed_min = right;
1008   multigrapher->x_trans.squeezed_max = right + width;
1009   multigrapher->x_trans.squeezed_range = width;
1010   multigrapher->y_trans.squeezed_min = up;
1011   multigrapher->y_trans.squeezed_max = up + height;
1012   multigrapher->y_trans.squeezed_range = height;
1013 
1014   /* specify interval range for each coordinate, in libplot units */
1015   multigrapher->x_trans.output_min = 0.0;
1016   multigrapher->x_trans.output_max = (double)PLOT_SIZE;
1017   multigrapher->x_trans.output_range = multigrapher->x_trans.output_max - multigrapher->x_trans.output_min;
1018   multigrapher->x_trans.output_min = 0.0;
1019   multigrapher->y_trans.output_max = (double)PLOT_SIZE;
1020   multigrapher->y_trans.output_range = multigrapher->y_trans.output_max - multigrapher->y_trans.output_min;
1021 
1022   OtherAxisLoc(multigrapher);
1023 
1024   /* The following is a version of (multigrapher->frame_line_width)/2
1025      (expressed in terms of libplot coordinates) which the plotter uses as
1026      an offset, to get highly accurate positioning of ticks and labels. */
1027   if (frame_line_width < 0.0
1028       || pl_havecap_r (multigrapher->plotter, "WIDE_LINES") == 0)
1029     multigrapher->half_line_width = 0.0;/* N.B. <0.0 -> default width, pres. small */
1030   else
1031     multigrapher->half_line_width = 0.5 * frame_line_width * multigrapher->x_trans.output_range;
1032 
1033   /* initialize the plotter state variables */
1034   multigrapher->first_point_of_polyline = true;
1035   multigrapher->oldpoint_x = 0.0;
1036   multigrapher->oldpoint_y = 0.0;
1037 }
1038 
1039 
1040 /* draw_frame_of_graph() plots the graph frame (grid plus axis labels and
1041    title), using the multigrapher's variables (the multigrapher->x_axis,
1042    multigrapher->y_axis, multigrapher->x_trans, multigrapher->y_trans
1043    structures).  It comprises several almost-independent tasks:
1044 
1045    0. draw opaque white rectangle (``canvas''), if requested
1046    1. draw title, if any, above drawing box
1047    2. draw axes, and a full drawing box if requested
1048    3. draw ticks, grid lines, and tick labels on abscissa;
1049       also subticks, if abscissa is a linear axis
1050    4. draw ticks, grid lines, and tick labels on ordinate,
1051       also subticks, if ordinate is a linear axis
1052    5. draw sub-tick marks, sub-grid lines, and sub-labels,
1053       if abscissa is a logarithmic axis
1054    6. draw sub-tick marks, sub-grid lines, and sub-labels,
1055       if ordinate is a logarithmic axis
1056    7. draw axis label on abscissa
1057    8. draw axis label on ordinate (normally rotated 90 degrees)
1058 
1059    A savestate()--restorestate() pair is wrapped around these nine tasks.
1060    They are not quite independent because in (4) and (6) we keep track of
1061    the maximum width of the tick labels on the ordinate, to position the
1062    rotated ordinate label properly in (8).
1063 
1064    At the conclusion of the eight tasks, we warn the user if (i) he/she
1065    didn't specify a tick spacing for a logarithmic axis by hand, and (ii)
1066    our default algorithm for drawing ticks on a logarithmic axis has drawn
1067    too few ticks (i.e. <= 2 ticks) on the axis.
1068 
1069    Task #0 (drawing a white canvas) isn't always performed; only if the
1070    argument draw_canvas is set.  It isn't set when we call this function
1071    for the first time; see graph.c.  */
1072 
1073 void
draw_frame_of_graph(Multigrapher * multigrapher,int draw_canvas)1074 draw_frame_of_graph (Multigrapher *multigrapher, int draw_canvas)
1075 {
1076   static bool tick_warning_printed = false; /* when too few labelled ticks */
1077 
1078   /* wrap savestate()--restorestate() around all 9 tasks */
1079   pl_savestate_r (multigrapher->plotter);
1080 
1081   /* set color for graph frame */
1082   if (multigrapher->frame_color)
1083     pl_pencolorname_r (multigrapher->plotter, multigrapher->frame_color);
1084 
1085   /* set line width as a fraction of size of display, <0.0 means default */
1086   pl_flinewidth_r (multigrapher->plotter,
1087 		   multigrapher->frame_line_width * (double)PLOT_SIZE);
1088 
1089   /* axes (or box) will be drawn in solid line style */
1090   pl_linemod_r (multigrapher->plotter, "solid");
1091 
1092   /* turn off filling */
1093   pl_filltype_r (multigrapher->plotter, 0);
1094 
1095   /* 0.  DRAW AN OPAQUE WHITE BOX */
1096 
1097   if (draw_canvas)
1098     {
1099       pl_savestate_r (multigrapher->plotter);
1100       /* use user-specified background color (if any) instead of white */
1101       if (pl_havecap_r (multigrapher->plotter, "SETTABLE_BACKGROUND") != 0
1102 	  && multigrapher->bg_color)
1103 	pl_colorname_r (multigrapher->plotter, multigrapher->bg_color);
1104       else
1105 	pl_colorname_r (multigrapher->plotter, "white");
1106 
1107       pl_filltype_r (multigrapher->plotter, 1);	/* turn on filling */
1108       pl_fbox_r (multigrapher->plotter,
1109 		 XP(XSQ(0.5 - 0.5 * multigrapher->blankout_fraction)),
1110 		 YP(YSQ(0.5 - 0.5 * multigrapher->blankout_fraction)),
1111 		 XP(XSQ(0.5 + 0.5 * multigrapher->blankout_fraction)),
1112 		 YP(YSQ(0.5 + 0.5 * multigrapher->blankout_fraction)));
1113       pl_restorestate_r (multigrapher->plotter);
1114     }
1115 
1116   /* 1.  DRAW THE TITLE, I.E. THE TOP LABEL */
1117 
1118   if (multigrapher->grid_spec != NO_AXES
1119       && !multigrapher->y_axis.switch_axis_end /* no title if x axis is at top of plot */
1120       && multigrapher->title != NULL && *multigrapher->title != '\0')
1121     {
1122       double title_font_size;
1123 
1124       /* switch to our font for drawing title */
1125       pl_fontname_r (multigrapher->plotter, multigrapher->title_font_name);
1126       title_font_size = pl_ffontsize_r (multigrapher->plotter,
1127 					SS(multigrapher->title_font_size));
1128 
1129       pl_fmove_r (multigrapher->plotter,
1130 		  XP(XSQ(0.5)),
1131 		  YP(YSQ(1.0
1132 			 + (((multigrapher->grid_spec == AXES_AND_BOX
1133 			      || multigrapher->grid_spec == AXES)
1134 			     && (multigrapher->tick_size <= 0.0) ? 1.0 : 0.5)
1135 			    * fabs(multigrapher->tick_size))))
1136 		  + 0.65 * title_font_size
1137 		  + multigrapher->half_line_width);
1138       /* title centered, bottom spec'd */
1139       pl_alabel_r (multigrapher->plotter, 'c', 'b', multigrapher->title);
1140     }
1141 
1142   /* 2.  DRAW AXES FOR THE PLOT */
1143 
1144   switch (multigrapher->grid_spec)
1145     {
1146     case AXES_AND_BOX_AND_GRID:
1147     case AXES_AND_BOX:
1148       /* draw a box, not just a pair of axes */
1149       pl_fbox_r (multigrapher->plotter,
1150 		 XP(XSQ(0.0)), YP(YSQ(0.0)), XP(XSQ(1.0)), YP(YSQ(1.0)));
1151       break;
1152     case AXES:
1153       {
1154 	double xstart, ystart, xmid, ymid, xend, yend;
1155 
1156 	xstart = (multigrapher->x_axis.switch_axis_end
1157 		  ? XN(multigrapher->x_axis.other_axis_loc) - multigrapher->half_line_width
1158 		  : XN(multigrapher->x_axis.alt_other_axis_loc) + multigrapher->half_line_width);
1159 	ystart = (multigrapher->y_axis.switch_axis_end
1160 		  ? YN(multigrapher->y_axis.alt_other_axis_loc)
1161 		  : YN(multigrapher->y_axis.other_axis_loc));
1162 	xmid = (multigrapher->x_axis.switch_axis_end
1163 		? XN(multigrapher->x_axis.alt_other_axis_loc)
1164 		: XN(multigrapher->x_axis.other_axis_loc));
1165 	ymid = ystart;
1166 	xend = xmid;
1167 	yend = (multigrapher->y_axis.switch_axis_end
1168 		? YN(multigrapher->y_axis.other_axis_loc) - multigrapher->half_line_width
1169 		: YN(multigrapher->y_axis.alt_other_axis_loc) + multigrapher->half_line_width);
1170 
1171 	pl_fmove_r (multigrapher->plotter, xstart, ystart);
1172 	pl_fcont_r (multigrapher->plotter, xmid, ymid);
1173 	pl_fcont_r (multigrapher->plotter, xend, yend);
1174       }
1175       break;
1176     case AXES_AT_ORIGIN:
1177       {
1178 	double xpos, ypos;
1179 
1180 	xpos = (multigrapher->x_axis.switch_axis_end
1181 		? XN(multigrapher->x_axis.other_axis_loc)
1182 		: XN(multigrapher->x_axis.alt_other_axis_loc));
1183 	ypos = (multigrapher->y_axis.switch_axis_end
1184 		? YN(multigrapher->y_axis.alt_other_axis_loc)
1185 		: YN(multigrapher->y_axis.other_axis_loc));
1186 
1187 	pl_fline_r (multigrapher->plotter,
1188 		    xpos, YP(YSQ(0.0)) - multigrapher->half_line_width,
1189 		    xpos, YP(YSQ(1.0)) + multigrapher->half_line_width);
1190 	pl_fline_r (multigrapher->plotter,
1191 		    XP(XSQ(0.0)) - multigrapher->half_line_width, ypos,
1192 		    XP(XSQ(1.0)) + multigrapher->half_line_width, ypos);
1193       }
1194       break;
1195     case NO_AXES:
1196     default:
1197       break;
1198     }
1199 
1200   /* 3.  PLOT TICK MARKS, GRID LINES, AND TICK LABELS ON ABSCISSA */
1201 
1202   if (multigrapher->grid_spec != NO_AXES && !multigrapher->x_axis.omit_ticks
1203       && !multigrapher->x_axis.user_specified_subsubticks)
1204     {
1205       int i;
1206       double xval, xrange = multigrapher->x_trans.input_max - multigrapher->x_trans.input_min;
1207       /* there is no way you could use longer labels on tick marks! */
1208       char labelbuf[2048];
1209 
1210       /* switch to our font for drawing x axis label and tick labels */
1211       pl_fontname_r (multigrapher->plotter, multigrapher->x_axis.font_name);
1212       pl_ffontsize_r (multigrapher->plotter, SS(multigrapher->x_axis.font_size));
1213 
1214       for (i = multigrapher->x_axis.min_tick_count; i <= multigrapher->x_axis.max_tick_count; i++)
1215 	/* tick range can be empty */
1216 	{
1217 	  xval = i * multigrapher->x_axis.tick_spacing;
1218 
1219 	  /* discard tick locations outside plotting area */
1220 	  if (xval < multigrapher->x_trans.input_min - FUZZ * xrange
1221 	      || xval > multigrapher->x_trans.input_max + FUZZ * xrange)
1222 	    continue;
1223 
1224 	  /* Plot the abscissa tick labels. */
1225 	  if (!multigrapher->y_axis.switch_axis_end
1226 	      && !(multigrapher->grid_spec == AXES_AT_ORIGIN
1227 		   /* don't plot label if it could run into an axis */
1228 		   && NEAR_EQUALITY (xval, multigrapher->x_axis.other_axis_loc,
1229 				     multigrapher->x_trans.input_range)
1230 		   && (multigrapher->y_axis.other_axis_loc != multigrapher->y_trans.input_min)
1231 		   && (multigrapher->y_axis.other_axis_loc != multigrapher->y_trans.input_max)))
1232 	    /* print labels below bottom boundary */
1233 	    {
1234 	      pl_fmove_r (multigrapher->plotter,
1235 			  XV (xval),
1236 			  YN (multigrapher->y_axis.other_axis_loc)
1237 			  - (SS ((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75) * fabs(multigrapher->tick_size))
1238 			     + multigrapher->half_line_width));
1239 	      print_tick_label (labelbuf,
1240 				&multigrapher->x_axis, &multigrapher->x_trans,
1241 				(multigrapher->x_axis.type == A_LOG10) ? pow (10.0, xval) : xval);
1242 	      pl_alabel_r (multigrapher->plotter, 'c', 't', labelbuf);
1243 	      multigrapher->x_axis.labelled_ticks++;
1244 	    }
1245 	  else
1246 	    /* print labels above top boundary */
1247 	    if (multigrapher->y_axis.switch_axis_end
1248 	      && !(multigrapher->grid_spec == AXES_AT_ORIGIN
1249 		   /* don't plot label if it could run into an axis */
1250 		   && NEAR_EQUALITY (xval, multigrapher->x_axis.other_axis_loc,
1251 				     multigrapher->x_trans.input_range)
1252 		   && (multigrapher->y_axis.other_axis_loc != multigrapher->y_trans.input_min)
1253 		   && (multigrapher->y_axis.other_axis_loc != multigrapher->y_trans.input_max)))
1254 	      {
1255 		pl_fmove_r (multigrapher->plotter,
1256 			    XV (xval),
1257 			    YN (multigrapher->y_axis.alt_other_axis_loc)
1258 			    + (SS ((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75) * fabs(multigrapher->tick_size))
1259 			     + multigrapher->half_line_width));
1260 		print_tick_label (labelbuf,
1261 				  &multigrapher->x_axis, &multigrapher->x_trans,
1262 				  (multigrapher->x_axis.type == A_LOG10) ? pow (10.0, xval) : xval);
1263 		pl_alabel_r (multigrapher->plotter, 'c', 'b', labelbuf);
1264 		multigrapher->x_axis.labelled_ticks++;
1265 	      }
1266 
1267 	  /* Plot the abscissa tick marks, and vertical grid lines. */
1268 	  switch (multigrapher->grid_spec)
1269 	    {
1270 	    case AXES_AND_BOX_AND_GRID:
1271 	      pl_linemod_r (multigrapher->plotter, "dotted");
1272 	      pl_fmove_r (multigrapher->plotter, XV(xval), YP(YSQ(0.0)));
1273 	      pl_fcont_r (multigrapher->plotter, XV(xval), YP(YSQ(1.0)));
1274 	      pl_linemod_r (multigrapher->plotter, "solid");
1275 	      /* fall through */
1276 	    case AXES_AND_BOX:
1277 	      if (!multigrapher->y_axis.switch_axis_end)
1278 		{
1279 		  pl_fmove_r (multigrapher->plotter,
1280 			      XV (xval),
1281 			      YN (multigrapher->y_axis.alt_other_axis_loc));
1282 		  pl_fcont_r (multigrapher->plotter,
1283 			      XV (xval),
1284 			      YN (multigrapher->y_axis.alt_other_axis_loc)
1285 			      - (SS (multigrapher->tick_size)
1286 				 + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
1287 				    : -multigrapher->half_line_width)));
1288 		}
1289 	      else
1290 		{
1291 		  pl_fmove_r (multigrapher->plotter,
1292 			      XV (xval),
1293 			      YN (multigrapher->y_axis.other_axis_loc));
1294 		  pl_fcont_r (multigrapher->plotter,
1295 			      XV (xval),
1296 			      YN (multigrapher->y_axis.other_axis_loc)
1297 			      + (SS (multigrapher->tick_size)
1298 				 + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
1299 				    : -multigrapher->half_line_width)));
1300 		}
1301 	      /* fall through */
1302 	    case AXES:
1303 	    case AXES_AT_ORIGIN:
1304 	      if (!multigrapher->y_axis.switch_axis_end)
1305 		{
1306 		  pl_fmove_r (multigrapher->plotter,
1307 			      XV (xval),
1308 			      YN (multigrapher->y_axis.other_axis_loc));
1309 		  pl_fcont_r (multigrapher->plotter,
1310 			      XV (xval),
1311 			      YN (multigrapher->y_axis.other_axis_loc)
1312 			      + (SS (multigrapher->tick_size)
1313 				 + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
1314 				    : -multigrapher->half_line_width)));
1315 		}
1316 	      else
1317 		{
1318 		  pl_fmove_r (multigrapher->plotter,
1319 			      XV (xval),
1320 			      YN (multigrapher->y_axis.alt_other_axis_loc));
1321 		  pl_fcont_r (multigrapher->plotter,
1322 			      XV (xval),
1323 			      YN (multigrapher->y_axis.alt_other_axis_loc)
1324 			      - (SS (multigrapher->tick_size)
1325 				 + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
1326 				    : -multigrapher->half_line_width)));
1327 		}
1328 	      break;
1329 	    default:		/* shouldn't happen */
1330 	      break;
1331 	    }
1332 	}
1333 
1334       if (multigrapher->x_axis.have_lin_subticks)
1335 	{
1336 	  double subtick_size;	/* libplot coordinates */
1337 
1338 	  /* linearly spaced subticks on log axes are as long as reg. ticks */
1339 	  subtick_size = (multigrapher->x_axis.type == A_LOG10
1340 			  ? SS(multigrapher->tick_size) : SS(multigrapher->subtick_size));
1341 
1342 	  /* Plot the linearly spaced subtick marks on the abscissa */
1343 	  for (i = multigrapher->x_axis.min_lin_subtick_count; i <= multigrapher->x_axis.max_lin_subtick_count; i++)
1344 	    /* tick range can be empty */
1345 	    {
1346 	      xval = i * multigrapher->x_axis.lin_subtick_spacing;
1347 
1348 	      /* discard subtick locations outside plotting area */
1349 	      if (xval < multigrapher->x_trans.input_min - FUZZ * xrange
1350 		  || xval > multigrapher->x_trans.input_max + FUZZ * xrange)
1351 		continue;
1352 
1353 	      switch (multigrapher->grid_spec)
1354 		{
1355 		case AXES_AND_BOX_AND_GRID:
1356 		case AXES_AND_BOX:
1357 		  /* draw on both sides */
1358 		  if (!multigrapher->y_axis.switch_axis_end)
1359 		    {
1360 		      pl_fmove_r (multigrapher->plotter,
1361 				  XV (xval),
1362 				  YN (multigrapher->y_axis.alt_other_axis_loc));
1363 		      pl_fcont_r (multigrapher->plotter,
1364 				  XV (xval),
1365 				  YN (multigrapher->y_axis.alt_other_axis_loc)
1366 				  - (subtick_size
1367 				     + (subtick_size > 0.0 ? multigrapher->half_line_width
1368 					: -multigrapher->half_line_width)));
1369 		    }
1370 		  else
1371 		    {
1372 		      pl_fmove_r (multigrapher->plotter,
1373 				  XV (xval),
1374 				  YN (multigrapher->y_axis.other_axis_loc));
1375 		      pl_fcont_r (multigrapher->plotter,
1376 				  XV (xval),
1377 				  YN (multigrapher->y_axis.other_axis_loc)
1378 				  + (subtick_size
1379 				     + (subtick_size > 0.0 ? multigrapher->half_line_width
1380 					: -multigrapher->half_line_width)));
1381 		    }
1382 		  /* fall through */
1383 		case AXES:
1384 		case AXES_AT_ORIGIN:
1385 		  if (!multigrapher->y_axis.switch_axis_end)
1386 		    /* draw on only one side */
1387 		    {
1388 		      pl_fmove_r (multigrapher->plotter,
1389 				  XV (xval),
1390 				  YN (multigrapher->y_axis.other_axis_loc));
1391 		      pl_fcont_r (multigrapher->plotter,
1392 				  XV (xval),
1393 				  YN (multigrapher->y_axis.other_axis_loc)
1394 				  + (subtick_size
1395 				     + (subtick_size > 0.0 ? multigrapher->half_line_width
1396 					: -multigrapher->half_line_width)));
1397 		    }
1398 		  else
1399 		    {
1400 		      pl_fmove_r (multigrapher->plotter,
1401 				  XV (xval),
1402 				  YN (multigrapher->y_axis.alt_other_axis_loc));
1403 		      pl_fcont_r (multigrapher->plotter,
1404 				  XV (xval),
1405 				  YN (multigrapher->y_axis.alt_other_axis_loc)
1406 				  - (subtick_size
1407 				     + (subtick_size > 0.0 ? multigrapher->half_line_width
1408 					: -multigrapher->half_line_width)));
1409 		    }
1410 		  break;
1411 		default:		/* shouldn't happen */
1412 		  break;
1413 		}
1414 	    }
1415 	}
1416 
1417       /* plot a vertical dotted line at x = 0 */
1418       if (multigrapher->grid_spec != AXES_AT_ORIGIN
1419 	  && multigrapher->x_axis.type == A_LINEAR
1420 	  && multigrapher->x_trans.input_min * multigrapher->x_trans.input_max < 0.0)
1421 	{
1422 	  pl_linemod_r (multigrapher->plotter, "dotted");
1423 	  pl_fline_r (multigrapher->plotter,
1424 		      XV(0.0), YP(YSQ(0.0)), XV(0.0), YP(YSQ(1.0)));
1425 	  pl_linemod_r (multigrapher->plotter, "solid");
1426 	}
1427     }
1428 
1429   /* 4.  PLOT TICK MARKS, GRID LINES, AND TICK LABELS ON ORDINATE */
1430 
1431   if (multigrapher->grid_spec != NO_AXES && !multigrapher->y_axis.omit_ticks
1432       && !multigrapher->y_axis.user_specified_subsubticks)
1433     {
1434       int i;
1435       double yval, yrange = multigrapher->y_trans.input_max - multigrapher->y_trans.input_min;
1436       /* there is no way you could use longer labels on tick marks! */
1437       char labelbuf[2048];
1438 
1439       /* switch to our font for drawing y axis label and tick labels */
1440       pl_fontname_r (multigrapher->plotter, multigrapher->y_axis.font_name);
1441       pl_ffontsize_r (multigrapher->plotter, SS(multigrapher->y_axis.font_size));
1442 
1443       for (i = multigrapher->y_axis.min_tick_count; i <= multigrapher->y_axis.max_tick_count; i++)
1444 	/* range can be empty */
1445 	{
1446 	  yval = i * multigrapher->y_axis.tick_spacing;
1447 
1448 	  /* discard tick locations outside plotting area */
1449 	  if (yval < multigrapher->y_trans.input_min - FUZZ * yrange
1450 	      || yval > multigrapher->y_trans.input_max + FUZZ * yrange)
1451 	    continue;
1452 
1453 	  /* Plot the ordinate tick labels. */
1454 	  if (!multigrapher->x_axis.switch_axis_end
1455 	      && !(multigrapher->grid_spec == AXES_AT_ORIGIN
1456 		   /* don't plot label if it could run into an axis */
1457 		   && NEAR_EQUALITY (yval, multigrapher->y_axis.other_axis_loc,
1458 				     multigrapher->y_trans.input_range)
1459 		   && (multigrapher->x_axis.other_axis_loc != multigrapher->x_trans.input_min)
1460 		   && (multigrapher->x_axis.other_axis_loc != multigrapher->x_trans.input_max)))
1461 	    /* print labels to left of left boundary */
1462 	    {
1463 	      double new_width;
1464 
1465 	      pl_fmove_r (multigrapher->plotter,
1466 			  XN (multigrapher->x_axis.other_axis_loc)
1467 			  - (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
1468 				* fabs(multigrapher->tick_size))
1469 			     + multigrapher->half_line_width),
1470 			  YV (yval));
1471 	      print_tick_label (labelbuf,
1472 				&multigrapher->y_axis, &multigrapher->y_trans,
1473 				(multigrapher->y_axis.type == A_LOG10) ? pow (10.0, yval) : yval);
1474 	      new_width = pl_flabelwidth_r (multigrapher->plotter, labelbuf);
1475 	      pl_alabel_r (multigrapher->plotter, 'r', 'c', labelbuf);
1476 	      multigrapher->y_axis.max_label_width = DMAX(multigrapher->y_axis.max_label_width, new_width);
1477 	      multigrapher->y_axis.labelled_ticks++;
1478 	    }
1479 	  else
1480 	    /* print labels to right of right boundary */
1481 	    if (multigrapher->x_axis.switch_axis_end
1482 		&& !(multigrapher->grid_spec == AXES_AT_ORIGIN
1483 		     /* don't plot label if it could run into an axis */
1484 		     && NEAR_EQUALITY (yval, multigrapher->y_axis.other_axis_loc,
1485 				       multigrapher->y_trans.input_range)
1486 		     && (multigrapher->x_axis.other_axis_loc != multigrapher->x_trans.input_min)
1487 		     && (multigrapher->x_axis.other_axis_loc != multigrapher->x_trans.input_max)))
1488 	    {
1489 	      double new_width;
1490 
1491 	      pl_fmove_r (multigrapher->plotter,
1492 			  XN (multigrapher->x_axis.alt_other_axis_loc)
1493 			  + (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
1494 				* fabs(multigrapher->tick_size))
1495 			     + multigrapher->half_line_width),
1496 			  YV (yval));
1497 	      print_tick_label (labelbuf,
1498 				&multigrapher->y_axis, &multigrapher->y_trans,
1499 				(multigrapher->y_axis.type == A_LOG10) ? pow (10.0, yval) : yval);
1500 	      new_width = pl_flabelwidth_r (multigrapher->plotter, labelbuf);
1501 	      pl_alabel_r (multigrapher->plotter, 'l', 'c', labelbuf);
1502 	      multigrapher->y_axis.max_label_width = DMAX(multigrapher->y_axis.max_label_width, new_width);
1503 	      multigrapher->y_axis.labelled_ticks++;
1504 	    }
1505 
1506 	  /* Plot the tick marks on the y-axis, and horizontal grid lines. */
1507 	  switch (multigrapher->grid_spec)
1508 	    {
1509 	    case AXES_AND_BOX_AND_GRID:
1510 	      pl_linemod_r (multigrapher->plotter, "dotted");
1511 	      pl_fmove_r (multigrapher->plotter, XP(XSQ(0.0)), YV (yval));
1512 	      pl_fcont_r (multigrapher->plotter, XP(XSQ(1.0)), YV (yval));
1513 	      pl_linemod_r (multigrapher->plotter, "solid");
1514 	      /* fall through */
1515 	    case AXES_AND_BOX:
1516 	      if (!multigrapher->x_axis.switch_axis_end)
1517 		{
1518 		  pl_fmove_r (multigrapher->plotter,
1519 			      XN (multigrapher->x_axis.alt_other_axis_loc),
1520 			      YV (yval));
1521 		  pl_fcont_r (multigrapher->plotter,
1522 			      XN (multigrapher->x_axis.alt_other_axis_loc)
1523 			      - (SS (multigrapher->tick_size)
1524 				 + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
1525 				    : -multigrapher->half_line_width)),
1526 			      YV (yval));
1527 		}
1528 	      else
1529 		{
1530 		  pl_fmove_r (multigrapher->plotter,
1531 			      XN (multigrapher->x_axis.other_axis_loc),
1532 			      YV (yval));
1533 		  pl_fcont_r (multigrapher->plotter,
1534 			      XN (multigrapher->x_axis.other_axis_loc)
1535 			      + (SS (multigrapher->tick_size)
1536 				 + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
1537 				    : -multigrapher->half_line_width)),
1538 			      YV (yval));
1539 		}
1540 	      /* fall through */
1541 	    case AXES:
1542 	    case AXES_AT_ORIGIN:
1543 	      if (!multigrapher->x_axis.switch_axis_end)
1544 		{
1545 		  pl_fmove_r (multigrapher->plotter,
1546 			      XN (multigrapher->x_axis.other_axis_loc),
1547 			      YV (yval));
1548 		  pl_fcont_r (multigrapher->plotter,
1549 			      XN (multigrapher->x_axis.other_axis_loc)
1550 			      + (SS (multigrapher->tick_size)
1551 				 + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
1552 				    : -multigrapher->half_line_width)),
1553 			      YV (yval));
1554 		}
1555 	      else
1556 		{
1557 		  pl_fmove_r (multigrapher->plotter,
1558 			      XN (multigrapher->x_axis.alt_other_axis_loc),
1559 			      YV (yval));
1560 		  pl_fcont_r (multigrapher->plotter,
1561 			      XN (multigrapher->x_axis.alt_other_axis_loc)
1562 			      - (SS (multigrapher->tick_size)
1563 				 + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
1564 				    : -multigrapher->half_line_width)),
1565 			      YV (yval));
1566 		}
1567 	      break;
1568 	    default:		/* shouldn't happen */
1569 	      break;
1570 	    }
1571 	}
1572 
1573       if (multigrapher->y_axis.have_lin_subticks)
1574 	{
1575 	  double subtick_size;	/* libplot coordinates */
1576 
1577 	  /* linearly spaced subticks on a log axis are as long as regular ticks */
1578 	  subtick_size = (multigrapher->y_axis.type == A_LOG10
1579 			  ? SS(multigrapher->tick_size) : SS(multigrapher->subtick_size));
1580 
1581 	  /* Plot the linearly spaced subtick marks on the ordinate */
1582 	  for (i = multigrapher->y_axis.min_lin_subtick_count; i <= multigrapher->y_axis.max_lin_subtick_count; i++)
1583 	    /* range can be empty */
1584 	    {
1585 	      yval = i * multigrapher->y_axis.lin_subtick_spacing;
1586 
1587 	      /* discard subtick locations outside plotting area */
1588 	      if (yval < multigrapher->y_trans.input_min - FUZZ * yrange
1589 		  || yval > multigrapher->y_trans.input_max + FUZZ * yrange)
1590 		continue;
1591 
1592 	      /* Plot the tick marks on the y-axis, and horizontal grid lines. */
1593 	      switch (multigrapher->grid_spec)
1594 		{
1595 		case AXES_AND_BOX_AND_GRID:
1596 		case AXES_AND_BOX:
1597 		  if (!multigrapher->x_axis.switch_axis_end)
1598 		    {
1599 		      pl_fmove_r (multigrapher->plotter,
1600 				  XN (multigrapher->x_axis.alt_other_axis_loc),
1601 				  YV (yval));
1602 		      pl_fcont_r (multigrapher->plotter,
1603 				  XN (multigrapher->x_axis.alt_other_axis_loc)
1604 				  - (subtick_size
1605 				     + (subtick_size > 0.0 ? multigrapher->half_line_width
1606 					: -multigrapher->half_line_width)),
1607 				  YV (yval));
1608 		    }
1609 		  else
1610 		    {
1611 		      pl_fmove_r (multigrapher->plotter,
1612 				  XN (multigrapher->x_axis.other_axis_loc),
1613 				  YV (yval));
1614 		      pl_fcont_r (multigrapher->plotter,
1615 				  XN (multigrapher->x_axis.other_axis_loc)
1616 				  + (subtick_size
1617 				     + (subtick_size > 0.0 ? multigrapher->half_line_width
1618 					: -multigrapher->half_line_width)),
1619 				  YV (yval));
1620 		    }
1621 		  /* fall through */
1622 		case AXES:
1623 		case AXES_AT_ORIGIN:
1624 		  if (!multigrapher->x_axis.switch_axis_end)
1625 		    {
1626 		      pl_fmove_r (multigrapher->plotter,
1627 				  XN (multigrapher->x_axis.other_axis_loc),
1628 				  YV (yval));
1629 		      pl_fcont_r (multigrapher->plotter,
1630 				  XN (multigrapher->x_axis.other_axis_loc)
1631 				  + (subtick_size
1632 				     + (subtick_size > 0.0 ? multigrapher->half_line_width
1633 					: -multigrapher->half_line_width)),
1634 				  YV (yval));
1635 		    }
1636 		  else
1637 		    {
1638 		      pl_fmove_r (multigrapher->plotter,
1639 				  XN (multigrapher->x_axis.alt_other_axis_loc),
1640 				  YV (yval));
1641 		      pl_fcont_r (multigrapher->plotter,
1642 				  XN (multigrapher->x_axis.alt_other_axis_loc)
1643 				  - (subtick_size
1644 				     + (subtick_size > 0.0 ? multigrapher->half_line_width
1645 					: -multigrapher->half_line_width)),
1646 				  YV (yval));
1647 		    }
1648 		  break;
1649 		default:		/* shouldn't happen */
1650 		  break;
1651 		}
1652 	    }
1653 	}
1654 
1655       /* plot a horizontal dotted line at y = 0 */
1656       if (multigrapher->grid_spec != AXES_AT_ORIGIN
1657 	  && multigrapher->y_axis.type == A_LINEAR
1658 	  && multigrapher->y_trans.input_min * multigrapher->y_trans.input_max < 0.0)
1659 	{
1660 	  pl_linemod_r (multigrapher->plotter, "dotted");
1661 	  pl_fline_r (multigrapher->plotter,
1662 		      XP(XSQ(0.0)), YV(0.0), XP(XSQ(1.0)), YV(0.0));
1663 	  pl_linemod_r (multigrapher->plotter, "solid");
1664 	}
1665     }
1666 
1667   /* 5.  DRAW LOGARITHMIC SUBSUBTICKS AND THEIR LABELS ON ABSCISSA */
1668 
1669   /* first, draw normal logarithmic subsubticks if any */
1670   if (multigrapher->grid_spec != NO_AXES && multigrapher->x_axis.have_normal_subsubticks
1671       && !multigrapher->x_axis.user_specified_subsubticks && !multigrapher->x_axis.omit_ticks)
1672     {
1673       int i, m, imin, imax;
1674       double xval, xrange = multigrapher->x_trans.input_max - multigrapher->x_trans.input_min;
1675 
1676       /* compute an integer range (of powers of 10) large enough to include
1677 	 the entire desired axis */
1678       imin = (int)(floor (multigrapher->x_trans.input_min - FUZZ * xrange));
1679       imax = (int)(ceil (multigrapher->x_trans.input_max + FUZZ * xrange));
1680 
1681       for (i = imin; i < imax; i++)
1682 	{
1683 	  for (m = 1; m <= 9 ; m++)
1684 	    {
1685 	      xval = i + log10 ((double)m);
1686 
1687 	      /* Plot subsubtick and label, if desired. */
1688 	      /* N.B. if tick is outside axis range, nothing will be printed */
1689 	      plot_abscissa_log_subsubtick (multigrapher, xval);
1690 	    }
1691 	}
1692     }
1693 
1694   /* second, draw user-specified logarithmic subsubticks instead, if any */
1695   if (multigrapher->grid_spec != NO_AXES && multigrapher->x_axis.user_specified_subsubticks
1696       && !multigrapher->x_axis.omit_ticks)
1697     {
1698       int i, imin, imax;
1699       double xval, xrange = multigrapher->x_trans.input_max - multigrapher->x_trans.input_min;
1700 
1701       /* compute an integer range large enough to include the entire
1702 	 desired axis */
1703       imin = (int)(floor (pow (10.0, multigrapher->x_trans.input_min - FUZZ * xrange)
1704 			  / multigrapher->x_axis.subsubtick_spacing));
1705       imax = (int)(ceil (pow (10.0, multigrapher->x_trans.input_max + FUZZ * xrange)
1706 			 / multigrapher->x_axis.subsubtick_spacing));
1707 
1708       /* draw user-specified subsubticks */
1709       for (i = imin; i <= imax; i++)
1710 	{
1711 	  xval = log10 (i * multigrapher->x_axis.subsubtick_spacing);
1712 
1713 	  /* Plot subsubtick and label, if desired. */
1714 	  /* N.B. if tick is outside axis range, nothing will be printed */
1715 	  plot_abscissa_log_subsubtick (multigrapher, xval);
1716 	}
1717     }
1718 
1719   /* 6.  DRAW LOGARITHMIC SUBSUBTICKS AND THEIR LABELS ON ORDINATE */
1720 
1721   /* first, draw normal logarithmic subsubticks if any */
1722   if (multigrapher->grid_spec != NO_AXES && multigrapher->y_axis.have_normal_subsubticks
1723       && !multigrapher->y_axis.user_specified_subsubticks && !multigrapher->y_axis.omit_ticks)
1724     {
1725       int i, m, imin, imax;
1726       double yval, yrange = multigrapher->y_trans.input_max - multigrapher->y_trans.input_min;
1727 
1728       /* compute an integer range (of powers of 10) large enough to include
1729 	 the entire desired axis */
1730       imin = (int)(floor (multigrapher->y_trans.input_min - FUZZ * yrange));
1731       imax = (int)(ceil (multigrapher->y_trans.input_max + FUZZ * yrange));
1732 
1733       /* draw normal subticks */
1734       for (i = imin; i < imax; i++)
1735 	{
1736 	  for (m = 1; m <= 9; m++)
1737 	    {
1738 	      yval = i + log10 ((double)m);
1739 
1740 	      /* Plot subsubtick and label, if desired. */
1741 	      /* N.B. if tick is outside axis range, nothing will be printed */
1742 	      plot_ordinate_log_subsubtick (multigrapher, yval);
1743 	    }
1744 	}
1745     }
1746 
1747   /* second, draw user-specified logarithmic subsubticks instead, if any */
1748   if (multigrapher->grid_spec != NO_AXES && multigrapher->y_axis.user_specified_subsubticks
1749       && !multigrapher->y_axis.omit_ticks)
1750     {
1751       int i, imin, imax;
1752       double yval, yrange = multigrapher->y_trans.input_max - multigrapher->y_trans.input_min;
1753 
1754       /* compute an integer range large enough to include the entire
1755 	 desired axis */
1756       imin = (int)(floor (pow (10.0, multigrapher->y_trans.input_min - FUZZ * yrange)
1757 			  / multigrapher->y_axis.subsubtick_spacing));
1758       imax = (int)(ceil (pow (10.0, multigrapher->y_trans.input_max + FUZZ * yrange)
1759 			 / multigrapher->y_axis.subsubtick_spacing));
1760 
1761       /* draw user-specified subsubticks */
1762       for (i = imin; i <= imax; i++)
1763 	{
1764 	  yval = log10 (i * multigrapher->y_axis.subsubtick_spacing);
1765 
1766 	  /* Plot subsubtick and label, if desired. */
1767 	  /* N.B. if tick is outside axis range, nothing will be printed */
1768 	  plot_ordinate_log_subsubtick (multigrapher, yval);
1769 	}
1770     }
1771 
1772   /* 7.  DRAW THE ABSCISSA LABEL */
1773 
1774       if ((multigrapher->grid_spec != NO_AXES)
1775       && multigrapher->x_axis.label != NULL && multigrapher->x_axis.label != '\0')
1776     {
1777       double x_axis_font_size;
1778       double xloc;
1779 
1780       /* switch to our font for drawing x axis label and tick labels */
1781       pl_fontname_r (multigrapher->plotter, multigrapher->x_axis.font_name);
1782       x_axis_font_size = pl_ffontsize_r (multigrapher->plotter,
1783 					 SS(multigrapher->x_axis.font_size));
1784 
1785       if (multigrapher->grid_spec != AXES_AT_ORIGIN)
1786 	/* center the label on the axis */
1787 	xloc = 0.5 * (multigrapher->x_trans.input_max + multigrapher->x_trans.input_min);
1788       else
1789 	{
1790 	  if ((multigrapher->y_axis.other_axis_loc == multigrapher->y_trans.input_min)
1791 	      || (multigrapher->y_axis.other_axis_loc == multigrapher->y_trans.input_max))
1792 
1793 	    xloc = 0.5 * (multigrapher->x_trans.input_max + multigrapher->x_trans.input_min);
1794 	  else
1795 	    /* center label in the larger of the two halves */
1796 	    xloc =
1797 	      multigrapher->x_trans.input_max-multigrapher->x_axis.other_axis_loc >= multigrapher->x_axis.other_axis_loc-multigrapher->x_trans.input_min ?
1798 		0.5 * (multigrapher->x_trans.input_max + multigrapher->x_axis.other_axis_loc) :
1799 		  0.5 * (multigrapher->x_axis.other_axis_loc + multigrapher->x_trans.input_min);
1800 	}
1801 
1802       if (!multigrapher->y_axis.switch_axis_end) /* axis on bottom, label below it */
1803 	{
1804 	  pl_fmove_r (multigrapher->plotter,
1805 		      XV (xloc),
1806 		      YN (multigrapher->y_axis.other_axis_loc)
1807 		      - (SS ((multigrapher->tick_size >= 0.0 ? 0.875 : 2.125)
1808 			     * fabs(multigrapher->tick_size))
1809 			 + (6 * x_axis_font_size)/5
1810 			 + multigrapher->half_line_width));
1811 	  pl_alabel_r (multigrapher->plotter,
1812 		       'c', 't', multigrapher->x_axis.label);
1813 	}
1814       else			/* axis on top, label above it */
1815 	{
1816 	  pl_fmove_r (multigrapher->plotter,
1817 		      XV (xloc),
1818 		      YN (multigrapher->y_axis.alt_other_axis_loc)
1819 		      + (SS ((multigrapher->tick_size >= 0.0 ? 0.875 : 2.125)
1820 			     * fabs(multigrapher->tick_size))
1821 			 + (6 * x_axis_font_size)/5
1822 			 + multigrapher->half_line_width));
1823 	  pl_alabel_r (multigrapher->plotter,
1824 		       'c', 'b', multigrapher->x_axis.label);
1825 	}
1826     }
1827 
1828   /* 8.  DRAW THE ORDINATE LABEL */
1829 
1830   if ((multigrapher->grid_spec != NO_AXES)
1831       && (multigrapher->y_axis.label != NULL && *(multigrapher->y_axis.label) != '\0'))
1832     {
1833       double y_axis_font_size;
1834       double yloc;
1835 
1836       /* switch to our font for drawing y axis label and tick labels */
1837       pl_fontname_r (multigrapher->plotter, multigrapher->y_axis.font_name);
1838       y_axis_font_size = pl_ffontsize_r (multigrapher->plotter,
1839 					 SS(multigrapher->y_axis.font_size));
1840 
1841       if (multigrapher->grid_spec != AXES_AT_ORIGIN)
1842 	/* center the label on the axis */
1843 	yloc = 0.5 * (multigrapher->y_trans.input_min + multigrapher->y_trans.input_max);
1844       else
1845 	{
1846 	  if ((multigrapher->x_axis.other_axis_loc == multigrapher->x_trans.input_min)
1847 	      || (multigrapher->x_axis.other_axis_loc == multigrapher->x_trans.input_max))
1848 	    yloc = 0.5 * (multigrapher->y_trans.input_min + multigrapher->y_trans.input_max);
1849 	  else
1850 	    /* center label in the larger of the two halves */
1851 	    yloc =
1852 	      multigrapher->y_trans.input_max-multigrapher->y_axis.other_axis_loc >= multigrapher->y_axis.other_axis_loc-multigrapher->y_trans.input_min ?
1853 		0.5 * (multigrapher->y_trans.input_max + multigrapher->y_axis.other_axis_loc) :
1854 		  0.5 * (multigrapher->y_axis.other_axis_loc + multigrapher->y_trans.input_min);
1855 	}
1856 
1857 /* a relic of temps perdus */
1858 #define libplot_has_font_metrics 1
1859 
1860       if (!multigrapher->x_axis.switch_axis_end)
1861 	{
1862 	  pl_fmove_r (multigrapher->plotter,
1863 		      XN (multigrapher->x_axis.other_axis_loc)
1864 		      - (libplot_has_font_metrics ?
1865 			 (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
1866 			     * fabs(multigrapher->tick_size))
1867 			  + 1.15 * multigrapher->y_axis.max_label_width
1868 			  + 0.5 * y_axis_font_size
1869 			  + multigrapher->half_line_width)
1870 			 : (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
1871 			       * fabs(multigrapher->tick_size)) /* backup */
1872 			    + 1.0 * y_axis_font_size
1873 			    + multigrapher->half_line_width)),
1874 		      YV(yloc));
1875 
1876 	  if (libplot_has_font_metrics
1877 	      && !multigrapher->no_rotate_y_label) /* can rotate label */
1878 	    {
1879 	      pl_textangle_r (multigrapher->plotter, 90);
1880 	      pl_alabel_r (multigrapher->plotter,
1881 			   'c', 'x', multigrapher->y_axis.label);
1882 	      pl_textangle_r (multigrapher->plotter, 0);
1883 	    }
1884 	  else
1885 	    /* non-rotated axis label, right justified */
1886 	    pl_alabel_r (multigrapher->plotter,
1887 			 'r', 'c', multigrapher->y_axis.label);
1888 	}
1889       else
1890 	{
1891 	  pl_fmove_r (multigrapher->plotter,
1892 		      XN (multigrapher->x_axis.alt_other_axis_loc)
1893 		      + (libplot_has_font_metrics ?
1894 			 (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
1895 			     * fabs(multigrapher->tick_size))
1896 			  + 1.15 * multigrapher->y_axis.max_label_width
1897 			  + 0.5 * y_axis_font_size
1898 			  + multigrapher->half_line_width)
1899 			 : (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
1900 			       * fabs(multigrapher->tick_size)) /* backup */
1901 			    + 1.0 * y_axis_font_size
1902 			    + multigrapher->half_line_width)),
1903 		      YV(yloc));
1904 
1905 	  if (libplot_has_font_metrics
1906 	      && !multigrapher->no_rotate_y_label) /* can rotate label */
1907 	    {
1908 	      pl_textangle_r (multigrapher->plotter, 90);
1909 	      pl_alabel_r (multigrapher->plotter,
1910 			   'c', 't', multigrapher->y_axis.label);
1911 	      pl_textangle_r (multigrapher->plotter, 0);
1912 	    }
1913 	  else
1914 	    /* non-rotated axis label, left justified */
1915 	    pl_alabel_r (multigrapher->plotter,
1916 			 'l', 'c', multigrapher->y_axis.label);
1917 	}
1918     }
1919 
1920   /* END OF TASKS */
1921 
1922   /* flush frame to device */
1923   pl_flushpl_r (multigrapher->plotter);
1924 
1925   pl_restorestate_r (multigrapher->plotter);
1926 
1927   if (multigrapher->grid_spec != NO_AXES)
1928     {
1929       if (!tick_warning_printed &&
1930 	  ((!multigrapher->x_axis.omit_ticks && multigrapher->x_axis.labelled_ticks <= 2)
1931 	   || (!multigrapher->y_axis.omit_ticks && multigrapher->y_axis.labelled_ticks <= 2)))
1932 	{
1933 	  fprintf (stderr,
1934 		   "%s: too few labelled axis ticks, adjust tick spacing manually\n",
1935 		   progname);
1936 	  tick_warning_printed = true;
1937 	}
1938     }
1939 }
1940 
1941 /* plot_abscissa_log_subsubtick() and plot_ordinate_log_subsubtick() are
1942    called to plot both normal log subticks and special (user-requested)
1943    ones */
1944 
1945 static void
plot_abscissa_log_subsubtick(Multigrapher * multigrapher,double xval)1946 plot_abscissa_log_subsubtick (Multigrapher *multigrapher, double xval)
1947 {
1948   double xrange = multigrapher->x_trans.input_max - multigrapher->x_trans.input_min;
1949   /* there is no way you could use longer labels on tick marks! */
1950   char labelbuf[2048];
1951   double tick_size = SS(multigrapher->tick_size); /* for positioning labels */
1952   double subsubtick_size = SS(multigrapher->subtick_size);
1953 
1954   /* switch to our font for drawing x axis label and tick labels */
1955   pl_fontname_r (multigrapher->plotter, multigrapher->x_axis.font_name);
1956   pl_ffontsize_r (multigrapher->plotter, SS(multigrapher->x_axis.font_size));
1957 
1958   /* discard subsubtick locations outside plotting area */
1959   if (xval < multigrapher->x_trans.input_min - FUZZ * xrange
1960       || xval > multigrapher->x_trans.input_max + FUZZ * xrange)
1961     return;
1962 
1963   /* label subsubtick if it seems appropriate */
1964   if (multigrapher->x_axis.user_specified_subsubticks)
1965     {
1966       print_tick_label (labelbuf,
1967 			&multigrapher->x_axis, &multigrapher->x_trans,
1968 			pow (10.0, xval));
1969       if (!multigrapher->y_axis.switch_axis_end)
1970 	{
1971 	  pl_fmove_r (multigrapher->plotter,
1972 		      XV (xval),
1973 		      YN (multigrapher->y_axis.other_axis_loc)
1974 		      - ((tick_size >= 0 ? 0.75 : 1.75)
1975 			 * fabs((double)tick_size)
1976 			 + multigrapher->half_line_width));
1977 	  pl_alabel_r (multigrapher->plotter, 'c', 't', labelbuf);
1978 	  multigrapher->x_axis.labelled_ticks++;
1979 	}
1980       else
1981 	{
1982 	  pl_fmove_r (multigrapher->plotter,
1983 		      XV (xval),
1984 		      YN (multigrapher->y_axis.alt_other_axis_loc)
1985 		      + ((tick_size >= 0 ? 0.75 : 1.75)
1986 			 * fabs((double)tick_size)
1987 			 + multigrapher->half_line_width));
1988 	  pl_alabel_r (multigrapher->plotter, 'c', 'b', labelbuf);
1989 	  multigrapher->x_axis.labelled_ticks++;
1990 	}
1991     }
1992 
1993   /* draw subsubtick */
1994   switch (multigrapher->grid_spec)
1995     {
1996     case AXES_AND_BOX_AND_GRID:
1997       pl_linemod_r (multigrapher->plotter, "dotted");
1998       pl_fmove_r (multigrapher->plotter, XV (xval), YP(YSQ(0.0)));
1999       pl_fcont_r (multigrapher->plotter, XV (xval), YP(YSQ(1.0)));
2000       pl_linemod_r (multigrapher->plotter, "solid");
2001       /* fall through */
2002     case AXES_AND_BOX:
2003       if (!multigrapher->y_axis.switch_axis_end)
2004 	{
2005 	  pl_fmove_r (multigrapher->plotter,
2006 		      XV (xval),
2007 		      YN (multigrapher->y_axis.alt_other_axis_loc));
2008 	  pl_fcont_r (multigrapher->plotter,
2009 		      XV (xval),
2010 		      YN (multigrapher->y_axis.alt_other_axis_loc)
2011 		      - (subsubtick_size
2012 			 + (subsubtick_size > 0.0
2013 			    ? multigrapher->half_line_width
2014 			    : -multigrapher->half_line_width)));
2015 	}
2016       else
2017 	{
2018 	  pl_fmove_r (multigrapher->plotter,
2019 		      XV (xval),
2020 		      YN (multigrapher->y_axis.other_axis_loc));
2021 	  pl_fcont_r (multigrapher->plotter,
2022 		      XV (xval),
2023 		      YN (multigrapher->y_axis.other_axis_loc)
2024 		      + (subsubtick_size
2025 			 + (subsubtick_size > 0.0
2026 			    ? multigrapher->half_line_width
2027 			    : -multigrapher->half_line_width)));
2028 	}
2029       /* fall through */
2030     case AXES:
2031     case AXES_AT_ORIGIN:
2032       if (!multigrapher->y_axis.switch_axis_end)
2033 	{
2034 	  pl_fmove_r (multigrapher->plotter,
2035 		      XV (xval),
2036 		      YN (multigrapher->y_axis.other_axis_loc));
2037 	  pl_fcont_r (multigrapher->plotter,
2038 		      XV (xval),
2039 		      YN (multigrapher->y_axis.other_axis_loc)
2040 		      + (subsubtick_size
2041 			 + (subsubtick_size > 0.0
2042 			    ? multigrapher->half_line_width
2043 			    : -multigrapher->half_line_width)));
2044 	}
2045       else
2046 	{
2047 	  pl_fmove_r (multigrapher->plotter,
2048 		      XV (xval),
2049 		      YN (multigrapher->y_axis.alt_other_axis_loc));
2050 	  pl_fcont_r (multigrapher->plotter,
2051 		      XV (xval),
2052 		      YN (multigrapher->y_axis.alt_other_axis_loc)
2053 		      - (subsubtick_size
2054 			 + (subsubtick_size > 0.0
2055 			    ? multigrapher->half_line_width
2056 			    : -multigrapher->half_line_width)));
2057 	}
2058       break;
2059     default:			/* shouldn't happen */
2060       break;
2061     }
2062 }
2063 
2064 static void
plot_ordinate_log_subsubtick(Multigrapher * multigrapher,double yval)2065 plot_ordinate_log_subsubtick (Multigrapher *multigrapher, double yval)
2066 {
2067   double yrange = multigrapher->y_trans.input_max - multigrapher->y_trans.input_min;
2068   /* there is no way you could use longer labels on tick marks! */
2069   char labelbuf[2048];
2070   double tick_size = SS(multigrapher->tick_size); /* for positioning labels */
2071   double subsubtick_size = SS(multigrapher->subtick_size);
2072 
2073   /* switch to our font for drawing y axis label and tick labels */
2074   pl_fontname_r (multigrapher->plotter, multigrapher->y_axis.font_name);
2075   pl_ffontsize_r (multigrapher->plotter, SS(multigrapher->y_axis.font_size));
2076 
2077   /* discard subsubtick locations outside plotting area */
2078   if (yval < multigrapher->y_trans.input_min - FUZZ * yrange
2079       || yval > multigrapher->y_trans.input_max + FUZZ * yrange)
2080     return;
2081 
2082   /* label subsubtick if it seems appropriate */
2083   if (multigrapher->y_axis.user_specified_subsubticks)
2084     {
2085       double new_width;
2086 
2087       print_tick_label (labelbuf,
2088 			&multigrapher->y_axis, &multigrapher->y_trans,
2089 			pow (10.0, yval));
2090       if (!multigrapher->x_axis.switch_axis_end)
2091 	{
2092 	  pl_fmove_r (multigrapher->plotter,
2093 		      XN(multigrapher->x_axis.other_axis_loc)
2094 		      - ((tick_size >= 0 ? 0.75 : 1.75)
2095 			 * fabs((double)tick_size)
2096 			 + multigrapher->half_line_width),
2097 		      YV (yval));
2098 	  new_width = pl_flabelwidth_r (multigrapher->plotter, labelbuf);
2099 	  pl_alabel_r (multigrapher->plotter, 'r', 'c', labelbuf);
2100 	  multigrapher->y_axis.max_label_width = DMAX(multigrapher->y_axis.max_label_width, new_width);
2101 	  multigrapher->y_axis.labelled_ticks++;
2102 	}
2103       else
2104 	{
2105 	  pl_fmove_r (multigrapher->plotter,
2106 		      XN(multigrapher->x_axis.alt_other_axis_loc)
2107 		      + ((tick_size >= 0 ? 0.75 : 1.75)
2108 			 * fabs((double)tick_size)
2109 			 + multigrapher->half_line_width),
2110 		      YV (yval));
2111 	  new_width = pl_flabelwidth_r (multigrapher->plotter, labelbuf);
2112 	  pl_alabel_r (multigrapher->plotter, 'l', 'c', labelbuf);
2113 	  multigrapher->y_axis.max_label_width = DMAX(multigrapher->y_axis.max_label_width, new_width);
2114 	  multigrapher->y_axis.labelled_ticks++;
2115 	}
2116     }
2117 
2118   /* draw subsubtick */
2119   switch (multigrapher->grid_spec)
2120     {
2121     case AXES_AND_BOX_AND_GRID:
2122       pl_linemod_r (multigrapher->plotter, "dotted");
2123       pl_fmove_r (multigrapher->plotter, XP(XSQ(0.0)), YV (yval));
2124       pl_fcont_r (multigrapher->plotter, XP(XSQ(1.0)), YV (yval));
2125       pl_linemod_r (multigrapher->plotter, "solid");
2126       /* fall through */
2127     case AXES_AND_BOX:
2128       if (!multigrapher->x_axis.switch_axis_end)
2129 	{
2130 	  pl_fmove_r (multigrapher->plotter,
2131 		      XN (multigrapher->x_axis.alt_other_axis_loc),
2132 		      YV (yval));
2133 	  pl_fcont_r (multigrapher->plotter,
2134 		      XN (multigrapher->x_axis.alt_other_axis_loc)
2135 		      - (subsubtick_size
2136 			 + (subsubtick_size > 0.0
2137 			    ? multigrapher->half_line_width
2138 			    : -multigrapher->half_line_width)),
2139 		      YV (yval));
2140 	}
2141       else
2142 	{
2143 	  pl_fmove_r (multigrapher->plotter,
2144 		      XN (multigrapher->x_axis.other_axis_loc),
2145 		      YV (yval));
2146 	  pl_fcont_r (multigrapher->plotter,
2147 		      XN (multigrapher->x_axis.other_axis_loc)
2148 		      + (subsubtick_size
2149 			 + (subsubtick_size > 0.0
2150 			    ? multigrapher->half_line_width
2151 			    : -multigrapher->half_line_width)),
2152 		      YV (yval));
2153 	}
2154       /* fall through */
2155     case AXES:
2156     case AXES_AT_ORIGIN:
2157       if (!multigrapher->x_axis.switch_axis_end)
2158 	{
2159 	  pl_fmove_r (multigrapher->plotter,
2160 		      XN (multigrapher->x_axis.other_axis_loc),
2161 		      YV (yval));
2162 	  pl_fcont_r (multigrapher->plotter,
2163 		      XN (multigrapher->x_axis.other_axis_loc)
2164 		      + (subsubtick_size
2165 			 + (subsubtick_size > 0.0
2166 			    ? multigrapher->half_line_width
2167 			    : -multigrapher->half_line_width)),
2168 		      YV (yval));
2169 	}
2170       else
2171 	{
2172 	  pl_fmove_r (multigrapher->plotter,
2173 		      XN (multigrapher->x_axis.alt_other_axis_loc),
2174 		      YV (yval));
2175 	  pl_fcont_r (multigrapher->plotter,
2176 		      XN (multigrapher->x_axis.alt_other_axis_loc)
2177 		      - (subsubtick_size
2178 			 + (multigrapher->tick_size > 0.0
2179 			    ? multigrapher->half_line_width
2180 			    : -multigrapher->half_line_width)),
2181 		      YV (yval));
2182 	}
2183       break;
2184     default:	/* shouldn't happen */
2185       break;
2186     }
2187 }
2188 
2189 /* set_line_style() maps from line modes to physical line modes.  See
2190  * explanation at head of file. */
2191 
2192 static void
set_line_style(Multigrapher * multigrapher,int style,bool use_color)2193 set_line_style (Multigrapher *multigrapher, int style, bool use_color)
2194 {
2195   if (!use_color)		/* monochrome */
2196     {
2197       if (style > 0)
2198 	/* don't issue pl_linemod_r() if style<=0, since no polyline will
2199            be drawn */
2200 	{
2201 	  int i;
2202 
2203 	  i = (style - 1) % NO_OF_LINEMODES;
2204 	  pl_linemod_r (multigrapher->plotter, linemodes[i]);
2205 	}
2206 
2207       /* use same color as used for plot frame */
2208       pl_colorname_r (multigrapher->plotter, multigrapher->frame_color);
2209     }
2210   else				/* color */
2211     {
2212       int i, j;
2213 
2214       if (style > 0)		/* solid lines, various colors */
2215 	{
2216 	  i = ((style - 1) / NO_OF_LINEMODES) % NO_OF_LINEMODES;
2217 	  j = (style - 1) % NO_OF_LINEMODES;
2218 	  pl_linemod_r (multigrapher->plotter, linemodes[i]);
2219 	}
2220 
2221       else if (style == 0)	/* use first color, as if -m 1 was spec'd */
2222 				/* (no line will be drawn) */
2223 	j = 0;
2224 
2225       else			/* neg. pl_linemode_r (no line will be drawn)*/
2226 	j = (-style - 1) % (NO_OF_LINEMODES - 1);
2227 
2228       pl_colorname_r (multigrapher->plotter, colorstyle[j]);
2229 #if 0
2230 	fprintf(stderr, "set_line_style() : color %s\n", colorstyle[j]);
2231 #endif
2232     }
2233 }
2234 
2235 
2236 /* plot_point() plots a single point, including the appropriate symbol and
2237  * errorbar(s) if any.  It may call either pl_fcont_r() or pl_fmove_r(),
2238  * depending on whether the pendown flag is set or not.  Gnuplot-style
2239  * clipping (clip mode = 0,1,2) is supported.
2240  *
2241  * plot_point() makes heavy use of the multigrapher->x_trans and
2242  * multigrapher->y_trans structures, which specify the linear
2243  * transformation from user coordinates to device coordinates.  It also
2244  * updates the multigrapher's internal state variables.  */
2245 
2246 void
plot_point(Multigrapher * multigrapher,const Point * point)2247 plot_point (Multigrapher *multigrapher, const Point *point)
2248 {
2249   double local_x0, local_y0, local_x1, local_y1;
2250   int clipval;
2251 
2252   /* If new polyline is beginning, take its line style, color/monochrome
2253      attribute, and line width and fill fraction attributes from the first
2254      point of the polyline.  We assume all such attribute fields are the
2255      same for all points in the polyline (our point reader arranges this
2256      for us). */
2257   if (!(point->pendown) || multigrapher->first_point_of_polyline)
2258     {
2259       int intfill;
2260 
2261       set_line_style (multigrapher, point->linemode, point->use_color);
2262 
2263       /* N.B. linewidth < 0.0 means use libplot default */
2264       pl_flinewidth_r (multigrapher->plotter,
2265 		       point->line_width * (double)PLOT_SIZE);
2266 
2267       if (point->fill_fraction < 0.0)
2268 	intfill = 0;		/* transparent */
2269       else			/* guaranteed to be <= 1.0 */
2270 	intfill = 1 + IROUND((1.0 - point->fill_fraction) * 0xfffe);
2271       pl_filltype_r (multigrapher->plotter, intfill);
2272     }
2273 
2274   /* determine endpoints of new line segment (for the first point of a
2275      polyline, use a zero-length line segment) */
2276   if (multigrapher->first_point_of_polyline)
2277     {
2278       local_x0 = point->x;
2279       local_y0 = point->y;
2280     }
2281   else
2282     {
2283       local_x0 = multigrapher->oldpoint_x;
2284       local_y0 = multigrapher->oldpoint_y;
2285     }
2286   local_x1 = point->x;
2287   local_y1 = point->y;
2288 
2289   /* save current point for use as endpoint of next line segment */
2290   multigrapher->oldpoint_x = point->x;
2291   multigrapher->oldpoint_y = point->y;
2292 
2293   /* apply Cohen-Sutherland clipper to new line segment */
2294   clipval = clip_line (multigrapher,
2295 		       &local_x0, &local_y0, &local_x1, &local_y1);
2296 
2297   if (!(clipval & ACCEPTED))	/* rejected in toto */
2298     {
2299       pl_fmove_r (multigrapher->plotter,
2300 		  XV (point->x), YV (point->y)); /* move with pen up */
2301       multigrapher->first_point_of_polyline = false;
2302       return;
2303     }
2304 
2305   /* not rejected, ideally move with pen down */
2306   if (point->pendown && (point->linemode > 0))
2307     {
2308       switch (multigrapher->clip_mode) /* gnuplot style clipping (0,1, or 2) */
2309 	{
2310 	case 0:
2311 	  if ((clipval & CLIPPED_FIRST) || (clipval & CLIPPED_SECOND))
2312 	    /* clipped on at least one end, so move with pen up */
2313 	    pl_fmove_r (multigrapher->plotter, XV (point->x), YV (point->y));
2314 	  else
2315 	    /* line segment within box, so move with pen down */
2316 	    {
2317 	      if (!multigrapher->first_point_of_polyline)
2318 		pl_fcont_r (multigrapher->plotter,
2319 			    XV (point->x), YV (point->y));
2320 	      else
2321 		pl_fmove_r (multigrapher->plotter,
2322 			    XV (point->x), YV (point->y));
2323 	    }
2324 	  break;
2325 	case 1:
2326 	default:
2327 	  if ((clipval & CLIPPED_FIRST) && (clipval & CLIPPED_SECOND))
2328 	    /* both OOB, so move with pen up */
2329 	    pl_fmove_r (multigrapher->plotter, XV (point->x), YV (point->y));
2330 	  else
2331 	    /* at most one point is OOB */
2332 	    {
2333 	      if (clipval & CLIPPED_FIRST) /*current pt. OOB, new pt. not OOB*/
2334 		{
2335 		  if (!multigrapher->first_point_of_polyline)
2336 		    {
2337 		      /* move to clipped current point, draw line segment */
2338 		      pl_fmove_r (multigrapher->plotter,
2339 				  XV (local_x0), YV (local_y0));
2340 		      pl_fcont_r (multigrapher->plotter,
2341 				  XV (point->x), YV (point->y));
2342 		    }
2343 		  else
2344 		    pl_fmove_r (multigrapher->plotter,
2345 				XV (point->x), YV (point->y));
2346 		}
2347 	      else		/* current point not OOB, new point OOB */
2348 		{
2349 		  if (!multigrapher->first_point_of_polyline)
2350 		    {
2351 		      /* draw line segment to clipped new point */
2352 		      pl_fcont_r (multigrapher->plotter,
2353 				  XV (local_x1), YV (local_y1));
2354 		      /* N.B. lib's notion of position now differs from ours */
2355 		    }
2356 		  else
2357 		    pl_fmove_r (multigrapher->plotter,
2358 				XV (point->x), YV (point->y));
2359 		}
2360 	    }
2361 	  break;
2362 	case 2:
2363 	  if ((clipval & CLIPPED_FIRST) || multigrapher->first_point_of_polyline)
2364 	    /* move to clipped current point if necc. */
2365 	    pl_fmove_r (multigrapher->plotter, XV (local_x0), YV (local_y0));
2366 
2367 	  /* draw line segment to clipped new point */
2368 	  pl_fcont_r (multigrapher->plotter, XV (local_x1), YV (local_y1));
2369 
2370 	  if (clipval & CLIPPED_SECOND)
2371 	    /* new point OOB, so move to new point, breaking polyline */
2372 	    pl_fmove_r (multigrapher->plotter, XV (point->x), YV (point->y));
2373 	  break;
2374 	}
2375     }
2376   else				/* linemode=0 or pen up; so move with pen up */
2377     pl_fmove_r (multigrapher->plotter, XV (point->x), YV (point->y));
2378 
2379   multigrapher->first_point_of_polyline = false;
2380 
2381   /* if target point is OOB, return without plotting symbol or errorbar */
2382   if (clipval & CLIPPED_SECOND)
2383     return;
2384 
2385   /* plot symbol and errorbar, doing a pl_savestate_r()--pl_restorestate()
2386      to keep from breaking the polyline under construction (if any) */
2387   if (point->symbol >= 32)	/* yow, a character */
2388     {
2389       /* will do a font change, so save & restore state */
2390       pl_savestate_r (multigrapher->plotter);
2391       plot_errorbar (multigrapher, point);
2392       pl_fontname_r (multigrapher->plotter, point->symbol_font_name);
2393       pl_fmarker_r (multigrapher->plotter, XV(point->x), YV(point->y),
2394 		    point->symbol, SS(point->symbol_size));
2395       pl_restorestate_r (multigrapher->plotter);
2396     }
2397 
2398   else if (point->symbol > 0)	/* a marker symbol */
2399     {
2400       if (point->linemode > 0)
2401 	/* drawing a line, so (to keep from breaking it) save & restore state*/
2402 	{
2403 	  pl_savestate_r (multigrapher->plotter);
2404 	  plot_errorbar (multigrapher, point); /* may or may not have one */
2405 	  pl_fmarker_r (multigrapher->plotter, XV(point->x), YV(point->y),
2406 			point->symbol, SS(point->symbol_size));
2407 	  pl_restorestate_r (multigrapher->plotter);
2408 	}
2409       else
2410 	/* not drawing a line, so just place the marker */
2411 	{
2412 	  plot_errorbar (multigrapher, point);
2413 	  pl_fmarker_r (multigrapher->plotter, XV(point->x), YV(point->y),
2414 			point->symbol, SS(point->symbol_size));
2415 	}
2416     }
2417 
2418   else if (point->symbol == 0 && point->linemode == 0)
2419     /* backward compatibility: -m 0 (even with -S 0) plots a dot */
2420     {
2421       plot_errorbar (multigrapher, point);
2422       pl_fmarker_r (multigrapher->plotter,
2423 		    XV(point->x), YV(point->y), M_DOT, SS(point->symbol_size));
2424     }
2425 
2426   else				/* no symbol, but may be an errorbar */
2427     plot_errorbar (multigrapher, point);
2428 
2429   return;
2430 }
2431 
2432 /* plot_point_array() calls plot_point() on each point in an array of
2433  * points.
2434  */
2435 
2436 void
plot_point_array(Multigrapher * multigrapher,const Point * p,int length)2437 plot_point_array (Multigrapher *multigrapher, const Point *p, int length)
2438 {
2439   int index;
2440 
2441   for (index = 0; index < length; index++)
2442     plot_point (multigrapher, &(p[index]));
2443 }
2444 
2445 /* clip_line() takes two points, the endpoints of a line segment, and
2446  * destructively passes back two points: the endpoints of the line segment
2447  * clipped by Cohen-Sutherland to the rectangular plotting area.  Return
2448  * value contains bitfields ACCEPTED, CLIPPED_FIRST, and CLIPPED_SECOND.
2449  */
2450 
2451 static int
clip_line(Multigrapher * multigrapher,double * x0_p,double * y0_p,double * x1_p,double * y1_p)2452 clip_line (Multigrapher *multigrapher, double *x0_p, double *y0_p, double *x1_p, double *y1_p)
2453 {
2454   double x0 = *x0_p;
2455   double y0 = *y0_p;
2456   double x1 = *x1_p;
2457   double y1 = *y1_p;
2458   outcode outcode0 = compute_outcode (multigrapher, x0, y0, true);
2459   outcode outcode1 = compute_outcode (multigrapher, x1, y1, true);
2460   bool accepted;
2461   int clipval = 0;
2462 
2463   for ( ; ; )
2464     {
2465       if (!(outcode0 | outcode1)) /* accept */
2466 	{
2467 	  accepted = true;
2468 	  break;
2469 	}
2470       else if (outcode0 & outcode1) /* reject */
2471 	{
2472 	  accepted = false;
2473 	  break;
2474 	}
2475       else
2476 	{
2477 	  /* at least one endpoint is outside; choose one that is */
2478 	  outcode outcode_out = (outcode0 ? outcode0 : outcode1);
2479 	  double x, y;		/* intersection with clip edge */
2480 
2481 	  if (outcode_out & RIGHT)
2482 	    {
2483 	      x = multigrapher->x_trans.input_max;
2484 	      y = y0 + (y1 - y0) * (multigrapher->x_trans.input_max - x0) / (x1 - x0);
2485 	    }
2486 	  else if (outcode_out & LEFT)
2487 	    {
2488 	      x = multigrapher->x_trans.input_min;
2489 	      y = y0 + (y1 - y0) * (multigrapher->x_trans.input_min - x0) / (x1 - x0);
2490 	    }
2491 	  else if (outcode_out & TOP)
2492 	    {
2493 	      x = x0 + (x1 - x0) * (multigrapher->y_trans.input_max - y0) / (y1 - y0);
2494 	      y = multigrapher->y_trans.input_max;
2495 	    }
2496 	  else
2497 	    {
2498 	      x = x0 + (x1 - x0) * (multigrapher->y_trans.input_min - y0) / (y1 - y0);
2499 	      y = multigrapher->y_trans.input_min;
2500 	    }
2501 
2502 	  if (outcode_out == outcode0)
2503 	    {
2504 	      x0 = x;
2505 	      y0 = y;
2506 	      outcode0 = compute_outcode (multigrapher, x0, y0, true);
2507 	    }
2508 	  else
2509 	    {
2510 	      x1 = x;
2511 	      y1 = y;
2512 	      outcode1 = compute_outcode (multigrapher, x1, y1, true);
2513 	    }
2514 	}
2515     }
2516 
2517   if (accepted)
2518     {
2519       clipval |= ACCEPTED;
2520       if ((x0 != *x0_p) || (y0 != *y0_p))
2521 	clipval |= CLIPPED_FIRST;
2522       if ((x1 != *x1_p) || (y1 != *y1_p))
2523 	clipval |= CLIPPED_SECOND;
2524       *x0_p = x0;
2525       *y0_p = y0;
2526       *x1_p = x1;
2527       *y1_p = y1;
2528     }
2529 
2530   return clipval;
2531 }
2532 
2533 /* Compute usual Cohen-Sutherland outcode, containing bitfields LEFT,
2534    RIGHT, BOTTOM, TOP.  Nine possibilities:
2535    {LEFT, interior, RIGHT} x {BOTTOM, interior, TOP}.
2536    The `tolerant' flag specifies how we handle points on the boundary. */
2537 static outcode
compute_outcode(Multigrapher * multigrapher,double x,double y,bool tolerant)2538 compute_outcode (Multigrapher *multigrapher, double x, double y, bool tolerant)
2539 {
2540   outcode code = 0;
2541   double xfuzz = FUZZ * multigrapher->x_trans.input_range;
2542   double yfuzz = FUZZ * multigrapher->y_trans.input_range;
2543   int sign = (tolerant == true ? 1 : -1);
2544 
2545   if (x > multigrapher->x_trans.input_max + sign * xfuzz)
2546     code |= RIGHT;
2547   else if (x < multigrapher->x_trans.input_min - sign * xfuzz)
2548     code |= LEFT;
2549   if (y > multigrapher->y_trans.input_max + sign * yfuzz)
2550     code |= TOP;
2551   else if (y < multigrapher->y_trans.input_min - sign * yfuzz)
2552     code |= BOTTOM;
2553 
2554   return code;
2555 }
2556 
2557 static void
transpose_portmanteau(int * val)2558 transpose_portmanteau (int *val)
2559 {
2560   bool xtrue, ytrue;
2561   int newval;
2562 
2563   xtrue = ((*val & X_AXIS) ? true : false);
2564   ytrue = ((*val & Y_AXIS) ? true : false);
2565 
2566   newval = (xtrue ? Y_AXIS : 0) | (ytrue ? X_AXIS : 0);
2567   *val = newval;
2568 }
2569 
2570 static void
plot_errorbar(Multigrapher * multigrapher,const Point * p)2571 plot_errorbar (Multigrapher *multigrapher, const Point *p)
2572 {
2573   if (p->have_x_errorbar || p->have_y_errorbar)
2574     /* save & restore state, since we invoke pl_linemod_r() */
2575     {
2576       pl_savestate_r (multigrapher->plotter);
2577       pl_linemod_r (multigrapher->plotter, "solid");
2578 
2579       if (p->have_x_errorbar)
2580 	{
2581 	  pl_fline_r (multigrapher->plotter,
2582 		      XV(p->xmin), YV(p->y) - 0.5 * SS(p->symbol_size),
2583 		      XV(p->xmin), YV(p->y) + 0.5 * SS(p->symbol_size));
2584 	  pl_fline_r (multigrapher->plotter,
2585 		      XV(p->xmin), YV(p->y), XV(p->xmax), YV(p->y));
2586 	  pl_fline_r (multigrapher->plotter,
2587 		      XV(p->xmax), YV(p->y) - 0.5 * SS(p->symbol_size),
2588 		      XV(p->xmax), YV(p->y) + 0.5 * SS(p->symbol_size));
2589 	}
2590       if (p->have_y_errorbar)
2591 	{
2592 	  pl_fline_r (multigrapher->plotter,
2593 		      XV(p->x) - 0.5 * SS(p->symbol_size), YV(p->ymin),
2594 		      XV(p->x) + 0.5 * SS(p->symbol_size), YV(p->ymin));
2595 	  pl_fline_r (multigrapher->plotter,
2596 		      XV(p->x), YV(p->ymin), XV(p->x), YV(p->ymax));
2597 	  pl_fline_r (multigrapher->plotter,
2598 		      XV(p->x) - 0.5 * SS(p->symbol_size), YV(p->ymax),
2599 		      XV(p->x) + 0.5 * SS(p->symbol_size), YV(p->ymax));
2600 	}
2601 
2602       pl_restorestate_r (multigrapher->plotter);
2603     }
2604 }
2605 
2606 /* An alternative means of ending a polyline in progress.  Rather than
2607    ending it by passing plot_point() a point with the `pendown' flag not
2608    set, one may call this function.  This yields faster response in
2609    real-time work; e.g. in reader.c it is called by read_and_plot_file()
2610    after all dataset(s) have been read from the file and plotted. */
2611 
2612 void
end_polyline_and_flush(Multigrapher * multigrapher)2613 end_polyline_and_flush (Multigrapher *multigrapher)
2614 {
2615   pl_endpath_r (multigrapher->plotter);
2616   pl_flushpl_r (multigrapher->plotter);
2617   multigrapher->first_point_of_polyline = true;
2618 }
2619 
2620 
2621 /*
2622  * The API
2623  */
2624 void
sp_plot_point(Multigrapher * mg,const Point * point)2625 sp_plot_point(Multigrapher *mg, const Point *point)
2626 {
2627 #if 0
2628 	fprintf(stderr, "sp_plot_point(x %f, y %f, color %d)\n",
2629 		point->x, point->y, point->linemode);
2630 #endif
2631 
2632 	switch (mg->plot_type) {
2633 	case SP_PLOT_XY:
2634 		plot_point(mg, point);
2635 		break;
2636 	case SP_PLOT_BAR:
2637 		sp_bar_plot_point(mg, point);
2638 		break;
2639 	case SP_PLOT_PIE:
2640 		sp_pie_plot_point(mg, point);
2641 		break;
2642 	default: {}
2643 		/* ?? */
2644 	}
2645 }
2646 
2647 void
sp_plot_point_simple(Multigrapher * mg,int connected,double x,double y)2648 sp_plot_point_simple(Multigrapher *mg, int connected, double x, double y)
2649 {
2650 	Point	*p = &mg->point;
2651 
2652 #if 0	/* Danny */
2653 	fprintf(stderr, "sp_plot_point_simple(1, %f, %f)\n", connected, x, y);
2654 #endif
2655 	p->x = x;
2656 	p->y = y;
2657 	p->pendown = connected;
2658 
2659 	p->have_x_errorbar = p->have_y_errorbar = 0;
2660 	p->xmin = p->xmax = 0.0;
2661 	p->ymin = p->ymax = 0.0;
2662 
2663 	p->symbol = mg->symbol;
2664 	p->symbol_size = 0.02;
2665 	p->symbol_font_name = NULL;
2666 	p->line_width = 0.001;
2667 	p->fill_fraction = -1.0;
2668 	p->use_color = 1;
2669 
2670 	p->label = NULL;
2671 
2672 	sp_plot_point(mg, p);
2673 }
2674 
sp_plot_symbol(Multigrapher * mg,int symbol)2675 void sp_plot_symbol(Multigrapher *mg, int symbol)
2676 {
2677 	mg->symbol = symbol;
2678 }
2679 
2680 /*
2681  * You need to have created a plotter with pl_newpl() or pl_newpl_r() first.
2682  */
2683 Multigrapher *
sp_create_plot(plPlotter * plotter,const SpPlotType plot_type)2684 sp_create_plot(plPlotter *plotter, const SpPlotType plot_type)
2685 {
2686 	Multigrapher *multigrapher;
2687 
2688 	if (plotter == (plPlotter *)NULL)
2689 		return (Multigrapher *)NULL;
2690 #if 0
2691 	if (pl_openpl_r(plotter) < 0)
2692 		return (Multigrapher *)NULL;
2693 #endif
2694 
2695 	multigrapher = (Multigrapher *)malloc (sizeof (Multigrapher));
2696 	memset((void *)multigrapher, 0, sizeof(Multigrapher));
2697 
2698 	multigrapher->plot_type = plot_type;
2699 	multigrapher->plotter = plotter;
2700 	multigrapher->bg_color = "white";
2701 
2702 	multigrapher->x_axis.tick_type = SP_TICK_DEFAULT;
2703 	multigrapher->y_axis.tick_type = SP_TICK_DEFAULT;
2704 	multigrapher->symbol = 1;
2705 
2706 	pl_erase_r(plotter);
2707 
2708 	/*
2709 	 * FIX ME
2710 	 * We should use the same coordinates for all plot types.
2711 	 * --> consistency for the user of this library
2712 	 */
2713 	if (plot_type == SP_PLOT_PIE)
2714 		pl_fspace_r(plotter, -(double)PLOT_SIZE, -(double)PLOT_SIZE,
2715 					(double)PLOT_SIZE, (double)PLOT_SIZE);
2716 	else
2717 		pl_fspace_r(plotter, 0.0, 0.0, (double)PLOT_SIZE, (double)PLOT_SIZE);
2718 
2719 	/*
2720 	 * Set some defaults just in case.
2721 	 */
2722 	set_graph_parameters(multigrapher,
2723 		-0.001, "black",	/* Frame line width & colour */
2724 		"This is the title",	/* Title */
2725 		NULL,	0.07,		/* Title font */
2726 		0.02,	AXES_AND_BOX,	/* Tick size, grid spec */
2727 		0.0, 100.0, 0.0,		/* X min, max, spacing */
2728 		0.0, 100.0, 0.0,		/* Y min, max, spacing */
2729 		0,	0,		/* X, Y spacing */
2730 		0.75,	0.75,		/* Plot width, height */
2731 		0.125,	0.125,		/* Margin around plot */
2732 		NULL,	0.025,		/* X Axis label font */
2733 		"X Axis Label",		/* X Axis label */
2734 		NULL,	0.025,		/* Y Axis label font */
2735 		"Y Axis Label",		/* Y Axis label */
2736 		0,
2737 		0,			/* Log */
2738 		0,			/* Round */
2739 		0,
2740 		0,
2741 		1,
2742 		1.3,
2743 		0);
2744 
2745 	return multigrapher;
2746 }
2747 
2748 void
sp_destroy_plot(Multigrapher * mg)2749 sp_destroy_plot(Multigrapher *mg)
2750 {
2751 /*	(void) delete_multigrapher(mg);	*/
2752 	free(mg);
2753 }
2754 
2755 void
sp_begin_plot(Multigrapher * mg,double scale,double trans_x,double trans_y)2756 sp_begin_plot(Multigrapher *mg, double scale, double trans_x, double trans_y)
2757 {
2758 	switch (mg->plot_type) {
2759 	case SP_PLOT_XY:
2760 		sp_xy_begin_graph(mg, scale, trans_x, trans_y);
2761 
2762 		if (mg->transpose_axes)
2763 			transpose(mg);
2764 		break;
2765 
2766 	case SP_PLOT_BAR:
2767 		sp_bar_begin_graph(mg, scale, trans_x, trans_y);
2768 		break;
2769 
2770 	case SP_PLOT_PIE:
2771 		sp_pie_begin_graph(mg, scale, trans_x, trans_y);
2772 		break;
2773 	default: {}
2774 		/* ??? */
2775 	}
2776 
2777 	sp_first_dataset(mg);
2778 }
2779 
2780 void
sp_end_plot(Multigrapher * mg)2781 sp_end_plot(Multigrapher *mg)
2782 {
2783 	switch (mg->plot_type) {
2784 	case SP_PLOT_XY:
2785 		end_graph(mg);
2786 		break;
2787 	case SP_PLOT_BAR:
2788 		sp_bar_end_graph(mg);
2789 		break;
2790 	case SP_PLOT_PIE:
2791 		sp_pie_end_graph(mg);
2792 		break;
2793 	default: {}
2794 		/* ??? */
2795 	}
2796 }
2797 
2798 void
sp_flush(Multigrapher * mg)2799 sp_flush(Multigrapher *mg)
2800 {
2801 	end_polyline_and_flush(mg);
2802 }
2803 
2804 void
sp_draw_frame(Multigrapher * multigrapher,int draw_canvas)2805 sp_draw_frame(Multigrapher *multigrapher, int draw_canvas)
2806 {
2807 	draw_frame_of_graph(multigrapher, draw_canvas);
2808 }
2809 
2810 void
sp_set_all_parameters(Multigrapher * multigrapher,double frame_line_width,const char * frame_color,const char * title,const char * title_font_name,double title_font_size,double tick_size,grid_type grid_spec,double x_min,double x_max,double x_spacing,double y_min,double y_max,double y_spacing,bool spec_x_spacing,bool spec_y_spacing,double width,double height,double up,double right,const char * x_font_name,double x_font_size,const char * x_label,const char * y_font_name,double y_font_size,const char * y_label,bool no_rotate_y_label,int log_axis,int round_to_next_tick,int switch_axis_end,int omit_ticks,int clip_mode,double blankout_fraction,bool transpose_axes)2811 sp_set_all_parameters(Multigrapher *multigrapher,
2812 	double frame_line_width,
2813 	const char *frame_color,
2814 	const char *title,
2815 	const char *title_font_name,
2816 	double title_font_size,
2817 	double tick_size,
2818 	grid_type grid_spec,
2819 	double x_min,
2820 	double x_max,
2821 	double x_spacing,
2822 	double y_min,
2823 	double y_max,
2824 	double y_spacing,
2825 	bool spec_x_spacing,
2826 	bool spec_y_spacing,
2827 	double width,
2828 	double height,
2829 	double up,
2830 	double right,
2831 	const char *x_font_name,
2832 	double x_font_size,
2833 	const char *x_label,
2834 	const char *y_font_name,
2835 	double y_font_size,
2836 	const char *y_label,
2837 	bool no_rotate_y_label,
2838 	int log_axis,
2839 	int round_to_next_tick,
2840 	int switch_axis_end,
2841 	int omit_ticks,
2842 	int clip_mode,
2843 	double blankout_fraction,
2844 	bool transpose_axes)
2845 {
2846 	set_graph_parameters(multigrapher,
2847 		frame_line_width,
2848 		frame_color,
2849 		title,
2850 		title_font_name,
2851 		title_font_size,
2852 		tick_size,
2853 		grid_spec,
2854 		x_min,
2855 		x_max,
2856 		x_spacing,
2857 		y_min,
2858 		y_max,
2859 		y_spacing,
2860 		spec_x_spacing,
2861 		spec_y_spacing,
2862 		width,
2863 		height,
2864 		up,
2865 		right,
2866 		x_font_name,
2867 		x_font_size,
2868 		x_label,
2869 		y_font_name,
2870 		y_font_size,
2871 		y_label,
2872 		no_rotate_y_label,
2873 		log_axis,
2874 		round_to_next_tick,
2875 		switch_axis_end,
2876 		omit_ticks,
2877 		clip_mode,
2878 		blankout_fraction,
2879 		transpose_axes);
2880 }
2881 
2882 void
sp_set_axis_range(Multigrapher * mg,int axis,double min,double max,double spacing,int log_axis)2883 sp_set_axis_range(Multigrapher *mg, int axis, double min, double max, double spacing, int log_axis)
2884 {
2885 	double temp;
2886 	int	t;
2887 
2888 #if 0
2889 	fprintf(stderr, "sp_set_axis_range(%s, min %f, max %f, spacing %f, %s)\n",
2890 		(axis & X_AXIS) ? "X axis" : "Y axis",
2891 		min, max, spacing,
2892 		log_axis ? "log" : "linear");
2893 #endif
2894 
2895 	spacing = (spacing < 0) ? -spacing : spacing;
2896 	mg->user_specified_subsubticks = 0;	/* FIX ME */
2897 
2898 	if (axis == X_AXIS) {
2899 		mg->x_spacing = spacing;
2900 		if (max < min) {
2901 			mg->x_min = max;
2902 			mg->x_max = min;
2903 		} else {
2904 			mg->x_max = max;
2905 			mg->x_min = min;
2906 		}
2907 
2908 		t = t & Y_AXIS;
2909 		if (log_axis)
2910 			t |= X_AXIS;
2911 	} else {
2912 		mg->y_spacing = spacing;
2913 		if (max < min) {
2914 			mg->y_min = max;
2915 			mg->y_max = min;
2916 		} else {
2917 			mg->y_max = max;
2918 			mg->y_min = min;
2919 		}
2920 
2921 		t = t & X_AXIS;
2922 		if (log_axis)
2923 			t |= Y_AXIS;
2924 	}
2925 
2926 	mg->log_axis = t;
2927 
2928 	prepare_axis(&mg->x_axis, &mg->x_trans,
2929 		mg->x_min, mg->x_max, mg->x_spacing,
2930 		mg->x_subsubtick_spacing,
2931 		(bool)(mg->user_specified_subsubticks & X_AXIS),
2932 		(bool)(mg->round_to_next_tick & X_AXIS),
2933 		(bool)(mg->log_axis & X_AXIS),
2934 		(bool)(mg->reverse_axis & X_AXIS));
2935 	prepare_axis(&mg->y_axis, &mg->y_trans,
2936 		mg->y_min, mg->y_max, mg->y_spacing,
2937 		mg->y_subsubtick_spacing,
2938 		(bool)(mg->user_specified_subsubticks & Y_AXIS),
2939 		(bool)(mg->round_to_next_tick & Y_AXIS),
2940 		(bool)(mg->log_axis & Y_AXIS),
2941 		(bool)(mg->reverse_axis & Y_AXIS));
2942 
2943 	OtherAxisLoc(mg);
2944 }
2945 
2946 void
sp_set_title(Multigrapher * mg,const char * t)2947 sp_set_title(Multigrapher *mg, const char *t)
2948 {
2949 	if (mg->title)
2950 		free((void *)mg->title);
2951 
2952 	if (t != NULL)
2953 		mg->title = strdup(t);
2954 	else
2955 		mg->title = NULL;
2956 }
2957 
2958 void
sp_set_axis_title(Multigrapher * mg,const int axis,const char * t)2959 sp_set_axis_title(Multigrapher *mg, const int axis, const char *t)
2960 {
2961 	if (axis == X_AXIS) {
2962 		if (mg->x_axis.label)
2963 			free((char *)mg->x_axis.label);
2964 		if (t)
2965 			mg->x_axis.label = strdup(t);
2966 		else
2967 			mg->x_axis.label = NULL;
2968 	}
2969 	if (axis == Y_AXIS) {
2970 		if (mg->y_axis.label)
2971 			free((char *)mg->y_axis.label);
2972 		if (t)
2973 			mg->y_axis.label = strdup(t);
2974 		else
2975 			mg->y_axis.label = NULL;
2976 	}
2977 }
2978 
2979 void
sp_first_dataset(Multigrapher * mg)2980 sp_first_dataset(Multigrapher *mg)
2981 {
2982 	mg->datasetnum = 0;
2983 	mg->point.linemode = 1;
2984 	mg->point.dataset = 0;
2985 	set_line_style(mg, mg->point.linemode, mg->point.use_color);
2986 #if 0
2987 	fprintf(stderr, "sp_first_dataset()\n");
2988 #endif
2989 }
2990 
2991 void
sp_next_dataset(Multigrapher * mg)2992 sp_next_dataset(Multigrapher *mg)
2993 {
2994 	mg->datasetnum++;
2995 	mg->point.linemode++;
2996 	mg->point.dataset++;
2997 	set_line_style(mg, mg->point.linemode, mg->point.use_color);
2998 #if 0
2999 	fprintf(stderr, "sp_next_dataset(%d)\n", mg->datasetnum);
3000 #endif
3001 }
3002 
3003 void
sp_setcolor(Multigrapher * mg,char * color)3004 sp_setcolor(Multigrapher *mg, char *color)
3005 {
3006 	if (mg->frame_color)
3007 		free((char *)mg->frame_color);
3008 	mg->frame_color = NULL;
3009 	if (color) {
3010 		mg->frame_color = strdup(color);
3011 		pl_pencolorname_r(mg->plotter, mg->frame_color);
3012 	}
3013 }
3014 
3015 /* following line types are the five used by Unix graph(1) */
3016 const char *linemodes[NO_OF_LINEMODES] =
3017 {
3018   "solid", "dotted", "dotdashed", "shortdashed", "longdashed"
3019 };
3020 
3021 /* following colors are the first five used by the gnuplot X11 driver */
3022 
3023 const char *colorstyle[NO_OF_LINEMODES] =
3024 {
3025   "red", "green", "blue", "magenta", "cyan"
3026 };
3027 
3028 /*
3029  * FIX ME this is historic
3030  */
3031 static char	*colors[] = { "yellow", "green", "blue", "red",
3032 				"magenta", "beige", "orange", "pink"};
3033 static int	NO_OF_COLORS = sizeof(colors) / sizeof(char *);
3034 
3035 /*
3036  * Bar Graph code
3037  */
3038 static void
sp_bar_begin_graph(Multigrapher * multigrapher,double scale,double trans_x,double trans_y)3039 sp_bar_begin_graph(Multigrapher *multigrapher, double scale, double trans_x, double trans_y)
3040 {
3041 }
3042 
3043 void
sp_bar_plot_point(Multigrapher * mg,const Point * point)3044 sp_bar_plot_point(Multigrapher *mg, const Point *point)
3045 {
3046 	sp_pie_plot_point(mg, point);	/* FIX ME */
3047 }
3048 
3049 static void
sp_bar_end_graph(Multigrapher * mg)3050 sp_bar_end_graph(Multigrapher *mg)
3051 {
3052 	int		i, num, r, nsets, *dsvalid, n;
3053 	double		x, y, y1, y2, ymin, ymax, *ys;
3054 
3055 	int		stacked = 1;				/* FIX ME only stacked for now */
3056 
3057 	/* How many items ? */
3058 	nsets = mg->datasetnum;
3059 	dsvalid = (int *)calloc(nsets, sizeof(int));
3060 
3061 /*	fprintf(stderr, "sp_bar_end_graph() : %d datasets, overall %d points\n",
3062 		mg->datasetnum, mg->npoints);
3063 */
3064 
3065 	for (r = 0; r < nsets; r++)
3066 		dsvalid[r] = 0;
3067 
3068 	for (i=0; i < mg->npoints; i++)
3069 		dsvalid[mg->data[i].dataset]++;
3070 
3071 	num = 0;
3072 	for (r = 0; r < nsets; r++)
3073 		if (num < dsvalid[r])
3074 			num = dsvalid[r];
3075 
3076 	ys = (double *)calloc(nsets * num, sizeof(double));
3077 #define	Y_VALUE(a,b)	ys[nsets * a + b]
3078 
3079 	for (r = 0; r < nsets; r++)
3080 		dsvalid[r] = 0;
3081 	for (i = 0; i < mg->npoints; i++) {
3082 		r = mg->data[i].dataset;
3083 		n = dsvalid[r];
3084 		if (mg->data[i].y <= 0.0)
3085 			Y_VALUE(r,n) = 0.0;
3086 		else
3087 			Y_VALUE(r,n) = mg->data[i].y;
3088 /*		fprintf(stderr, "Data[%d,%d] = %f\n", r, n, mg->data[i].y);	*/
3089 		dsvalid[r]++;
3090 	}
3091 
3092 	/* Find Y boundaries */
3093 	if (stacked) {
3094 		ymin = ymax = Y_VALUE(0,0);
3095 		for (i=0; i<num; i++) {
3096 			y = 0.0;
3097 			for (r = 0; r < nsets; r++)
3098 				y += Y_VALUE(r,i);
3099 			if (ymax < y) ymax = y;
3100 			if (ymin > y) ymin = y;
3101 		}
3102 	} else {
3103 		ymin = ymax = Y_VALUE(0,0);
3104 		for (r = 0; r < nsets; r++) {
3105 			for (i = 0; i < dsvalid[r]; i++) {
3106 				y = Y_VALUE(r,i);
3107 				if (ymax < y) ymax = y;
3108 				if (ymin > y) ymin = y;
3109 			}
3110 		}
3111 	}
3112 
3113 #if 0
3114 	fprintf(stderr, "sp_bar_end_graph: Y range %f .. %f\n", ymin, ymax);
3115 #endif
3116 
3117 	pl_fspace_r(mg->plotter,
3118 			PLOT_SIZE * -0.1, PLOT_SIZE * -0.1,
3119 			PLOT_SIZE * 1.1, PLOT_SIZE * 1.1);
3120 
3121 	pl_pencolorname_r(mg->plotter, "black");			/* FIX ME */
3122 	pl_fline_r(mg->plotter, 0., 0., 0., PLOT_SIZE);
3123 	pl_fline_r(mg->plotter, 0., 0., PLOT_SIZE, 0.);
3124 
3125 #define	TO_X(ii)	(0.05 * PLOT_SIZE + PLOT_SIZE * ((double)ii) / ((double)num))
3126 
3127 	/*
3128 	 * Go over points in the "wrong" order : first we take all the first
3129 	 * points of all datasets, then the second points of all datasets, ..
3130 	 */
3131 	for (i=0; i<num; i++) {
3132 		for (r = 0; r < nsets; r++) {
3133 			pl_fillcolorname_r(mg->plotter, colors[r % NO_OF_COLORS]);
3134 			x = TO_X(i);
3135 			if (ymax) {
3136 				if (r == 0) {
3137 					/* The first point in this set */
3138 					pl_fbox_r(mg->plotter,
3139 						TO_X(i), 0.0,
3140 						TO_X(i + 0.6), Y_VALUE(r,i) / ymax * PLOT_SIZE);
3141 				} else {
3142 					/* Not the first point in this set */
3143 					int ii;
3144 					double	s = 0.0;
3145 
3146 					for (ii=0; ii<r; ii++)
3147 						s += Y_VALUE(ii,i);
3148 
3149 					pl_fbox_r(mg->plotter,
3150 						TO_X(i), s / ymax * PLOT_SIZE,
3151 						TO_X(i + 0.6), (s + Y_VALUE(r,i)) / ymax * PLOT_SIZE);
3152 				}
3153 			}
3154 		}
3155 	}
3156 
3157 #if 0
3158 	/* General Title */
3159 	if (mg->title) {
3160 		pl_fmove_r(mg->plotter, 5.0, -0.75);
3161 		pl_alabel_r(mg->plotter, 1, 1, mg->title);
3162 	}
3163 #endif
3164 
3165 	/* X Axis Labels */
3166 #if 0
3167 	i = 1;
3168 	while ((cp = next_cell_in_range())) {
3169 		x = TO_X(i);
3170 		if (GET_TYP(cp) == TYP_STR)
3171 			if (cp->cell_str) {
3172 				pl_fmove_r(mg->plotter, x, -0.3);
3173 				pl_alabel_r(mg->plotter, 1, 1, cp->cell_str);
3174 			}
3175 		else
3176 			/* ??? */ ;
3177 
3178 		i++;
3179 	}
3180 #endif
3181 
3182 #if 0
3183 	/* Data titles */
3184 	if (mg->x_axis.label) {
3185 		pl_fmove_r(mg->plotter, 10.0, -0.75);
3186 		pl_alabel_r(mg->plotter, 1, 1, mg->x_axis.label);
3187 	}
3188 	if (mg->y_axis.label) {
3189 		pl_fmove_r(mg->plotter, 0.0, 10.5);
3190 		pl_alabel_r(mg->plotter, 1, 1, mg->y_axis.label);
3191 	}
3192 #endif
3193 
3194 	free(ys);
3195 	free(dsvalid);
3196 }
3197 
3198 /*
3199  * Pie Chart code
3200  */
3201 static void
sp_pie_begin_graph(Multigrapher * multigrapher,double scale,double trans_x,double trans_y)3202 sp_pie_begin_graph(Multigrapher *multigrapher, double scale, double trans_x, double trans_y)
3203 {
3204 }
3205 
3206 void
sp_pie_plot_point(Multigrapher * mg,const Point * point)3207 sp_pie_plot_point(Multigrapher *mg, const Point *point)
3208 {
3209 	int	n = mg->npoints++;
3210 
3211 	mg->data = (Point *)realloc(mg->data, mg->npoints * sizeof(Point));
3212 	memcpy(&mg->data[n], point, sizeof(Point));
3213 
3214 	/* Must copy the string separately */
3215 	if (mg->data[n].label)
3216 		mg->data[n].label = strdup(mg->data[n].label);
3217 }
3218 
3219 static void
sp_pie_end_graph(Multigrapher * mg)3220 sp_pie_end_graph(Multigrapher *mg)
3221 {
3222 	int	i, num, c;
3223 	char	*s;
3224 	double	total = 0., curr, incr, f,
3225 		r = PLOT_SIZE * 0.7,
3226 		*lx, *ly, la;
3227 
3228 #define	X(r,a)	(cos(a)*(r))
3229 #define	Y(r,a)	(sin(a)*(r))
3230 #define	RAD(a)	((a)/180.*M_PI)
3231 #define	XY(r,a)	X((r),RAD(a)),Y((r),RAD(a))
3232 
3233 	/* Compute total */
3234 	for (i=0; i<mg->npoints; i++) {
3235 		total += mg->data[i].x;
3236 	}
3237 
3238 	if (mg->datasetnum != 1) {
3239 		fprintf(stderr, "GNU " PACKAGE " " VERSION
3240 		": warning : this is not kosher, pie charts should have only one dataset\n");
3241 	}
3242 #if 0
3243 	if (mg->npoints) {
3244 		fprintf(stderr, "Pie - have %d data points (%f", mg->npoints, mg->data[0].x);
3245 		for (i=1; i<mg->npoints; i++)
3246 			fprintf(stderr, ", %f", mg->data[i].x);
3247 		fprintf(stderr, ")\n");
3248 	}
3249 #endif
3250 
3251 	pl_pencolorname_r(mg->plotter, "black");
3252 
3253 	/* Allocate space for label coordinates */
3254 	lx = (double *)calloc(mg->npoints, sizeof(double));
3255 	if (lx == NULL)
3256 		return;
3257 	ly = (double *)calloc(mg->npoints, sizeof(double));
3258 	if (ly == NULL)
3259 		return;
3260 
3261 	incr = curr = 0.0;
3262 	for (i=0; i<mg->npoints; i++) {
3263 		double	x1, x2, x3, y1, y2, y3;
3264 
3265 		f = mg->data[i].x;
3266 		if (f <= 0.0)
3267 			continue;	/* Negative values considered invalid */
3268 
3269 		incr = f/total*360.;
3270 
3271 		pl_fillcolorname_r(mg->plotter, colors[i % NO_OF_COLORS]);
3272 
3273 		pl_fmove_r(mg->plotter, 0.0, 0.0);
3274 		pl_fcont_r(mg->plotter, XY(r, curr));
3275 		if (incr > 179) {
3276 			pl_farc_r(mg->plotter, 0.0,0.0,XY(r,curr),XY(r,curr+179.));
3277 			curr += 179.;
3278 			incr -= 179;
3279 		}
3280 
3281 		pl_farc_r(mg->plotter, 0.0,0.0, XY(r,curr), XY(r,incr+curr));
3282 		pl_fcont_r(mg->plotter, 0.0,0.0);
3283 		pl_endpath_r(mg->plotter);
3284 
3285 		la = curr + incr / 2.0;
3286 		lx[i] = X(PLOT_SIZE * 0.8, RAD(la));
3287 		ly[i] = Y(PLOT_SIZE * 0.8, RAD(la));
3288 
3289 		curr += incr;
3290 	}
3291 
3292 	/* Title */
3293 	if (mg->title) {
3294 		pl_fmove_r(mg->plotter, 0., PLOT_SIZE * -0.9);
3295 		pl_alabel_r(mg->plotter, 1, 1, mg->title);
3296 	}
3297 
3298 	/* Place labels */
3299 	for (i=0; i<mg->npoints; i++) {
3300 		pl_fmove_r(mg->plotter, lx[i], ly[i]);
3301 		if (mg->data[i].label) {
3302 /*		    fprintf(stderr, "Label %s for point %d\n", mg->data[i].label, i);	*/
3303 		    pl_alabel_r(mg->plotter, 1, 1, mg->data[i].label);
3304 		}
3305 	}
3306 
3307 	free(lx);
3308 	free(ly);
3309 
3310 #undef	X(r,a)
3311 #undef	Y(r,a)
3312 #undef	RAD(a)
3313 #undef	XY(r,a)
3314 }
3315 
3316 void
sp_set_axis_label_font_size(Multigrapher * mg,int axis,double s)3317 sp_set_axis_label_font_size(Multigrapher *mg, int axis, double s)
3318 {
3319 	if (axis & X_AXIS) {
3320 		mg->x_axis.font_size = s;
3321 	}
3322 	if (axis & Y_AXIS) {
3323 		mg->y_axis.font_size = s;
3324 	}
3325 }
3326 
3327 /*
3328  * sp_set_axis_ticktype()
3329  *
3330  * Use this function to indicate what to translate the axis values into.
3331  * E.g. if the axis value really represents a date, then you can specify
3332  * the value to round to (e.g. the representation of one month), the value
3333  * to increment with (e.g. the representation of three months), and a
3334  * function which is called to translate the value into a text string that
3335  * will be shown (e.g. "January\n2000").
3336  */
3337 void
sp_set_axis_ticktype(Multigrapher * mg,int axis,double round_to,double incr,axis_xlate_tick xlate_tick)3338 sp_set_axis_ticktype(Multigrapher *mg, int axis,
3339 	double round_to, double incr, axis_xlate_tick xlate_tick)
3340 {
3341 }
3342 
3343 /*
3344  * sp_set_axis_ticktype_date()
3345  *
3346  * This is a special case of sp_set_axis_ticktype(), where the function
3347  * called is strftime(), a standard function to convert time/date into
3348  * text format.
3349  * The parameter format_string is the format parameter to strftime.
3350  */
3351 void
sp_set_axis_ticktype_date(Multigrapher * mg,int axis,double round_to,double incr,char * format_string)3352 sp_set_axis_ticktype_date(Multigrapher *mg, int axis,
3353 	double round_to, double incr, char *format_string)
3354 {
3355 	sp_set_axis_ticktype(mg, axis, round_to, incr, NULL);
3356 	mg->x_axis.tick_type = SP_TICK_STRFTIME;
3357 	mg->x_axis.tick_format = format_string;
3358 }
3359 
3360 /*
3361  * Historic
3362  */
3363 void
begin_graph(Multigrapher * multigrapher,double scale,double trans_x,double trans_y)3364 begin_graph(Multigrapher *multigrapher, double scale, double trans_x, double trans_y)
3365 {
3366 	sp_xy_begin_graph(multigrapher, scale, trans_x, trans_y);
3367 }
3368 
end_graph(Multigrapher * mg)3369 void end_graph(Multigrapher *mg)
3370 {
3371 	sp_xy_end_graph(mg);
3372 }
3373 #endif	/* HAVE_LIBSCIPLOT */
3374 #endif	/* HAVE_LIBPLOT */
3375