forked from gnuplot/gnuplot-old
-
Notifications
You must be signed in to change notification settings - Fork 0
/
graphics.c
4374 lines (3947 loc) · 125 KB
/
graphics.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#ifndef lint
static char *RCSid = "$Id: graphics.c,v 1.17 1998/11/04 14:48:43 lhecking Exp $";
#endif
/* GNUPLOT - graphics.c */
/*[
* Copyright 1986 - 1993, 1998 Thomas Williams, Colin Kelley
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose with or without fee is hereby granted,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation.
*
* Permission to modify the software is granted, but not the right to
* distribute the complete modified source code. Modifications are to
* be distributed as patches to the released version. Permission to
* distribute binaries produced by compiling modified sources is granted,
* provided you
* 1. distribute the corresponding source modifications from the
* released version in the form of a patch file along with the binaries,
* 2. add special version identification to distinguish your version
* in addition to the base release version number,
* 3. provide your name and address as the primary contact for the
* support of your modified version, and
* 4. retain our contact information in regard to use of the base
* software.
* Permission to distribute the released version of the source code along
* with corresponding source modifications in the form of a patch file is
* granted with same provisions 2 through 4 for binary distributions.
*
* This software is provided "as is" without express or implied warranty
* to the extent permitted by applicable law.
]*/
#include "plot.h"
#include "setshow.h"
/* key placement is calculated in boundary, so we need file-wide variables
* To simplify adjustments to the key, we set all these once [depends on
* key_reverse] and use them throughout.
*/
/*{{{ local and global variables */
static int key_sample_width; /* width of line sample */
static int key_sample_left; /* offset from x for left of line sample */
static int key_sample_right; /* offset from x for right of line sample */
static int key_point_offset; /* offset from x for point sample */
static int key_text_left; /* offset from x for left-justified text */
static int key_text_right; /* offset from x for right-justified text */
static int key_size_left; /* size of left bit of key (text or sample, depends on key_reverse) */
static int key_size_right; /* size of right part of key (including padding) */
/* I think the following should also be static ?? */
static int key_xl, key_xr, key_yt, key_yb; /* boundarys for key field */
static int max_ptitl_len = 0; /* max length of plot-titles (keys) */
static int ktitl_lines = 0; /* no lines in key_title (key header) */
static int ptitl_cnt; /* count keys with len > 0 */
static int key_cols; /* no cols of keys */
static int key_rows, key_col_wth, yl_ref;
/* penalty for doing tics by callback in gen_tics is need for
* global variables to communicate with the tic routines
* Dont need to be arrays for this
*/
static int tic_start, tic_direction, tic_text, rotate_tics, tic_hjust,
tic_vjust, tic_mirror;
/* set by tic_callback - how large to draw polar radii */
static double largest_polar_circle;
/* either xformat etc or invented time format
* index with FIRST_X_AXIS etc
* global because used in gen_tics, which graph3d also uses
*/
char ticfmt[8][25];
int timelevel[8];
double ticstep[8];
static int xlablin, x2lablin, ylablin, y2lablin, titlelin, xticlin,
x2ticlin;
static int key_entry_height; /* bigger of t->v_size, pointsize*t->v_tick */
static int p_width, p_height; /* pointsize * { t->h_tic | t->v_tic } */
/* there are several things on right of plot - key, y2tics and y2label
* when working out boundary, save posn of y2label for later...
* Same goes for x2label.
* key posn is also stored in key_xl, and tics go at xright
*/
static int ylabel_x, y2label_x, xlabel_y, x2label_y, title_y, time_y,
time_x;
static int ylabel_y, y2label_y, xtic_y, x2tic_y, ytic_x, y2tic_x;
/*}}} */
/*{{{ static fns and local macros */
static void plot_impulses __PROTO((struct curve_points * plot, int yaxis_x, int xaxis_y));
static void plot_lines __PROTO((struct curve_points * plot));
static void plot_points __PROTO((struct curve_points * plot));
static void plot_dots __PROTO((struct curve_points * plot));
static void plot_bars __PROTO((struct curve_points * plot));
static void plot_boxes __PROTO((struct curve_points * plot, int xaxis_y));
static void plot_vectors __PROTO((struct curve_points * plot));
static void plot_f_bars __PROTO((struct curve_points * plot));
static void plot_c_bars __PROTO((struct curve_points * plot));
static void edge_intersect __PROTO((struct coordinate GPHUGE * points, int i, double *ex, double *ey));
static int two_edge_intersect __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));
static TBOOLEAN two_edge_intersect_steps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));
static void plot_steps __PROTO((struct curve_points * plot)); /* JG */
static void plot_fsteps __PROTO((struct curve_points * plot)); /* HOE */
static void plot_histeps __PROTO((struct curve_points * plot)); /* CAC */
static void histeps_horizontal __PROTO((int *xl, int *yl, double x1, double x2, double y)); /* CAC */
static void histeps_vertical __PROTO((int *xl, int *yl, double x, double y1, double y2)); /* CAC */
static void edge_intersect_steps __PROTO((struct coordinate GPHUGE * points, int i, double *ex, double *ey)); /* JG */
static void edge_intersect_fsteps __PROTO((struct coordinate GPHUGE * points, int i, double *ex, double *ey)); /* HOE */
static TBOOLEAN two_edge_intersect_steps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly)); /* JG */
static TBOOLEAN two_edge_intersect_fsteps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));
static double LogScale __PROTO((double coord, int is_log, double log_base_log, char *what, char *axis));
static double dbl_raise __PROTO((double x, int y));
static void boundary __PROTO((int scaling, struct curve_points * plots, int count));
static double make_tics __PROTO((int axis, int guide));
static int widest_tic; /* widest2d_callback keeps longest so far in here */
static void widest2d_callback __PROTO((int axis, double place, char *text, struct lp_style_type grid));
static void ytick2d_callback __PROTO((int axis, double place, char *text, struct lp_style_type grid));
static void xtick2d_callback __PROTO((int axis, double place, char *text, struct lp_style_type grid));
static void map_position __PROTO((struct position * pos, unsigned int *x, unsigned int *y, char *what));
static void mant_exp __PROTO((double log_base, double x, int scientific, double *m, int *p));
static void gprintf __PROTO((char *dest, size_t count, char *format, double log_base, double x));
#if defined(sun386) || defined(AMIGA_SC_6_1)
static double CheckLog __PROTO((TBOOLEAN is_log, double base_log, double x));
#endif
/* for plotting error bars
* half the width of error bar tic mark
*/
#define ERRORBARTIC (t->h_tic/2)
/*
* The Amiga SAS/C 6.2 compiler moans about macro envocations causing
* multiple calls to functions. I converted these macros to inline
* functions coping with the problem without loosing speed.
* If your compiler supports __inline, you should add it to the
* #ifdef directive
* (MGR, 1993)
*/
#ifdef AMIGA_SC_6_1
GP_INLINE static TBOOLEAN i_inrange(int z, int min, int max)
{
return ((min < max) ? ((z >= min) && (z <= max)) : ((z >= max) && (z <= min)));
}
GP_INLINE static double f_max(double a, double b)
{
return (GPMAX(a, b));
}
GP_INLINE static double f_min(double a, double b)
{
return (GPMIN(a, b));
}
#else
#define f_max(a,b) GPMAX((a),(b))
#define f_min(a,b) GPMIN((a),(b))
#define i_inrange(z,a,b) inrange((z),(a),(b))
#endif
/* True if a and b have the same sign or zero (positive or negative) */
#define samesign(a,b) ((a) * (b) >= 0)
/*}}} */
/*{{{ more variables */
/* Define the boundary of the plot
* These are computed at each call to do_plot, and are constant over
* the period of one do_plot. They actually only change when the term
* type changes and when the 'set size' factors change.
* - no longer true, for 'set key out' or 'set key under'. also depend
* on tic marks and multi-line labels.
* They are shared with graph3d.c since we want to use its draw_clip_line()
*/
int xleft, xright, ybot, ytop;
/* we make a local copy of the 'key' variable so that if something
* goes wrong, we can switch it off temporarily
*/
static int lkey;
/* First attempt at double axes...
* x_min etc are now accessed from a global array min_array[], max_array[]
* put the scale factors into a similar array
* for convenience in this first attack on double axes, just define x_min etc
* since code already uses x_min, etc Eventually it will be done properly
*/
extern double min_array[], max_array[];
extern int auto_array[];
extern int log_array[];
extern double base_array[], log_base_array[];
static int x_axis = FIRST_X_AXIS, y_axis = FIRST_Y_AXIS; /* current axes */
static double scale[AXIS_ARRAY_SIZE]; /* scale factors for mapping for each axis */
/* BODGES BEFORE I FIX IT UP */
#define x_min min_array[x_axis]
#define x_max max_array[x_axis]
#define y_min min_array[y_axis]
#define y_max max_array[y_axis]
/* And the functions to map from user to terminal coordinates */
/* maps floating point x to screen */
#define map_x(x) (int)(xleft+(x-min_array[x_axis])*scale[x_axis]+0.5)
/* same for y */
#define map_y(y) (int)(ybot +(y-min_array[y_axis])*scale[y_axis]+0.5)
/* (DFK) Watch for cancellation error near zero on axes labels */
/* less than one hundredth of a tic mark */
#define SIGNIF (0.01)
#define CheckZero(x,tic) (fabs(x) < ((tic) * SIGNIF) ? 0.0 : (x))
#define NearlyEqual(x,y,tic) (fabs((x)-(y)) < ((tic) * SIGNIF))
/*}}} */
/*{{{ CheckLog() */
/* (DFK) For some reason, the Sun386i compiler screws up with the CheckLog
* macro, so I write it as a function on that machine.
*
* Amiga SAS/C 6.2 thinks it will do too much work calling functions in
* macro arguments twice, thus I inline theese functions. (MGR, 1993)
* If your compiler doesn't handle those macros correctly, you should
* also subscribe here. Even without inlining you gain speed with log plots
*/
#if defined(sun386) || defined(AMIGA_SC_6_1)
GP_INLINE static double CheckLog(is_log, base_log, x)
TBOOLEAN is_log;
double base_log;
double x;
{
if (is_log)
return (pow(base_log, x));
else
return (x);
}
#else
/* (DFK) Use 10^x if logscale is in effect, else x */
#define CheckLog(is_log, base_log, x) ((is_log) ? pow(base_log, (x)) : (x))
#endif /* sun386 || SAS/C */
/*}}} */
/*{{{ LogScale() */
static double LogScale(coord, is_log, log_base_log, what, axis)
double coord; /* the value */
TBOOLEAN is_log; /* is this axis in logscale? */
double log_base_log; /* if so, the log of its base */
char *what; /* what is the coord for? */
char *axis; /* which axis is this for ("x" or "y")? */
{
if (is_log) {
if (coord <= 0.0) {
char errbuf[100]; /* place to write error message */
(void) sprintf(errbuf, "%s has %s coord of %g; must be above 0 for log scale!",
what, axis, coord);
graph_error(errbuf);
} else
return (log(coord) / log_base_log);
}
return (coord);
}
/*}}} */
/*{{{ graph_error() */
/* handle errors during graph-plot in a consistent way */
void graph_error(text)
char *text;
{
multiplot = FALSE;
term_end_plot();
int_error(text, NO_CARET);
}
/*}}} */
/*{{{ fixup_range() */
/*
* === SYNOPSIS ===
*
* This function checks whether the data and/or plot range in a given axis
* is too small (which would cause divide-by-zero and/or infinite-loop
* problems later on). If so,
* - if autoscaling is in effect for this axis, we widen the range
* - otherwise, we abort with a call to int_error() (which prints out
* a suitable error message, then (hopefully) aborts this command and
* returns to the command prompt or whatever).
*
*
* === HISTORY AND DESIGN NOTES ===
*
* 1998 Oct 4, Jonathan Thornburg <jthorn@galileo.thp.univie.ac.at>
*
* This function used to be a (long) macro FIXUP_RANGE(AXIS, WHICH)
* which was (identically!) defined in plot2d.c and plot3d.c . As
* well as now being a function instead of a macro, the logic is also
* changed: The "too small" range test no longer depends on 'set zero'
* and is now properly scaled relative to the data magnitude.
*
* The key question in designing this function is the policy for just how
* much to widen the data range by, as a function of the data magnitude.
* This is to some extent a matter of taste. IMHO the key criterion is
* that (at least) all of the following should (a) not infinite-loop, and
* (b) give correct plots, regardless of the 'set zero' setting:
* plot 6.02e23 # a huge number >> 1 / FP roundoff level
* plot 3 # a "reasonable-sized" number
* plot 1.23e-12 # a small number still > FP roundoff level
* plot 1.23e-12 * sin(x) # a small function still > FP roundoff level
* plot 1.23e-45 # a tiny number << FP roundoff level
* plot 1.23e-45 * sin(x) # a tiny function << FP roundoff level
* plot 0 # or (more commonly) a data file of all zeros
* That is, IMHO gnuplot should *never* infinite-loop, and it should *never*
* producing an incorrect or misleading plot. In contrast, the old code
* would infinite-loop on most of these examples with 'set zero 0.0' in
* effect, or would plot the small-amplitude sine waves as the zero function
* with 'zero' set larger than the sine waves' amplitude.
*
* The current code plots all the above examples correctly and without
* infinite looping.
*
*
* === USAGE ===
*
* Arguments:
* axis = (in) An integer specifying which axis (x1, x2, y1, y2, z, etc)
* we should do our stuff for. We use this argument as an
* index into the global arrays {min,max,auto}_array . In
* practice this argument will typically be one of the constants
* {FIRST,SECOND}_{X,Y,Z}_AXIS defined in plot.h.
* axis_name --> (in) This argument should point to the character string
* name corresponding to axis , e.g. "x", "y2", etc.
* We use this (only) in formatting warning/error messages.
*
* Global Variables:
* auto_array[axis] = (in) (defined in command.c) Bit-flags which tell
* [in some manner I don't fully understand :=( ]
* whether and/or how autoscaling is in effect for
* this axis.
* {min,max}_array = (in out) (defined in command.c) The data ranges which
* this function manipulates.
* c_token = (in) (defined in plot.h) Used in formatting an error message.
*
* Bugs:
* - If strlen(axis_name) > strlen("%s") , we may overflow an
* error-message buffer, which would be A Bad Thing. Caveat caller...
*/
void fixup_range(axis, axis_name)
int axis;
char *axis_name;
{
#define MAX_AXIS_NAME_LEN 2 /* max legal strlen(axis_name) */
/* These two macro definitions set the range-widening policy: */
#define FIXUP_RANGE__WIDEN_ZERO_ABS 1.0 /* widen [0:0] by
+/- this absolute amount */
#define FIXUP_RANGE__WIDEN_NONZERO_REL 0.01 /* widen [nonzero:nonzero] by
-/+ this relative amount */
double dmin = min_array[axis];
double dmax = max_array[axis];
if (dmax - dmin == 0.0) {
/* empty range */
if (auto_array[axis]) {
/* range came from autoscaling ==> widen it */
double widen = (dmax == 0.0)
? FIXUP_RANGE__WIDEN_ZERO_ABS
: FIXUP_RANGE__WIDEN_NONZERO_REL * dmax;
fprintf(stderr,
"Warning: empty %s range [%g:%g], ",
axis_name, dmin, dmax);
min_array[axis] -= widen;
max_array[axis] += widen;
fprintf(stderr,
"adjusting to [%g:%g]\n",
min_array[axis], max_array[axis]);
} else {
/* user has explicitly set the range */
/* (to something empty) ==> we're in trouble */
char msg_buffer[MAX_LINE_LEN+1];
sprintf(msg_buffer, "Can't plot with an empty %s range!", axis_name);
int_error(msg_buffer, c_token); /* never returns */
}
}
}
/*}}} */
/*{{{ widest2d_callback() */
/* we determine widest tick label by getting gen_ticks to call this
* routine with every label
*/
static void widest2d_callback(axis, place, text, grid)
int axis;
double place;
char *text;
struct lp_style_type grid;
{
int len = label_width(text, NULL);
if (len > widest_tic)
widest_tic = len;
}
/*}}} */
/*{{{ boundary() */
/* borders of plotting area
* computed once on every call to do_plot
*
* The order in which things is done is getting pretty critical:
* ytop depends on title, x2label, ylabels (if no rotated text)
* ybot depends on key, if TUNDER
* once we have these, we can setup the y1 and y2 tics and the
* only then can we calculate xleft and xright
* xright depends also on key TRIGHT
* then we can do x and x2 tics
*
* For set size ratio ..., everything depends on everything else...
* not really a lot we can do about that, so we lose if the plot has to
* be reduced vertically. But the chances are the
* change will not be very big, so the number of tics will not
* change dramatically.
*
* Margin computation redone by Dick Crawford (rccrawford@lanl.gov) 4/98
*/
static void boundary(scaling, plots, count)
TBOOLEAN scaling; /* TRUE if terminal is doing the scaling */
struct curve_points *plots;
int count;
{
int ytlen;
int yticlin = 0, y2ticlin = 0, timelin = 0;
register struct termentry *t = term;
int key_h, key_w;
int can_rotate = (*t->text_angle) (1);
int xtic_textheight; /* height of xtic labels */
int x2tic_textheight; /* height of x2tic labels */
int title_textheight; /* height of title */
int xlabel_textheight; /* height of xlabel */
int x2label_textheight; /* height of x2label */
int timetop_textheight; /* height of timestamp (if at top) */
int timebot_textheight; /* height of timestamp (if at bottom) */
int ylabel_textheight; /* height of (unrotated) ylabel */
int y2label_textheight; /* height of (unrotated) y2label */
int ylabel_textwidth; /* width of (rotated) ylabel */
int y2label_textwidth; /* width of (rotated) y2label */
int timelabel_textwidth; /* width of timestamp */
int ytic_textwidth; /* width of ytic labels */
int y2tic_textwidth; /* width of y2tic labels */
int x2tic_height; /* 0 for tic_in or no x2tics, ticscale*v_tic otherwise */
int xtic_height;
int ytic_width;
int y2tic_width;
/* figure out which rotatable items are to be rotated
* (ylabel and y2label are rotated if possible) */
int vertical_timelabel = can_rotate && timelabel_rotate;
int vertical_xtics = can_rotate && rotate_xtics;
int vertical_x2tics = can_rotate && rotate_x2tics;
int vertical_ytics = can_rotate && rotate_ytics;
int vertical_y2tics = can_rotate && rotate_y2tics;
lkey = key; /* but we may have to disable it later */
xticlin = ylablin = y2lablin = xlablin = x2lablin = titlelin = 0;
/*{{{ count lines in labels and tics */
if (*title.text)
label_width(title.text, &titlelin);
if (*xlabel.text)
label_width(xlabel.text, &xlablin);
if (*x2label.text)
label_width(x2label.text, &x2lablin);
if (*ylabel.text)
label_width(ylabel.text, &ylablin);
if (*y2label.text)
label_width(y2label.text, &y2lablin);
if (xtics)
label_width(xformat, &xticlin);
if (x2tics)
label_width(x2format, &x2ticlin);
if (ytics)
label_width(yformat, &yticlin);
if (y2tics)
label_width(y2format, &y2ticlin);
if (*timelabel.text)
label_width(timelabel.text, &timelin);
/*}}} */
/*{{{ preliminary ytop calculation */
/* first compute heights of things to be written in the margin */
/* title */
if (titlelin)
title_textheight = (int) ((titlelin + title.yoffset) * (t->v_char));
else
title_textheight = 0;
/* x2label */
if (x2lablin)
x2label_textheight = (int) ((x2lablin + x2label.yoffset) * (t->v_char));
else
x2label_textheight = 0;
/* tic labels */
if (x2tics & TICS_ON_BORDER) {
/* ought to consider tics on axes if axis near border */
if (vertical_x2tics) {
/* guess at tic length, since we don't know it yet
--- we'll fix it after the tic labels have been created */
x2tic_textheight = (int) (5 * (t->h_char));
} else
x2tic_textheight = (int) ((x2ticlin) * (t->v_char));
} else
x2tic_textheight = 0;
/* tics */
if (!tic_in && ((x2tics & TICS_ON_BORDER) || ((xtics & TICS_MIRROR) && (xtics & TICS_ON_BORDER))))
x2tic_height = (int) ((t->v_tic) * ticscale);
else
x2tic_height = 0;
/* timestamp */
if (*timelabel.text && !timelabel_bottom && !vertical_timelabel)
timetop_textheight = (int) ((timelin + timelabel.yoffset) * (t->v_char));
else
timetop_textheight = 0;
/* horizontal ylabel */
if (*ylabel.text && !can_rotate)
ylabel_textheight = (int) ((ylablin + ylabel.yoffset) * (t->v_char));
else
ylabel_textheight = 0;
/* horizontal y2label */
if (*y2label.text && !can_rotate)
y2label_textheight = (int) ((y2lablin + y2label.yoffset) * (t->v_char));
else
y2label_textheight = 0;
/* compute ytop from the various components
* unless tmargin is explicitly specified */
ytop = (int) ((ysize + yoffset) * (t->ymax));
if (tmargin < 0) {
int top_margin = x2label_textheight + title_textheight;
if (timetop_textheight + ylabel_textheight > top_margin)
top_margin = timetop_textheight + ylabel_textheight;
if (y2label_textheight > top_margin)
top_margin = y2label_textheight;
top_margin += x2tic_height + x2tic_textheight;
/* FIXME: what is this additional space reservation for??? */
if (top_margin > x2tic_height)
top_margin += (int) (t->v_char);
ytop -= top_margin;
if (ytop == (int) ((ysize + yoffset) * (t->ymax))) {
/* make room for the end of rotated ytics or y2tics */
ytop -= (int) ((t->h_char) * 2);
}
} else
ytop -= (int) ((t->v_char) * tmargin);
/* end of preliminary ytop calculation }}} */
/*{{{ tentative xleft, needed for TUNDER */
if (lmargin >= 0)
xleft = (int) (xoffset * (t->xmax) + (t->h_char) * lmargin);
else
xleft = (int) (xoffset * (t->xmax) + (t->h_char) * 2);
/*}}} */
/*{{{ tentative xright, needed for TUNDER */
if (rmargin >= 0)
xright = (int) ((xsize + xoffset) * (t->xmax) - (t->h_char) * rmargin);
else
xright = (int) ((xsize + xoffset) * (t->xmax) - (t->h_char) * 2);
/*}}} */
/*{{{ preliminary ybot calculation
* first compute heights of labels and tics */
/* tic labels */
if (xtics & TICS_ON_BORDER) {
/* ought to consider tics on axes if axis near border */
if (vertical_xtics) {
/* guess at tic length, since we don't know it yet */
xtic_textheight = (int) ((t->h_char) * 5);
} else
xtic_textheight = (int) ((t->v_char) * (xticlin + 1));
} else
xtic_textheight = 0;
/* tics */
if (!tic_in && ((xtics & TICS_ON_BORDER) || ((x2tics & TICS_MIRROR) && (x2tics & TICS_ON_BORDER))))
xtic_height = (int) ((t->v_tic) * ticscale);
else
xtic_height = 0;
/* xlabel */
if (xlablin)
/* offset is subtracted because if . 0, the margin is smaller */
xlabel_textheight = (int) ((xlablin - xlabel.yoffset) * (t->v_char));
else
xlabel_textheight = 0;
/* timestamp */
if (*timelabel.text && timelabel_bottom && !vertical_timelabel)
/* offset is subtracted because if . 0, the margin is smaller */
timebot_textheight = (int) ((timelin - timelabel.yoffset) * (t->v_char));
else
timebot_textheight = 0;
/* compute ybot from the various components
* unless bmargin is explicitly specified */
ybot = (int) ((t->ymax) * yoffset);
if (bmargin < 0) {
ybot += (timebot_textheight > xlabel_textheight ? timebot_textheight : xlabel_textheight) + xtic_height + xtic_textheight;
if (ybot == (t->ymax) * yoffset) {
/* make room for the end of rotated ytics or y2tics */
ybot += (int) ((t->h_char) * 2);
}
} else
ybot += (int) (bmargin * (t->v_char));
/* end of preliminary ybot calculation }}} */
#define KEY_PANIC(x) if (x) { lkey = 0; goto key_escape; }
if (lkey) {
/*{{{ essential key features */
p_width = pointsize * t->h_tic;
p_height = pointsize * t->v_tic;
if (key_swidth >= 0)
key_sample_width = key_swidth * (t->h_char) + p_width;
else
key_sample_width = 0;
key_entry_height = p_height * 1.25 * key_vert_factor;
if (key_entry_height < (t->v_char))
key_entry_height = (t->v_char) * key_vert_factor;
/* count max_len key and number keys with len > 0 */
max_ptitl_len = find_maxl_keys(plots, count, &ptitl_cnt);
if ((ytlen = label_width(key_title, &ktitl_lines)) > max_ptitl_len)
max_ptitl_len = ytlen;
if (key_reverse) {
key_sample_left = -key_sample_width;
key_sample_right = 0;
/* if key width is being used, adjust right-justified text */
key_text_left = t->h_char;
key_text_right = (t->h_char) * (max_ptitl_len + 1 + key_width_fix);
key_size_left = t->h_char - key_sample_left; /* sample left is -ve */
key_size_right = key_text_right;
} else {
key_sample_left = 0;
key_sample_right = key_sample_width;
/* if key width is being used, adjust left-justified text */
key_text_left = -(int) ((t->h_char) * (max_ptitl_len + 1 + key_width_fix));
key_text_right = -(int) (t->h_char);
key_size_left = -key_text_left;
key_size_right = key_sample_right + t->h_char;
}
key_point_offset = (key_sample_left + key_sample_right) / 2;
/* advance width for cols */
key_col_wth = key_size_left + key_size_right;
key_rows = ptitl_cnt;
key_cols = 1;
/* calculate rows and cols for key - if something goes wrong,
* the tidiest way out is to set lkey = 0, and a goto
*/
if (lkey == -1) {
if (key_vpos == TUNDER) {
/* maximise no cols, limited by label-length */
key_cols = (int) (xright - xleft) / key_col_wth;
KEY_PANIC(key_cols == 0);
key_rows = (int) (ptitl_cnt + key_cols - 1) / key_cols;
KEY_PANIC(key_rows == 0);
/* now calculate actual no cols depending on no rows */
key_cols = (int) (ptitl_cnt + key_rows - 1) / key_rows;
KEY_PANIC(key_cols == 0);
key_col_wth = (int) (xright - xleft) / key_cols;
/* we divide into columns, then centre in column by considering ratio
* of key_left_size to key_right_size
* key_size_left/(key_size_left+key_size_right) * (xright-xleft)/key_cols
* do one integer division to maximise accuracy (hope we dont overflow !)
*/
key_xl = xleft - key_size_left + ((xright - xleft) * key_size_left) / (key_cols * (key_size_left + key_size_right));
key_xr = key_xl + key_col_wth * (key_cols - 1) + key_size_left + key_size_right;
key_yb = t->ymax * yoffset;
key_yt = key_yb + key_rows * key_entry_height + ktitl_lines * t->v_char;
ybot += key_entry_height * key_rows + (int) ((t->v_char) * (ktitl_lines + 1));
} else {
/* maximise no rows, limited by ytop-ybot */
int i = (int) (ytop - ybot - (ktitl_lines + 1) * (t->v_char)) / key_entry_height;
KEY_PANIC(i == 0);
if (ptitl_cnt > i) {
key_cols = (int) (ptitl_cnt + i - 1) / i;
/* now calculate actual no rows depending on no cols */
KEY_PANIC(key_cols == 0);
key_rows = (int) (ptitl_cnt + key_cols - 1) / key_cols;
}
}
/* come here if we detect a division by zero in key calculations */
key_escape:
; /* ansi requires this */
}
/*}}} */
}
/*{{{ set up y and y2 tics */
{
/* setup_tics allows max number of tics to be specified
* but users dont like it to change with size and font,
* so we use value of 20, which is 3.5 behaviour.
* Note also that if format is '', yticlin = 0, so this gives
* division by zero.
* int guide = (ytop-ybot)/term->v_char;
*/
if (ytics)
setup_tics(FIRST_Y_AXIS, &yticdef, yformat, 20 /*(int) (guide/yticlin) */ );
if (y2tics)
setup_tics(SECOND_Y_AXIS, &y2ticdef, y2format, 20 /*(int) (guide/y2ticlin) */ );
}
/*}}} */
/*{{{ recompute xleft based on widths of ytics, ylabel etc
unless it has been explicitly set by lmargin */
/* tic labels */
if (ytics & TICS_ON_BORDER) {
if (vertical_ytics)
/* HBB: we will later add some white space as part of this, so
* reserve two more rows (one above, one below the text ...).
* Same will be done to similar calc.'s elsewhere */
ytic_textwidth = (int) ((t->v_char) * (yticlin + 2));
else {
widest_tic = 0; /* reset the global variable ... */
/* get gen_tics to call widest2d_callback with all labels
* the latter sets widest_tic to the length of the widest one
* ought to consider tics on axis if axis near border...
*/
gen_tics(FIRST_Y_AXIS, &yticdef, 0, 0, 0.0, widest2d_callback);
ytic_textwidth = (int) ((t->h_char) * (widest_tic + 2));
}
} else {
ytic_textwidth = 0;
}
/* tics */
if (!tic_in && ((ytics & TICS_ON_BORDER) || ((y2tics & TICS_MIRROR) && (y2tics & TICS_ON_BORDER))))
ytic_width = (int) ((t->h_tic) * ticscale);
else
ytic_width = 0;
/* ylabel */
if (*ylabel.text && can_rotate)
ylabel_textwidth = (int) ((ylablin + ylabel.xoffset) * (t->v_char));
else
ylabel_textwidth = 0;
/* timestamp */
if (*timelabel.text && vertical_timelabel)
timelabel_textwidth = (int) ((timelin + timelabel.xoffset) * (t->v_char));
else
timelabel_textwidth = 0;
/* compute xleft from the various components
* unless lmargin is explicitly specified */
xleft = (int) ((t->xmax) * xoffset);
if (lmargin < 0) {
xleft += (timelabel_textwidth > ylabel_textwidth ? timelabel_textwidth : ylabel_textwidth)
+ ytic_width + ytic_textwidth;
if (xleft == (t->xmax) * xoffset) {
/* make room for end of xtic or x2tic label */
xleft += (int) ((t->h_char) * 2);
}
} else
xleft += (int) (lmargin * (t->h_char));
/* make sure xleft is wide enough for a negatively x-offset horizontal timestamp */
if (!vertical_timelabel && xleft - ytic_width - ytic_textwidth < -(int) (timelabel.xoffset * (t->h_char)))
xleft = ytic_width + ytic_textwidth - (int) (timelabel.xoffset * (t->h_char));
/* end of xleft calculation }}} */
/*{{{ recompute xright based on widest y2tic. y2labels, key TOUT
unless it has been explicitly set by rmargin */
/* tic labels */
if (y2tics & TICS_ON_BORDER) {
if (vertical_y2tics)
y2tic_textwidth = (int) ((t->v_char) * (y2ticlin + 2));
else {
widest_tic = 0; /* reset the global variable ... */
/* get gen_tics to call widest2d_callback with all labels
* the latter sets widest_tic to the length of the widest one
* ought to consider tics on axis if axis near border...
*/
gen_tics(SECOND_Y_AXIS, &y2ticdef, 0, 0, 0.0, widest2d_callback);
y2tic_textwidth = (int) ((t->h_char) * (widest_tic + 2));
}
} else {
y2tic_textwidth = 0;
}
/* tics */
if (!tic_in && ((y2tics & TICS_ON_BORDER) || ((ytics & TICS_MIRROR) && (ytics & TICS_ON_BORDER))))
y2tic_width = (int) ((t->h_tic) * ticscale);
else
y2tic_width = 0;
/* y2label */
if (can_rotate && *y2label.text)
/* offset is subtracted because if > 0, the margin is smaller */
/* HBB: removed the (superfluous?) the '+1' */
y2label_textwidth = (int) ((y2lablin - y2label.yoffset) * (t->v_char));
else
y2label_textwidth = 0;
/* compute xright from the various components
* unless rmargin is explicitly specified */
xright = (int) ((t->xmax) * (xsize + xoffset));
if (rmargin < 0) {
xright -= y2label_textwidth + y2tic_width + y2tic_textwidth;
/* adjust for outside key */
if (lkey == -1 && key_hpos == TOUT) {
xright -= key_col_wth * key_cols;
key_xl = xright + (int) (t->h_tic);
}
if (xright == (t->xmax) * (xsize + xoffset)) {
/* make room for end of xtic or x2tic label */
xright -= (int) ((t->h_char) * 2);
}
} else
xright -= (int) (rmargin * (t->h_char));
/* end of xright calculation }}} */
if (aspect_ratio != 0.0) {
double current_aspect_ratio;
if (aspect_ratio < 0 && (max_array[x_axis] - min_array[x_axis]) != 0.0) {
current_aspect_ratio = -aspect_ratio * (max_array[y_axis] - min_array[y_axis]) / (max_array[x_axis] - min_array[x_axis]);
} else
current_aspect_ratio = aspect_ratio;
/*{{{ set aspect ratio if valid and sensible */
if (current_aspect_ratio >= 0.01 && current_aspect_ratio <= 100.0) {
double current = ((double) (ytop - ybot)) / ((double) (xright - xleft));
double required = (current_aspect_ratio * (double) t->v_tic) / ((double) t->h_tic);
if (current > required) {
/* too tall */
ytop = ybot + required * (xright - xleft);
} else {
/* HBB: y2label_x wasn't defined yet, and would be overwritten later */
xright = xleft + (ytop - ybot) / required;
}
}
/*}}} */
}
/*{{{ set up x and x2 tics */
/* we should base the guide on the width of the xtics, but we cannot
* use widest_tics until tics are set up. Bit of a downer - let us
* assume tics are 5 characters wide
*/
{
/* see equivalent code for ytics above
* int guide = (xright - xleft) / (5*t->h_char);
*/
if (xtics)
setup_tics(FIRST_X_AXIS, &xticdef, xformat, 20 /*guide */ );
if (x2tics)
setup_tics(SECOND_X_AXIS, &x2ticdef, x2format, 20 /*guide */ );
}
/*}}} */
/* adjust top and bottom margins for tic label rotation */
if (tmargin < 0 && x2tics & TICS_ON_BORDER && vertical_x2tics) {
widest_tic = 0; /* reset the global variable ... */
gen_tics(SECOND_X_AXIS, &x2ticdef, 0, 0, 0.0, widest2d_callback);
/* HBB: redid this: remove rough guess value first. Among other reasons,
* I suspected the '-4 lines' of the original code to be in error, as the
* original calc. of x2tic_textheight uses *5* lines */
ytop += x2tic_textheight;
/* Now compute a new one and use that instead: */
x2tic_textheight = (int) ((t->h_char) * (widest_tic));
ytop -= x2tic_textheight;
}
if (bmargin < 0 && xtics & TICS_ON_BORDER && vertical_xtics) {
widest_tic = 0; /* reset the global variable ... */
gen_tics(FIRST_X_AXIS, &xticdef, 0, 0, 0.0, widest2d_callback);
/* HBB: same changes as for tmargin/ytop above */
ybot -= xtic_textheight;
xtic_textheight = (int) ((t->h_char) * widest_tic);
ybot += xtic_textheight;
}
/* compute coordinates for axis labels, title et al
* (some of these may not be used) */
x2label_y = ytop + x2tic_height + x2tic_textheight + x2label_textheight;
title_y = x2label_y + title_textheight;
ylabel_y = ytop + x2tic_height + x2tic_textheight + ylabel_textheight;
y2label_y = ytop + x2tic_height + x2tic_textheight + y2label_textheight;
xlabel_y = ybot - xtic_height - xtic_textheight + (int) (xlabel.yoffset * (t->v_char));
ylabel_x = xleft - ytic_width - ytic_textwidth;
if (can_rotate && *ylabel.text) {
ylabel_x -= ylabel_textwidth + (int) ((ylabel.xoffset) * (t->h_char));
#if 0
/* HBB: iff the calc. of xleft and friends is correct, this *must*
* be wrong: at this point, xleft would be exactly zero (if origin x is
* zero and lmargin unset). Subtracting something from it thus ends up with
* a *negative* x, which is, simply put, rubbish. Actually, I think this
* originally was a kludge for the (non-functional, anyway) vertical ytic
* labelling code, as the width of that 'box' was calculated wrongly.
* But moving the ylabel out of the way is the wrong way to fix that. */
if (vertical_ytics)
ylabel_x -= (int) (t->v_char);
#endif
}
y2label_x = xright + y2tic_width + y2tic_textwidth;
if (can_rotate && *y2label.text) {
#if 1
/* HBB: my version of this */
y2label_x += (int) ((y2label.xoffset) * (t->h_char));
#else
/* HBB: this one's even worse: text will be printed *top_justified*,
* so y2label_x should be the *top* of the text box. This calculation
* would give the bottom, instead: */
y2label_x += y2label_textwidth + (int) ((y2label.xoffset) * (t->h_char));
/* HBB: see above for this... */
if (vertical_y2tics)
y2label_x += (int) (t->v_char);
#endif
}
if (vertical_timelabel) {
if (timelabel_bottom)
time_y = xlabel_y;
else
time_y = title_y;
} else {
if (timelabel_bottom)