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