-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdraw.c
More file actions
2407 lines (2228 loc) · 62.2 KB
/
draw.c
File metadata and controls
2407 lines (2228 loc) · 62.2 KB
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
/*
* Copyright (c) 1991, 1992, 1994, 1996, 1997
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Lawrence Berkeley Laboratory,
* Berkeley, CA. The name of the University may not be used to
* endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* This code is derived from the original X10 xgraph written by
* David Harrison University of California, Berkeley 1986, 1987
*
* Heavily hacked by Van Jacobson and Steven McCanne, UCB/LBL: added mouse
* functions to id points and compute slopes and distances. added keystroke
* commands to toggle most of the visual attributes of a window (grid, line,
* markers, etc.).
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
#include "flags.h"
#include "xwin.h"
#include "dataset.h"
#ifdef notdef
#include "bitmaps/dot.11"
#endif
#include "bitmaps/mark1.11"
#include "bitmaps/mark2.11"
#include "bitmaps/mark3.11"
#include "bitmaps/mark4.11"
#include "bitmaps/mark5.11"
#include "bitmaps/mark6.11"
#include "bitmaps/mark7.11"
#include "bitmaps/mark8.11"
#include "bitmaps/mark9.11"
extern void WriteValue(char *str, double val, int expn);
extern int DataSetHidden(LocalWin *wi, int setno);
extern Window NewWindow(BBox *bbp, struct plotflags *flags, LocalWin *parent);
extern int GetColor(char *name);
extern void xSetWindowName(char* name);
extern char *progname;
int TransformCompute(LocalWin *wi);
/* All marks have same dimensions. */
#define mark_w mark1_width
#define mark_h mark1_height
#define mark_center_x mark1_y_hot
#define mark_center_y mark1_x_hot
static char *marks[] = {
mark1_bits,
mark2_bits,
mark9_bits,
mark3_bits,
mark4_bits,
mark5_bits,
mark6_bits,
mark7_bits,
mark8_bits,
};
#define MARKNO(n) ((n) % (sizeof(marks) / sizeof(marks[0])))
#define SPACE 10
#define TICKLENGTH 5
#define PADDING 2
#define LABELPAD 5
#define PIXVALUE(set) (AllAttrs[(set) % MAXATTR].pixelValue)
static int pad = TICKLENGTH + PADDING + mark_w/2; /* pad inside outline */
static char *date_formats[] = {"", "%Y", "%Y-%m-%d", "%Y-%m-%d %H:%M:",
"%Y-%m-%d %H:%M:%S."};
/* The following used to be defined in terms of the maximum and minimum
* value a "short" can hold. But using those values can tickle bugs in
* the X server, which is in the habit of adding offsets to coordinates
* and assuming that the result still fits in a short. So we use quite
* conservative values. As long as these are significantly larger than
* the number of pixels on a screen, they should suffice.
*/
#define MAX_X11_COOR 16384
#define MIN_X11_COOR -16384
/*
* To properly clip the line between a visible point and one which is outside
* the X11 coordinate space we need to map the invisible point to the point on
* the boundary where that line intersects. To calculate that boundary point
* we need to have the coordinates of the visible point (op) as well as the
* invisible point (vp). The second clip calculation in each of the two cases
* should only rarely happen for points near the corner of the coordinate
* space.
*
* Since a line between two invisible points may also cross the visible space,
* this function may also be called with two invisible points. This requires
* the code to protect itself against various overflow conditions. The result
* should always be a point within the coordinate space.
*
* The return value is True if vp was beyond the X11 coordinate space.
*/
static inline int
SCREENXY(LocalWin *ws, Value *vp, Value *op, XPoint *xp)
{
double x = ws->XOrgX +
(vp->x - ws->UsrOrgX) / ws->XUnitsPerPixel;
double y = ws->XOppY -
(vp->y - ws->UsrOrgY) / ws->YUnitsPerPixel;
double xabs = fabs(x);
double yabs = fabs(y);
int beyond = False;
if (xabs > MAX_X11_COOR || yabs > MAX_X11_COOR) {
double ox = ws->XOrgX +
(op->x - ws->UsrOrgX) / ws->XUnitsPerPixel;
double oy = ws->XOppY -
(op->y - ws->UsrOrgY) / ws->YUnitsPerPixel;
double mx, my;
beyond = True;
if (xabs > yabs) {
mx = copysign(MAX_X11_COOR, x);
if (x == ox || fabs(my = (y - oy) * (mx - ox) / (x - ox) + oy) > MAX_X11_COOR) {
my = copysign(MAX_X11_COOR, y);
if (y == oy || fabs(mx = (x - ox) * (my - oy) / (y - oy) + ox) > MAX_X11_COOR)
mx = copysign(MAX_X11_COOR, x);
}
} else {
my = copysign(MAX_X11_COOR, y);
if (y == oy || fabs(mx = (x - ox) * (my - oy) / (y - oy) + ox) > MAX_X11_COOR) {
mx = copysign(MAX_X11_COOR, x);
if (x == ox || fabs(my = (y - oy) * (mx - ox) / (x - ox) + oy) > MAX_X11_COOR)
my = copysign(MAX_X11_COOR, y);
}
}
x = mx;
y = my;
}
xp->x = rint(x);
xp->y = rint(y);
return beyond;
}
/*
* SCREENX and SCREENY are only valid when the points are known to be within
* the X11 coordinate limits. Otherwise, SCREENXY must be used in order to
* properly clip.
*/
static inline int
SCREENX(LocalWin *ws, double userX)
{
return ws->XOrgX +
rint((userX - ws->UsrOrgX) / ws->XUnitsPerPixel);
}
static inline int
SCREENY(LocalWin *ws, double userY)
{
return ws->XOppY -
rint((userY - ws->UsrOrgY) / ws->YUnitsPerPixel);
}
static inline int
SCREENXS(LocalWin *ws, double userX)
{
double x = userX / ws->XUnitsPerPixel;
if (x < MIN_X11_COOR)
return MIN_X11_COOR;
if (x > MAX_X11_COOR)
return MAX_X11_COOR;
return rint(x);
}
static inline int
SCREENYS(LocalWin *ws, double userY)
{
double y = userY / ws->YUnitsPerPixel;
if (y < MIN_X11_COOR)
return MIN_X11_COOR;
if (y > MAX_X11_COOR)
return MAX_X11_COOR;
return rint(y);
}
#define TRANX(xval) \
(((double) ((xval) - wi->XOrgX)) * wi->XUnitsPerPixel + wi->UsrOrgX)
#define TRANY(yval) \
(wi->UsrOppY - (((double) ((yval) - wi->XOrgY)) * wi->YUnitsPerPixel))
#define nlog10(x) (x == 0.0 ? 0.0 : log10(x))
static GC echoGC;
static GC fitGC;
static GC textGC;
static GC text2GC;
static GC text3GC;
static GC infoGC;
static GC titleGC;
static GC copyGC;
double slope_scale = 1.0;
char *graph_title = NULL;
static int maxName = 0;
static int
WriteDate(str, val)
char *str; /* String to write into */
double val; /* Value to print */
{
time_t xsec = (time_t)lround(val);
int usec = (int)fmod(round(val*1000000), 1000000);
struct tm xtm;
if (localTime)
localtime_r(&xsec, &xtm);
else
gmtime_r(&xsec, &xtm);
if (xtm.tm_hour == 0 && xtm.tm_min == 0 && xtm.tm_sec == 0 &&
usec == 0) {
if (xtm.tm_mon == 0 && xtm.tm_mday == 1) {
if (str)
strftime(str, 6, "%Y", &xtm);
return 1;
} else {
if (str)
strftime(str, 6, "%m/%d", &xtm);
return 2;
}
} else if (lround(fmod(val, 60) * 1000000) == 0) {
if (str)
strftime(str, 6, "%H:%M", &xtm);
return 3;
} else if (fmod(round(val*1000000), 1000) == 0.0) {
if (str)
(void)sprintf(str, "%06.3f", fmod(val, 60));
return 4;
} else {
if (str)
(void)sprintf(str, "%06u", usec);
return 5;
}
}
/*
* This routine draws grid line labels in engineering notation, the grid
* lines themselves, and unit labels on the axes.
*/
static void
DrawGridAndAxis(Window win, LocalWin *wi)
{
int expX, expY; /* Engineering powers */
int exp;
int y, x, w, n;
int height;
int len;
int textLevelOffset = 0;
int isDST = 0;
int Xmonths = 0;
int Xweeks = 0;
double Xincr, Yincr, Xstart, Ystart, Yindex, Xindex, larger;
double Xoffset, Yoffset, Xbase, Ybase, Xend, Yend;
char value[32];
double RoundUp();
double MaskDigit();
/*
* Grid display powers are computed by taking the log of the largest
* numbers and rounding down to the nearest multiple of 3.
*/
if (fabs(wi->UsrOrgX) > fabs(wi->UsrOppX))
larger = fabs(wi->UsrOrgX);
else
larger = fabs(wi->UsrOppX);
expX = ((int) floor(nlog10(larger) / 3.0)) * 3;
if (fabs(wi->UsrOrgY) > fabs(wi->UsrOppY))
larger = fabs(wi->UsrOrgY);
else
larger = fabs(wi->UsrOppY);
expY = ((int) floor(nlog10(larger) / 3.0)) * 3;
/*
* The grid line increments are determined by the number of labels
* that will fit in the vertical and horizontal space.
*/
height = axisFont->ascent + axisFont->descent;
Yincr = (SPACE + height) * wi->YUnitsPerPixel;
Yincr = RoundUp(Yincr);
if (dateXFlag)
w = XTextWidth(axisFont, "000000", 6); /* > 00.000 or HH:MM */
else
w = XTextWidth(axisFont, "-000.00", 7);
Xincr = (SPACE + w) * wi->XUnitsPerPixel;
if (!dateXFlag)
Xincr = RoundUp(Xincr);
/*
* If the grid line labels will not have sufficient resolution in
* two decimal places to represent the grid line increment, then
* adjust the grid display powers and change the grid line labels
* to be offsets from a starting value to be shown in the axis label.
* The offset value is masked to show up to two integer digits in the
* grid label, with a possible third digit for overflow, to simplify
* mental addition of the offset and the label.
*/
Ystart = ceil(wi->UsrOrgY / Yincr) * Yincr;
if (Ystart == -0.0) Ystart = 0.0;
Yoffset = 0.0;
exp = (int) floor(nlog10(Yincr));
if (!logYFlag && exp < expY - 2) {
wi->YPrecisionOffset = ((expY - exp) / 3) * 3;
expY -= wi->YPrecisionOffset;
Yoffset = Ystart;
if (Ystart < 0) {
n = (int) floor((wi->UsrOppY - Ystart) / Yincr);
Yoffset += n * Yincr;
}
Ybase = MaskDigit(&Yoffset, expY + 2) * 100.0;
Ystart -= Yoffset;
}
if (!dateXFlag) {
Xstart = ceil(wi->UsrOrgX / Xincr) * Xincr;
if (Xstart == -0.0) Xstart = 0.0;
Xoffset = 0.0;
exp = (int) floor(nlog10(Xincr));
if (!logXFlag && exp < expX - 2) {
wi->XPrecisionOffset = ((expX - exp) / 3) * 3;
expX -= wi->XPrecisionOffset;
Xoffset = Xstart;
if (Xstart < 0) {
n = (int) floor((wi->UsrOppX - Xstart) / Xincr);
Xoffset += n * Xincr;
}
Xbase = MaskDigit(&Xoffset, expX + 2) * 100.0;
Xstart -= Xoffset;
}
}
/*
* For date labels on the X axis, find what span of dates is covered in
* order to determine the level of detail in the labels: 2015 12/31
* 23:59 59.999 999999. For any given tick mark, the level used will
* be the lowest that is not zero or January 1.
*/
if (dateXFlag) {
if (Xincr > 60*60) {
time_t step = 24*60*60;
if (Xincr > 12*60*60) {
Xincr = floor((Xincr+step-1)/step)*step;
if (Xincr > 14*24*60*60) {
Xmonths = 1;
if (Xincr > 28*24*60*60 &&
Xincr < 32*24*60*60)
Xincr = 32*24*60*60;
if (Xincr > 30*24*60*60)
Xmonths = (int) ceil(Xincr /
(30.5*24*60*60));
} else if (Xincr > 5*24*60*60) {
Xweeks = 1;
if (Xincr > 7*24*60*60)
Xincr = 14*24*60*60;
else
Xincr = 7*24*60*60;
}
} else {
if (Xincr > 4*60*60) {
Xincr = 12*60*60;
} else if (Xincr > 2*60*60) {
Xincr = 4*60*60;
} else {
Xincr = 2*60*60;
}
step = Xincr;
}
time_t xsec = (time_t)wi->UsrOrgX;
xsec += step - 1;
struct tm xtm;
if (localTime)
localtime_r(&xsec, &xtm);
else
gmtime_r(&xsec, &xtm);
if (Xweeks && xtm.tm_wday != 0) {
xtm.tm_mday += 7 - xtm.tm_wday;
}
if (Xmonths && xtm.tm_mday != 1) {
xtm.tm_mday = 1;
++xtm.tm_mon;
}
/* Make Xmonths > 4 be a multiple of 6 to see years */
if (Xmonths > 4)
Xmonths = ((Xmonths + 5) / 6) * 6;
/* Align to Xmonths boundary */
if (Xmonths > 1) {
int rem = xtm.tm_mon % Xmonths;
if (rem != 0) {
xtm.tm_mon += Xmonths - rem;
if (xtm.tm_mon > 11) {
xtm.tm_mon -= 12;
++xtm.tm_year;
}
}
}
xtm.tm_hour -= xtm.tm_hour % (step/(60*60));
xtm.tm_min = xtm.tm_sec = 0;
xtm.tm_isdst = -1;
Xstart = localTime ? mktime(&xtm) : timegm(&xtm);
} else if (Xincr > 1) {
if (Xincr > 30*60) {
Xincr = 60*60;
} else if (Xincr > 10*60) {
Xincr = 30*60;
} else if (Xincr > 5*60) {
Xincr = 10*60;
} else if (Xincr > 60) {
Xincr = 5*60;
} else if (Xincr > 60) {
Xincr = 5*60;
} else if (Xincr > 30) {
Xincr = 60;
} else if (Xincr > 10) {
Xincr = 30;
} else if (Xincr > 5) {
Xincr = 10;
} else if (Xincr > 2) {
Xincr = 5;
} else
Xincr = 2;
Xstart = ceil(wi->UsrOrgX / Xincr) * Xincr;
} else {
Xincr = fmax(RoundUp(Xincr), 0.000001);
Xstart = ceil(wi->UsrOrgX / Xincr) * Xincr;
}
if (localTime) {
time_t xsec = (time_t)Xstart;
struct tm xtm;
localtime_r(&xsec, &xtm);
isDST = xtm.tm_isdst;
}
Xoffset = 0.0;
expX = 9;
exp = (int) floor(nlog10(Xincr));
wi->XPrecisionOffset = ((expX - exp) / 3) * 3;
}
/*
* With the powers computed, we can draw the axis labels.
*/
if (expY || logYFlag || Yoffset != 0.0) {
char powerbuf[100];
char *power = powerbuf;
if (logYFlag) {
if (expY || Yoffset != 0.0)
power += sprintf(power, " x 10^(y");
else
power += sprintf(power, " x 10^y");
}
if (expY) {
if (Yoffset == 0.0)
power += sprintf(power, " x 10^%d", expY);
else
power += sprintf(power, " + %.0f x 10^%d",
Ybase, expY);
} else if (Yoffset != 0.0)
power += sprintf(power, " + %.0f", Ybase);
if (logYFlag && (expY || Yoffset != 0.0))
power += sprintf(power, ")");
XDrawString(display, win, textGC, w/2, axisFont->ascent+PADDING,
powerbuf, strlen(powerbuf));
}
if (!dateXFlag && (expX || logXFlag || Xoffset != 0.0)) {
char powerbuf[100];
char *power = powerbuf;
if (logXFlag) {
if (expX || Xoffset != 0.0)
power += sprintf(power, " x 10^(X");
else
power += sprintf(power, " x 10^x");
}
if (expX) {
if (Xoffset == 0.0)
power += sprintf(power, " x 10^%d", expX);
else
power += sprintf(power, " + %.0f x 10^%d",
Xbase, expX);
} else if (Xoffset != 0.0)
power += sprintf(power, " + %.0f", Xbase);
if (logXFlag && (expX || Xoffset != 0.0))
power += sprintf(power, ")");
len = strlen(powerbuf);
x = wi->width - XTextWidth(axisFont, powerbuf, len) - 17;
if (x > wi->XOppX)
x = wi->XOppX;
y = wi->height - PADDING;
XDrawString(display, win, textGC, x, y, powerbuf, len);
}
/*
* For X axis labels in date/time mode, determine how many levels of
* gray we should use to distinguish labels of different resolutions.
*/
if (dateXFlag) {
time_t xsmin = (time_t)wi->UsrOrgX;
time_t xsmax = (time_t)wi->UsrOppX;
struct tm xtmin, xtmax;
if (localTime) {
localtime_r(&xsmin, &xtmin);
localtime_r(&xsmax, &xtmax);
} else {
gmtime_r(&xsmin, &xtmin);
gmtime_r(&xsmax, &xtmax);
}
if (xtmin.tm_year == xtmax.tm_year) {
++textLevelOffset;
if (xtmin.tm_yday == xtmax.tm_yday) {
++textLevelOffset;
if (xtmin.tm_hour == xtmax.tm_hour &&
xtmin.tm_min == xtmax.tm_min) {
++textLevelOffset;
if (xtmin.tm_sec == xtmax.tm_sec)
++textLevelOffset;
}
}
} else if (!Xmonths) {
/* Determine if no grid line at New Year's */
xtmax.tm_sec = xtmax.tm_min = xtmax.tm_hour = 0;
xtmax.tm_mday = 1;
xtmax.tm_mon = 0;
xtmax.tm_yday = 0;
double newyears = localTime ? mktime(&xtmax) :
timegm(&xtmax);
if (fmod(newyears - Xstart, Xincr) != 0)
++textLevelOffset;
}
}
/*
* Now, we can figure out the grid line labels and grid lines
*/
Yend = wi->UsrOppY - Yoffset;
for (Yindex = Ystart; Yindex <= Yend; Yindex += Yincr) {
y = SCREENY(wi, Yindex + Yoffset);
/* Write the axis label */
WriteValue(value, Yindex, expY);
len = strlen(value);
x = wi->XOrgX - XTextWidth(axisFont, value, len) - PADDING;
XDrawString(display, win, textGC, x, y + axisFont->ascent/2,
value, len);
/* Draw the grid line or tick mark */
if (wi->flags.tick) {
XDrawLine(display, win, textGC, wi->XOrgX, y,
wi->XOrgX + TICKLENGTH, y);
XDrawLine(display, win, textGC, wi->XOppX - TICKLENGTH,
y, wi->XOppX, y);
} else
XDrawLine(display, win, textGC, wi->XOrgX, y,
wi->XOppX, y);
}
y = wi->height - height;
int Xnum = 0;
double Xshift = 0;
double Xlast = 0;
Xend = wi->UsrOppX - Xoffset;
for (Xindex = Xstart; Xindex <= Xend; ++Xnum) {
x = SCREENX(wi, Xindex + Xoffset);
Xlast = Xindex;
/* Write the axis label */
int textLevel = 1;
if (dateXFlag)
textLevel = WriteDate(value, Xindex);
else
WriteValue(value, Xindex, expX);
len = strlen(value);
w = XTextWidth(axisFont, value, len);
textLevel -= textLevelOffset;
GC gc = textLevel <= 1 ? textGC :
textLevel == 2 ? text2GC : text3GC;
XDrawString(display, win, gc, x - w/2, y,
value, len);
/* Draw the grid line or tick marks */
if (wi->flags.tick) {
XDrawLine(display, win, textGC, x, wi->XOrgY,
x, wi->XOrgY + TICKLENGTH);
XDrawLine(display, win, textGC, x,
wi->XOppY - TICKLENGTH, x, wi->XOppY);
} else
XDrawLine(display, win, textGC, x, wi->XOrgY, x,
wi->XOppY);
/* Advance to the next grid line */
if (!Xmonths)
Xindex = Xstart + Xincr*Xnum + Xshift; /* Avoid error accum. */
if (dateXFlag) {
time_t xsec = (time_t)Xindex;
struct tm xtm;
if (localTime)
localtime_r(&xsec, &xtm);
else
gmtime_r(&xsec, &xtm);
if (Xmonths) {
xtm.tm_mon += Xmonths;
if (xtm.tm_mon > 11) {
xtm.tm_mon -= 12;
++xtm.tm_year;
}
xtm.tm_isdst = -1;
Xindex = localTime ? mktime(&xtm) :
timegm(&xtm);
} else {
if (localTime && (Xincr > 60*60)) {
if (isDST && !xtm.tm_isdst) {
Xshift += 60*60;
Xindex += 60*60;
} else if (!isDST && xtm.tm_isdst) {
Xshift -= 60*60;
Xindex -= 60*60;
if (Xincr < 4*60*60)
Xindex += Xincr;
}
}
}
if (localTime) {
xsec = (time_t)Xindex;
localtime_r(&xsec, &xtm);
isDST = xtm.tm_isdst;
}
}
}
/*
* Add the second line of the start and end date labels now that we
* know where the end label goes.
*/
if (dateXFlag) {
time_t xsec = (time_t)Xstart;
struct tm xtm;
int width;
char datebuf[MAXIDENTLEN];
char *labptr = datebuf;
char *format = date_formats[WriteDate(0, Xstart) - 1];
if (localTime)
localtime_r(&xsec, &xtm);
else
gmtime_r(&xsec, &xtm);
labptr += strftime(labptr, MAXIDENTLEN, format, &xtm);
len = labptr - datebuf;
width = XTextWidth(axisFont, datebuf, len);
x = SCREENX(wi, Xstart) - width / 2;
if (x < 17)
x = 17;
y = wi->height - PADDING;
XDrawString(display, win, textGC, x, y, datebuf, len);
xsec = (time_t)Xlast;
labptr = datebuf;
format = date_formats[WriteDate(0, Xlast) - 1];
if (localTime)
localtime_r(&xsec, &xtm);
else
gmtime_r(&xsec, &xtm);
labptr = datebuf;
format = date_formats[WriteDate(0, Xlast) - 1];
labptr += strftime(labptr, MAXIDENTLEN, format, &xtm);
len = labptr - datebuf;
width = XTextWidth(axisFont, datebuf, len);
x = SCREENX(wi, Xlast) - width / 2;
if (x + width > wi->width - 17)
x = wi->width - width - 17;
XDrawString(display, win, textGC, x, y, datebuf, len);
}
/* Center the title near the top of the graph */
if (graph_title) {
char win_name[MAXIDENTLEN];
snprintf(win_name, MAXIDENTLEN, "%s - %s", progname,
graph_title);
xSetWindowName(win_name);
len = strlen(graph_title);
w = XTextWidth(titleFont, graph_title, len);
x = (wi->width - w) / 2;
if (x < 0)
x = 0;
XDrawImageString(display, win, titleGC, x,
titleFont->ascent + PADDING/2,
graph_title, len);
}
/* Check to see if he wants an outline */
if (wi->flags.outline)
XDrawRectangle(display, win, textGC, wi->clip.x, wi->clip.y,
wi->clip.width, wi->clip.height);
}
void
DrawSet(LocalWin *wi, Window win, struct data_set *p)
{
Value *vp, *bp, *ep, *op;
XPoint *ver, *verend;
double loX, loY, hiX, hiY;
#define MAXPOINTS 512
XPoint Xpoints[MAXPOINTS];
XRectangle Xrectangles[MAXPOINTS];
XRectangle *rec = Xrectangles;
struct plotflags* flags = &wi->dflags[p->setno];
int beyond;
loX = wi->UsrOrgX; loY = wi->UsrOrgY;
hiX = wi->UsrOppX; hiY = wi->UsrOppY;
vp = p->dvec;
ep = &p->dvec[p->numPoints];
ver = Xpoints;
verend = &Xpoints[MAXPOINTS];
op = vp;
XSetClipRectangles(display, (GC)p->GC, 0, 0,
&wi->clip, 1, Unsorted);
while (vp < ep) {
while (ver < verend && vp < ep) {
beyond = SCREENXY(wi, vp, op, ver);
if (flags->rectangle) {
int w = SCREENXS(wi, vp->w);
int h = SCREENYS(wi, vp->h);
if (w < 0) {
rec->width = -w;
rec->x = ver->x + w;
if (rec->x < MIN_X11_COOR) {
rec->width = MIN_X11_COOR -
rec->x;
rec->x = MIN_X11_COOR;
}
} else {
rec->width = w;
rec->x = ver->x;
}
if (h < 0) {
rec->height = -h;
rec->y = ver->y;
} else {
rec->height = h;
rec->y = ver->y - h;
if (rec->y < MIN_X11_COOR) {
rec->height = MIN_X11_COOR -
rec->y;
rec->y = MIN_X11_COOR;
}
}
++rec;
}
if (flags->mark && !flags->pixmarks &&
vp->x >= loX && vp->x <= hiX &&
vp->y >= loY && vp->y <= hiY) {
#ifdef notdef
XFillArc(display, win, (GC)p->GC,
ver->x - 3, ver->y - 3, 7, 7,
0, 360*64);
#endif
XSetClipOrigin(display, (GC)p->mGC,
ver->x - mark_center_x,
ver->y - mark_center_y);
XFillRectangle(display, win, (GC)p->mGC,
ver->x - mark_center_x,
ver->y - mark_center_y,
mark_w, mark_h);
}
/* Draw bar elements if requested */
if (flags->bar)
XDrawLine(display, win, (GC)p->GC,
ver->x, SCREENY(wi, 0.0),
ver->x, ver->y);
/* Draw error bars if requested */
if (flags->errorbar) {
int w = SCREENXS(wi, vp->w);
int h = SCREENYS(wi, vp->h);
int min, max;
if (w < 0)
w = -w;
min = ver->x - w/2;
max = ver->x + w/2;
if (min < MIN_X11_COOR)
min = MIN_X11_COOR;
if (max > MAX_X11_COOR)
max = MAX_X11_COOR;
XDrawLine(display, win, (GC)p->GC,
min, ver->y, max, ver->y);
if (h < 0)
h = -h;
min = ver->y - h/2;
max = ver->y + h/2;
if (min < MIN_X11_COOR)
min = MIN_X11_COOR;
if (max > MAX_X11_COOR)
max = MAX_X11_COOR;
XDrawLine(display, win, (GC)p->GC,
ver->x, min, ver->x, max);
}
/*
* If the last point was an invisible point being drawn
* a second time, then ignore the fact that it was
* invisible so we will move on to the next point.
*/
if (op > vp)
beyond = False;
/*
* Save this point as the "other point" in case the next
* point is out of the X11 coordinate space and needs to
* be clipped on the line from this point to it.
*/
op = vp;
/*
* If this point wasn't visible, spin until we find one
* that is not offscreen on the same edge. Then back
* up so the invisible point immediately before it will
* be drawn on the next loop so the line drawn between
* those two invisible points will be totally
* offscreen. We don't back up if the preceding
* invisible point is the one we just drew unless it
* was outside the X11 coordinate space in which case
* we need to draw it again clipped to the coordinate
* space boundary on the line to the next point (which
* we save as op). If the next point is visible, or
* the line between the points crosses the visible
* space, then the line from this point to the next
* point will be in the correct direction and clipped
* at the edge(s).
*/
bp = vp;
if (vp->x < loX && vp->x + vp->w < loX) {
while (vp->x < loX && vp->x + vp->w < loX &&
vp < ep)
++vp;
if (beyond)
op = vp;
if (vp < ep)
--vp;
} else if (vp->x > hiX && vp->x + vp->w > hiX) {
while (vp->x > hiX && vp->x + vp->w > hiX &&
vp < ep)
++vp;
if (beyond)
op = vp;
if (vp < ep)
--vp;
} else if (vp->y < loY && vp->y + vp->h < loY) {
while (vp->y < loY && vp->y + vp->h < loY &&
vp < ep)
++vp;
if (beyond)
op = vp;
if (vp < ep)
--vp;
} else if (vp->y > hiY && vp->y + vp->h > hiY) {
while (vp->y > hiY && vp->y + vp->h > hiY &&
vp < ep)
++vp;
if (beyond)
op = vp;
if (vp < ep)
--vp;
}
if (vp > bp || beyond)
--vp;
++vp;
++ver;
}
if (flags->rectangle) {
if (flags->rectangle > 1)
XFillRectangles(display, win, (GC)p->GC,
Xrectangles, rec - Xrectangles);
XDrawRectangles(display, win, (GC)p->GC,
Xrectangles, rec - Xrectangles);
}
if (!flags->nolines)
XDrawLines(display, win, (GC)p->GC, Xpoints,
ver - Xpoints, CoordModeOrigin);
else if (flags->mark && flags->pixmarks)
XDrawPoints(display, win, (GC)p->GC, Xpoints,
ver - Xpoints, CoordModeOrigin);
/*
* If we break data across two calls, continue it from the
* last point of the last chunk.
*/
Xpoints[0] = verend[-1];
ver = &Xpoints[1];
rec = Xrectangles;
}
XSetClipMask(display, (GC)p->GC, None);
}
/*
* This routine draws the data sets themselves using the macros for
* translating coordinates.
*/
static void
DrawData(LocalWin *wi, Window win)
{
register struct data_set *p;
for (p = datasets; p != 0; p = p->next) {
if (DataSetHidden(wi, p->setno))
continue;
DrawSet(wi, win, p);
}
}
/*
* This routine draws a single data set so it will be rendered on top of any
* others already drawn.
*/
void
DrawSetNo(LocalWin *wi, Window win, int setno)
{
register struct data_set *p;
for (p = datasets; p != 0; p = p->next) {
if (p->setno != setno)
continue;
DrawSet(wi, win, p);
if (wi->cache != 0)
XCopyArea(display, win, wi->cache, copyGC,
0, 0, wi->width, wi->height, 0, 0);
break;
}
}
/*
* This draws a legend of the data sets displayed. Only those that will fit
* are drawn.
*/
static void
DrawLegend(LocalWin *wi, Window win)
{
int spot, maxlen, len, height, x0, x1;
struct data_set *p;
height = axisFont->ascent + axisFont->descent;
maxlen = 0;
x0 = wi->XOppX + PADDING + mark_w;
x1 = x0 + maxName;
spot = wi->XOrgY + PADDING;
for (p = datasets; p != 0; p = p->next) {
XDrawLine(display, win, (GC)p->GC, x0, spot, x1, spot);
XDrawString(display, win, textGC, x0, spot + height + 2,
p->setName, strlen(p->setName));
XSetClipOrigin(display, p->mGC, x0 - mark_w,
spot + height - mark_h);
XFillRectangle(display, win, p->mGC, x0 - mark_w,
spot + height - mark_h, mark_w, mark_h);
spot += 2 * height + PADDING;
}
}
static char *help[] = {
"\bMouse Functions",
"",
"Hold shift-ctrl-left: show (X,Y) coordinates at cursor",
"Hold shift-left: if on a data point, also show set label, ordinal, comment",
"Hold left: also decode X coordinate as GMT/local timestamp if after 1999",
"Drag left: show a triangle labeled with delta X, delta Y and slope values",
" (displayed string is set as X11 primary selection to paste in some other",
" X11 window using middle mouse button)",
"",
"Drag middle: show rubber-band line with slope value",
"Hold shift-middle on data point: draw regression line, show formula for it",
" (formula set as X11 primary selection; window redraw erases the line)",
"",
"Drag right: create new window with zoomed view of points spanned",
"Drag shift-right: write xgraph.tmp containing subset of points spanned",
"",
"Scroll wheel up/down/left/right: pan the graph view",
"Shift scroll wheel up/down: zoom the graph view in/out at the cursor",
"",
"",
"\bKeystroke Commands",
"",