1
/* GDCHART 0.10.0dev GDCHART.C 2 Nov 2000 */
2
/* Copyright Bruce Verderaime 1998, 1999, 2000 */
25
#define MAXINT 99999999
28
// scaled translation onto graph
29
#define PX(x) (int)(xorig + (setno*xdepth_3D) + (x)*xscl)
30
#define PY(y) (int)(yorig - (setno*ydepth_3D) + (y)*yscl)
31
#define PV(y) (int)(vyorig - (setno*ydepth_3D) + (y)*vyscl)
33
#define SET_RECT(gdp, x1, x2, y1, y2) gdp[0].x = gdp[3].x = x1, \
34
gdp[0].y = gdp[1].y = y1, \
35
gdp[1].x = gdp[2].x = x2, \
36
gdp[2].y = gdp[3].y = y2
39
// -------------------------------------------------------------------
40
// draw an arrow at (x,y)-upper left in arrwclr to the size of SmallFont
41
// could, with just a little difficulty, be made to accept a font size
42
// -------------------------------------------------------------------
44
smallarrow(gdImagePtr im,
50
gdImageLine(im, x+2, y, x+2, y+GDC_fontc[GDC_SMALL].h, arrwclr);
51
gdImageLine(im, x+3, y, x+3, y+GDC_fontc[GDC_SMALL].h, arrwclr);
54
gdImageSetPixel(im, x, y+2, arrwclr); /* uuoouu */
55
gdImageSetPixel(im, x+1, y+2, arrwclr); /* oo */
56
gdImageSetPixel(im, x+4, y+2, arrwclr); /* oo */
57
gdImageSetPixel(im, x+5, y+2, arrwclr); /* oo */
58
gdImageSetPixel(im, x+1, y+1, arrwclr); /* oo */
59
gdImageSetPixel(im, x+4, y+1, arrwclr); /* oo */
63
gdImageSetPixel(im, x, y+(GDC_fontc[GDC_SMALL].h-2), arrwclr); /* oo */
64
gdImageSetPixel(im, x+1, y+(GDC_fontc[GDC_SMALL].h-2), arrwclr);
65
gdImageSetPixel(im, x+4, y+(GDC_fontc[GDC_SMALL].h-2), arrwclr);
66
gdImageSetPixel(im, x+5, y+(GDC_fontc[GDC_SMALL].h-2), arrwclr);
67
gdImageSetPixel(im, x+1, y+(GDC_fontc[GDC_SMALL].h-1), arrwclr);
68
gdImageSetPixel(im, x+4, y+(GDC_fontc[GDC_SMALL].h-1), arrwclr);
74
#define SET_3D_POLY(gdp, x1, x2, y1, y2, xoff, yoff) \
75
gdp[0].x = x1, gdp[0].y = y1, \
76
gdp[1].x = x1+(xoff), gdp[1].y = y1-yoff, \
77
gdp[2].x = x2+(xoff), gdp[2].y = y2-yoff, \
78
gdp[3].x = x2, gdp[3].y = y2
79
/* ------------------------------------------------------------------------- */
81
/* ref is front plane */
82
/* allows for intersecting 3D lines */
83
/* (also used for single 3D lines >:-Q */
84
struct YS { int y1; int y2; float slope; int lnclr; int shclr; };
85
static int qcmpr(const void *a, const void *b)
86
{ if(((struct YS*)a)->y2 < ((struct YS*)b)->y2) return 1;
87
if(((struct YS*)a)->y2 > ((struct YS*)b)->y2) return -1;
90
draw_3d_line(gdImagePtr im,
102
#define F(x,i) (int)((float)((x)-x1)*slope[i]+(float)y1[i])
103
float depth_slope = xdepth==0? MAXFLOAT: (float)ydepth/(float)xdepth;
104
float slope[num_sets];
110
struct YS ypts[num_sets];
112
for(i=0; i<num_sets; ++i)
114
// lnclr[i] = clr[i];
115
// shclr[i] = clrshd[i];
116
slope[i] = x2==x1? MAXFLOAT: (float)(y2[i]-y1[i])/(float)(x2-x1);
119
for(x=x1+1; x<=x2; ++x)
121
for(i=0; i<num_sets; ++i) // load set of points
123
ypts[i].y1 = F(x-1,i);
125
ypts[i].lnclr = clr[i];
126
ypts[i].shclr = clrshd[i];
127
ypts[i].slope = slope[i];
128
} // sorted "lowest" first
129
qsort(ypts, num_sets, sizeof(struct YS), qcmpr);
130
// put out in that order
131
for(i=0; i<num_sets; ++i)
133
SET_3D_POLY(poly, x-1, x, ypts[i].y1, ypts[i].y2, xdepth, ydepth);
134
gdImageFilledPolygon(im, poly, 4, // depth_slope ever < 0 ?
135
-ypts[i].slope>depth_slope? ypts[i].shclr: ypts[i].lnclr);
136
if(x == x1+1) // edging
139
x-1+xdepth, ypts[i].y2-ydepth,
140
-ypts[i].slope<=depth_slope? ypts[i].shclr: ypts[i].lnclr);
145
/* ------------------------------------------------------------------------- */
147
/* ref is front plane */
149
draw_3d_area(gdImagePtr im,
152
int y0, // drawn from 0
162
int y_intercept = 0; // if xdepth || ydepth
166
float line_slope = x2==x1? MAXFLOAT: (float)-(y2-y1) / (float)(x2-x1);
167
float depth_slope = xdepth==0? MAXFLOAT: (float)ydepth/(float)xdepth;
169
y_intercept = (y1 > y0 && y2 < y0) || // line crosses y0
170
(y1 < y0 && y2 > y0)?
171
(int)((1.0/ABS(line_slope))*(float)(ABS(y1-y0)))+x1:
174
// edging along y0 depth
175
gdImageLine(im, x1+xdepth, y0-ydepth, x2+xdepth, y0-ydepth, clrshd);
177
SET_3D_POLY(poly, x1, x2, y1, y2, xdepth, ydepth); // top
178
gdImageFilledPolygon(im, poly, 4, line_slope>depth_slope? clrshd: clr);
180
SET_3D_POLY(poly, x1, x2, y0, y0, xdepth, ydepth+1); // along y axis
181
gdImageFilledPolygon(im, poly, 4, clr);
183
SET_3D_POLY(poly, x2, x2, y0, y2, xdepth, ydepth); // side
184
gdImageFilledPolygon(im, poly, 4, clrshd);
187
gdImageLine(im, y_intercept, y0,
188
y_intercept+xdepth, y0-ydepth, clrshd); // edging
189
gdImageLine(im, x1, y0, x1+xdepth, y0-ydepth, clrshd); // edging
190
gdImageLine(im, x2, y0, x2+xdepth, y0-ydepth, clrshd); // edging
192
// SET_3D_POLY(poly, x2, x2, y0, y2, xdepth, ydepth); // side
193
// gdImageFilledPolygon(im, poly, 4, clrshd);
195
gdImageLine(im, x1, y1, x1+xdepth, y1-ydepth, clrshd); // edging
196
gdImageLine(im, x2, y2, x2+xdepth, y2-ydepth, clrshd); // edging
199
if(y1 == y2) // bar rect
200
SET_RECT(poly, x1, x2, y0, y1); // front
203
poly[0].x = x1; poly[0].y = y0;
204
poly[1].x = x2; poly[1].y = y0;
205
poly[2].x = x2; poly[2].y = y2;
206
poly[3].x = x1; poly[3].y = y1;
208
gdImageFilledPolygon(im, poly, 4, clr);
210
gdImageLine(im, x1, y0, x2, y0, clrshd); // edging along y0
212
if((xdepth || ydepth) && // front edging only on 3D
213
(y1<y0 || y2<y0)) // and only above y0
215
if(y1 > y0 && y2 < y0) // line crosses from below y0
216
gdImageLine(im, y_intercept, y0, x2, y2, clrshd);
218
if(y1 < y0 && y2 > y0) // line crosses from above y0
219
gdImageLine(im, x1, y1, y_intercept, y0, clrshd);
220
else // completely above
221
gdImageLine(im, x1, y1, x2, y2, clrshd);
225
/* ------------------------------------------------------------------------- */
227
/* ref is front plane */
229
draw_3d_bar(gdImagePtr im,
239
#define SET_3D_BAR(gdp, x1, x2, y1, y2, xoff, yoff) \
240
gdp[0].x = x1, gdp[0].y = y1, \
241
gdp[1].x = x1+(xoff), gdp[1].y = y1-yoff, \
242
gdp[2].x = x2+(xoff), gdp[2].y = y2-yoff, \
243
gdp[3].x = x2, gdp[3].y = y2
246
int usd = MIN(y0, yhigh); // up-side-down bars
251
if(y0 != yhigh) // 0 height?
253
SET_3D_BAR(poly, x2, x2, y0, yhigh, xdepth, ydepth); // side
254
gdImageFilledPolygon(im, poly, 4, clrshd);
257
SET_3D_BAR(poly, x1, x2, usd, usd, xdepth, ydepth); // top
258
gdImageFilledPolygon(im, poly, 4, clr);
261
SET_RECT(poly, x1, x2, y0, yhigh); // front
262
gdImageFilledPolygon(im, poly, 4, clr);
265
gdImageLine(im, x1, usd, x2, usd, clrshd);
268
/* ------------------------------------------------------------------------- */
269
struct BS { float y1; float y2; int clr; int shclr; };
270
static int barcmpr(const void *a, const void *b)
271
{ if(((struct BS*)a)->y2 < ((struct BS*)b)->y2) return -1;
272
if(((struct BS*)a)->y2 > ((struct BS*)b)->y2) return 1;
275
/* ------------------------------------------------------------------------- */
276
// simple two-point linear interpolation
277
// attempts between first, then nearest
279
do_interpolations(int num_points,
284
float v1 = GDC_NOVALUE,
290
for(i=interp_point-1; i>=0 && p1==-1; --i)
291
if(vals[i] != GDC_NOVALUE && vals[i] != GDC_INTERP_VALUE)
297
for(j=interp_point+1; j<num_points && p2==-1; ++j)
298
if(vals[j] != GDC_NOVALUE && vals[j] != GDC_INTERP_VALUE)
303
// no forward sample, find backwards
304
for(; i>=0 && p2==-1; --i)
305
if(vals[i] != GDC_NOVALUE && vals[i] != GDC_INTERP_VALUE)
310
// no backwards sample, find forwards
311
for(; j<num_points && p1==-1; ++j)
312
if(vals[j] != GDC_NOVALUE && vals[j] != GDC_INTERP_VALUE)
317
if(p1==-1 || p2==-1 || // need both
320
vals[interp_point] = GDC_NOVALUE;
324
// Point-slope formula
325
vals[interp_point] = ((v2-v1)/(float)(p2-p1)) * (float)(interp_point-p1) + v1;
329
/* ========================================================================= */
330
// little/no error checking 0: ok,
331
// -ret: error no graph output
332
// ret: error graph out
333
// watch out for # params and array sizes==num_points
334
/* ------------------------------------------------------------------------- */
335
// original var arg interface
337
out_graph(short IMGWIDTH, /* no check for a image that's too small to fit */
338
short IMGHEIGHT, /* needed info (labels, etc), could core dump */
339
FILE *img_fptr, // open file pointer (img out)
341
int num_points, // points along x axis (even iterval)
342
// all arrays dependant on this
343
char *xlbl[], // array of xlabels
347
char do_hlc = (type == GDC_HILOCLOSE ||
348
type == GDC_3DHILOCLOSE ||
349
type == GDC_3DCOMBO_HLC_BAR ||
350
type == GDC_3DCOMBO_HLC_AREA ||
351
type == GDC_COMBO_HLC_BAR ||
352
type == GDC_COMBO_HLC_AREA);
354
char do_fb = (type == GDC_FLOATINGBAR ||
355
type == GDC_3DFLOATINGBAR);
357
char do_vol = (type == GDC_COMBO_HLC_BAR ||
358
type == GDC_COMBO_HLC_AREA ||
359
type == GDC_COMBO_LINE_BAR ||
360
type == GDC_COMBO_LINE_AREA ||
361
type == GDC_COMBO_LINE_LINE ||
362
type == GDC_3DCOMBO_HLC_BAR ||
363
type == GDC_3DCOMBO_HLC_AREA||
364
type == GDC_3DCOMBO_LINE_BAR||
365
type == GDC_3DCOMBO_LINE_AREA ||
366
type == GDC_3DCOMBO_LINE_LINE);
368
int num_arrays = num_sets * (do_hlc? 3:
371
float data[num_arrays*num_points],
372
*combo_data = (float*)NULL;
377
va_start(ap, num_sets);
378
for(i=0; i<num_arrays; ++i)
379
memcpy(data+i*num_points, va_arg(ap, float*), num_points*sizeof(float));
381
combo_data = va_arg(ap, float*);
384
return GDC_out_graph(IMGWIDTH,
395
/* ------------------------------------------------------------------------- */
396
// multi array interface
398
GDC_out_graph(short IMGWIDTH, /* no check for a img that's too small to fit */
399
short IMGHEIGHT, /* needed info (labels, etc), could core dump */
400
FILE *img_fptr, // open file pointer (img out)
402
int num_points, // points along x axis (even iterval)
403
// all arrays dependant on this
404
char *xlbl[], // array of xlabels
406
float *data, // (float*) cast on multi-dim array (num_sets > 1)
407
float *combo_data) // only used on COMBO chart types
414
gdImagePtr bg_img = NULL;
416
float xorig, yorig, vyorig;
420
float vhighest = -MAXFLOAT;
421
float vlowest = MAXFLOAT;
422
float highest = -MAXFLOAT;
423
float lowest = MAXFLOAT;
426
char do_vol = (type == GDC_COMBO_HLC_BAR || // aka: combo
427
type == GDC_COMBO_HLC_AREA ||
428
type == GDC_COMBO_LINE_BAR ||
429
type == GDC_COMBO_LINE_AREA ||
430
type == GDC_COMBO_LINE_LINE ||
431
type == GDC_3DCOMBO_HLC_BAR ||
432
type == GDC_3DCOMBO_HLC_AREA||
433
type == GDC_3DCOMBO_LINE_BAR||
434
type == GDC_3DCOMBO_LINE_AREA ||
435
type == GDC_3DCOMBO_LINE_LINE);
436
char threeD = (type == GDC_3DAREA ||
437
type == GDC_3DLINE ||
439
type == GDC_3DFLOATINGBAR ||
440
type == GDC_3DHILOCLOSE ||
441
type == GDC_3DCOMBO_HLC_BAR ||
442
type == GDC_3DCOMBO_HLC_AREA||
443
type == GDC_3DCOMBO_LINE_BAR||
444
type == GDC_3DCOMBO_LINE_AREA ||
445
type == GDC_3DCOMBO_LINE_LINE);
446
char num_groups = num_sets; // set before num_sets gets adjusted
447
char set_depth = (GDC_stack_type == GDC_STACK_DEPTH)? num_groups:
449
char do_bar = (type == GDC_3DBAR || // offset X objects to leave
450
type == GDC_BAR || // room at X(0) and X(n)
451
type == GDC_3DFLOATINGBAR || // i.e., not up against Y axes
452
type == GDC_FLOATINGBAR);
453
char do_ylbl_fractions = // %f format not given, or
454
(!GDC_ylabel_fmt || // format doesn't have a %,g,e,E,f or F
455
strlen(GDC_ylabel_fmt) == strcspn(GDC_ylabel_fmt,"%geEfF"));
456
float ylbl_interval = 0.0;
457
int num_lf_xlbls = 0;
458
int xdepth_3Dtotal = 0;
459
int ydepth_3Dtotal = 0;
460
int xdepth_3D = 0; // affects PX()
461
int ydepth_3D = 0; // affects PY() and PV()
462
int hlf_barwdth = 0; // half bar widths
463
int hlf_hlccapwdth = 0; // half cap widths for HLC_I_CAP and DIAMOND
468
int setno = 0; // affects PX() and PY()
469
float *uvals[ type == GDC_HILOCLOSE ||
470
type == GDC_3DHILOCLOSE ||
471
type == GDC_3DCOMBO_HLC_BAR ||
472
type == GDC_3DCOMBO_HLC_AREA ||
473
type == GDC_COMBO_HLC_BAR ||
474
type == GDC_COMBO_HLC_AREA? num_sets *= 3: // 1 more last set is vol
475
type == GDC_FLOATINGBAR ||
476
type == GDC_3DFLOATINGBAR? num_sets *= 2:
477
type == GDC_COMBO_LINE_BAR ||
478
type == GDC_3DCOMBO_LINE_BAR ||
479
type == GDC_3DCOMBO_LINE_AREA||
480
type == GDC_3DCOMBO_LINE_LINE||
481
type == GDC_COMBO_LINE_AREA ||
482
type == GDC_COMBO_LINE_LINE? num_sets: // 1 more last set is vol
491
ExtVolColor[num_points],
498
ExtColor[num_sets][num_points];
499
// shade colors only with 3D
500
// int ExtColorShd[threeD?1:num_sets][threeD?1:num_points]; // compiler limitation
501
int ExtColorShd[num_sets][num_points];
504
if(IMGWIDTH<=0 || IMGHEIGHT<=0 || (!img_fptr && GDC_generate_img))
508
out_err(IMGWIDTH, IMGHEIGHT, img_fptr, GDC_BGColor, GDC_LineColor, "No Data Available");
512
load_font_conversions();
520
// ----- get args -----
521
for(i=0; i<num_sets; ++i)
522
uvals[i] = data+i*num_points;
529
/* ----- calculate interpretations first ----- */
530
if(GDC_interpolations)
532
for(i=0; i<num_sets; ++i)
533
for(j=0; j<num_points; ++j)
534
if(uvals[i][j] == GDC_INTERP_VALUE)
536
do_interpolations(num_points, j, uvals[i]);
539
for(j=0; j<num_points; ++j)
540
if(uvol[j] == GDC_INTERP_VALUE)
542
do_interpolations(num_points, j, uvol);
546
/* ----- highest & lowest values ----- */
547
if(GDC_stack_type == GDC_STACK_SUM) // need to walk sideways
548
for(j=0; j<num_points; ++j)
551
for(i=0; i<num_sets; ++i)
552
if(uvals[i][j] != GDC_NOVALUE)
554
set_sum += uvals[i][j];
555
highest = MAX(highest, set_sum);
556
lowest = MIN(lowest, set_sum);
560
if(GDC_stack_type == GDC_STACK_LAYER) // need to walk sideways
561
for(j=0; j<num_points; ++j)
563
float neg_set_sum = 0.0,
565
for(i=0; i<num_sets; ++i)
566
if(uvals[i][j] != GDC_NOVALUE)
567
if(uvals[i][j] < 0.0)
568
neg_set_sum += uvals[i][j];
570
pos_set_sum += uvals[i][j];
571
lowest = MIN(lowest, MIN(neg_set_sum,pos_set_sum));
572
highest = MAX(highest, MAX(neg_set_sum,pos_set_sum));
575
for(i=0; i<num_sets; ++i)
576
for(j=0; j<num_points; ++j)
577
if(uvals[i][j] != GDC_NOVALUE)
579
highest = MAX(uvals[i][j], highest);
580
lowest = MIN(uvals[i][j], lowest);
583
for(i=0; i<GDC_num_scatter_pts; ++i)
585
highest = MAX((GDC_scatter+i)->val, highest);
586
lowest = MIN((GDC_scatter+i)->val, lowest );
588
if(do_vol) // for now only one combo set allowed
592
for(j=0; j<num_points; ++j)
593
if(uvol[j] != GDC_NOVALUE)
595
vhighest = MAX(uvol[j], vhighest);
596
vlowest = MIN(uvol[j], vlowest);
598
if(vhighest == -MAXFLOAT) // no values
599
vhighest = 1.0; // for scaling, need a range
600
if(vlowest == MAXFLOAT)
602
if(type == GDC_COMBO_LINE_BAR ||
603
type == GDC_COMBO_HLC_BAR ||
604
type == GDC_COMBO_LINE_AREA ||
605
type == GDC_COMBO_HLC_AREA ||
606
type == GDC_3DCOMBO_LINE_BAR ||
607
type == GDC_3DCOMBO_LINE_AREA ||
608
type == GDC_3DCOMBO_HLC_BAR ||
609
type == GDC_3DCOMBO_HLC_AREA)
614
vlowest = 0.0; // bar, area should always start at 0
617
if(lowest == MAXFLOAT)
619
if(highest == -MAXFLOAT)
620
highest = 1.0; // need a range
621
if(type == GDC_AREA || // bars and area should always start at 0
628
if(lowest > 0.0) // negs should be drawn from 0
631
if(GDC_requested_ymin != GDC_NOVALUE && GDC_requested_ymin < lowest)
632
lowest = GDC_requested_ymin;
633
if(GDC_requested_ymax != GDC_NOVALUE && GDC_requested_ymax > highest)
634
highest = GDC_requested_ymax;
637
/* ----- graph height and width within the img height width ----- */
638
/* grapheight/height is the actual size of the scalable graph */
640
int title_hgt = GDC_title? 2 /* title? horizontal text line(s) */
641
+ cnt_nl(GDC_title,(int*)NULL)*GDC_fontc[GDC_title_size].h
645
int xtitle_hgt = GDC_xtitle? 1+GDC_fontc[GDC_xtitle_size].h+1: 0;
646
int ytitle_hgt = GDC_ytitle? 1+GDC_fontc[GDC_ytitle_size].h+1: 0;
647
int vtitle_hgt = do_vol&&GDC_ytitle2? 1+GDC_fontc[GDC_ytitle_size].h+1: 0;
651
int xtics = GDC_ticks && (GDC_grid||GDC_xaxis)? 1+2: 0;
652
int ytics = GDC_ticks && (GDC_grid||GDC_yaxis)? 1+3: 0;
653
int vtics = GDC_ticks && (GDC_yaxis&&do_vol)? 3+1: 0;
656
#define HYP_DEPTH ((double)((IMGWIDTH+IMGHEIGHT)/2) * ((double)GDC_3d_depth)/100.0)
657
#define RAD_DEPTH ((double)GDC_3d_angle*2*M_PI/360)
658
xdepth_3D = threeD? (int)(cos(RAD_DEPTH) * HYP_DEPTH): 0;
659
ydepth_3D = threeD? (int)(sin(RAD_DEPTH) * HYP_DEPTH): 0;
660
xdepth_3Dtotal = xdepth_3D*set_depth;
661
ydepth_3Dtotal = ydepth_3D*set_depth;
662
annote_hgt = GDC_annotation && *(GDC_annotation->note)?
663
1 + /* space to note */
664
(1+GDC_fontc[GDC_annotation_font].h) * /* number of '\n' substrs */
665
cnt_nl(GDC_annotation->note,&annote_len) +
666
1 + /* space under note */
667
2: 0; /* space to chart */
668
annote_len *= GDC_fontc[GDC_annotation_font].w;
670
if(GDC_xaxis && xlbl)
672
int biggest = -MAXINT;
674
for(i=0; i<num_points; ++i)
677
// longest "...\n" segment
678
for(len=0, j=0; xlbl[i][j]; ++len, ++j)
679
if(xlbl[i][j] == '\n')
681
biggest = MAX(len, biggest);
685
biggest = MAX(len, biggest); // last seg
687
xlabel_hgt = 1+ biggest*GDC_fontc[GDC_xaxisfont_size].w +1;
690
grapheight = IMGHEIGHT - (xtics +
697
if(GDC_hard_size && GDC_hard_grapheight) /* user wants to use his */
698
grapheight = GDC_hard_grapheight;
699
GDC_hard_grapheight = grapheight;
700
// before width can be known...
701
/* ----- y labels intervals ----- */
704
/* possible y gridline points */
705
float ypoints[] = { 1.0/64.0, 1.0/32.0, 1.0/16.0, 1.0/8.0, 1.0/4.0, 1.0/2.0,
706
1.0, 2.0, 3.0, 5.0, 10.0, 25.0,
707
50.0, 100.0, 250.0, 500.0, 1000.0, 2500, 5000.0,
708
10000.0, 25000.0, 50000.0, 100000.0,500000.0,1000000, 5000000,
710
#define NUM_YPOINTS (sizeof(ypoints) / sizeof(float))
712
int longest_ylblen = 0;
713
/* maximum y lables that'll fit... */
714
max_num_ylbls = grapheight / (3+GDC_fontc[GDC_yaxisfont_size==GDC_TINY? GDC_yaxisfont_size+1:
715
GDC_yaxisfont_size].h);
716
if(max_num_ylbls < 3)
718
/* gdImageDestroy(im); haven't yet created it */
719
out_err(IMGWIDTH, IMGHEIGHT,
721
GDC_BGColor, GDC_LineColor,
722
"Insificient Height");
726
/* one "space" interval above + below */
727
for(i=1; i<NUM_YPOINTS; ++i)
728
// if(ypoints[i] > ylbl_interval)
730
if((highest-lowest)/ypoints[i] < ((float)max_num_ylbls-(1.0+1.0))
731
* (float)GDC_ylabel_density/100.0)
733
/* gotta go through the above loop to catch the 'tweeners :-| */
735
ylbl_interval = GDC_requested_yinterval != GDC_NOVALUE &&
736
GDC_requested_yinterval > ypoints[i-1]? GDC_requested_yinterval:
739
/* perform floating point remainders */
740
/* gonculate largest interval-point < lowest */
742
lowest != GDC_requested_ymin)
745
lowest -= ylbl_interval;
746
// lowest = (lowest-ypoints[0]) -
747
// ((((lowest-ypoints[0])/ylbl_interval)*ylbl_interval) -
748
// ((float)((int)((lowest-ypoints[0])/ylbl_interval))*ylbl_interval));
749
lowest = ylbl_interval * (float)(int)((lowest-ypoints[0])/ylbl_interval);
751
/* find smallest interval-point > highest */
752
tmp_highest = lowest;
753
do // while((tmp_highest += ylbl_interval) <= highest)
755
int nmrtr, dmntr, whole;
756
char *price_to_str(float, int*, int*, int*, char*);
761
{ /* XPG2 compatibility */
762
snprintf(foo,sizeof(foo) ,do_ylbl_fractions? "%.0f": GDC_ylabel_fmt, tmp_highest);
763
lbl_len = ylbl_interval<1.0? strlen(price_to_str(tmp_highest,
767
do_ylbl_fractions? NULL: GDC_ylabel_fmt)):
769
longest_ylblen = MAX(longest_ylblen, lbl_len);
771
} while((tmp_highest += ylbl_interval) <= highest);
772
ylabel_wth = longest_ylblen * GDC_fontc[GDC_yaxisfont_size].w;
773
highest = GDC_requested_ymax==GDC_NOVALUE? tmp_highest:
774
MAX(GDC_requested_ymax, highest);
778
float num_yintrvls = (highest-lowest) / ylbl_interval;
781
vhighest += (vhighest-vlowest) / (num_yintrvls*2.0);
783
vlowest -= (vhighest-vlowest) / (num_yintrvls*2.0);
788
int lbl_len_low = snprintf(svlongest,sizeof(svlongest) , GDC_ylabel2_fmt? GDC_ylabel2_fmt: "%.0f", vlowest);
789
int lbl_len_high = snprintf(svlongest,sizeof(svlongest) ,GDC_ylabel2_fmt? GDC_ylabel2_fmt: "%.0f", vhighest);
792
+ MAX(lbl_len_low,lbl_len_high) * GDC_fontc[GDC_yaxisfont_size].w;
797
graphwidth = IMGWIDTH - (((GDC_hard_size && GDC_hard_xorig)? GDC_hard_xorig:
805
if(GDC_hard_size && GDC_hard_graphwidth) /* user wants to use his */
806
graphwidth = GDC_hard_graphwidth;
807
GDC_hard_graphwidth = graphwidth;
809
/* ----- scale to img size ----- */
810
/* offset to 0 at lower left (where it should be) */
811
xscl = (float)(graphwidth-xdepth_3Dtotal) / (float)(num_points + (do_bar?2:0));
812
yscl = -((float)grapheight) / (float)(highest-lowest);
815
float hilow_diff = vhighest-vlowest==0.0? 1.0: vhighest-vlowest;
817
vyscl = -((float)grapheight) / hilow_diff;
818
vyorig = (float)grapheight
819
+ ABS(vyscl) * MIN(vlowest,vhighest)
824
xorig = (float)(IMGWIDTH - (graphwidth +
828
if(GDC_hard_size && GDC_hard_xorig)
829
xorig = GDC_hard_xorig;
830
GDC_hard_xorig = xorig;
831
// yorig = (float)grapheight + ABS(yscl * lowest) + ydepth_3Dtotal + title_hgt;
832
yorig = (float)grapheight
833
+ ABS(yscl) * MIN(lowest,highest)
837
//???? if(GDC_hard_size && GDC_hard_yorig) /* vyorig too? */
838
//???? yorig = GDC_hard_yorig;
839
GDC_hard_yorig = yorig;
841
hlf_barwdth = (int)((float)(PX(2)-PX(1)) * (((float)GDC_bar_width/100.0)/2.0)); // used only for bars
842
hlf_hlccapwdth = (int)((float)(PX(2)-PX(1)) * (((float)GDC_HLC_cap_width/100.0)/2.0));
844
// scaled, sized, ready
847
/* ----- OK start the graphic ----- */
848
if((GDC_hold_img & GDC_REUSE_IMAGE) &&
849
GDC_image != (void*)NULL)
852
im = gdImageCreate(IMGWIDTH, IMGHEIGHT);
855
BGColor = gdImageColorAllocate(im, l2gdcal(GDC_BGColor));
856
LineColor = clrallocate(im, GDC_LineColor);
857
PlotColor = clrallocate(im, GDC_PlotColor);
858
GridColor = clrallocate(im, GDC_GridColor);
861
VolColor = clrallocate(im, GDC_VolColor);
862
for(i=0; i<num_points; ++i)
864
ExtVolColor[i] = clrallocate(im, GDC_ExtVolColor[i]);
866
ExtVolColor[i] = VolColor;
868
// ArrowDColor = gdImageColorAllocate(im, 0xFF, 0, 0);
869
// ArrowUColor = gdImageColorAllocate(im, 0, 0xFF, 0);
871
AnnoteColor = clrallocate(im, GDC_annotation->color);
873
/* attempt to import optional background image */
876
FILE *in = fopen(GDC_BGImage, "rb");
879
; // Cant load background image, drop it
884
// should determine type by file extension, option, ...
885
if(bg_img = gdImageCreateFromPng(in)) // =
887
int bgxpos = gdImageSX(bg_img)<IMGWIDTH? IMGWIDTH/2 - gdImageSX(bg_img)/2: 0,
888
bgypos = gdImageSY(bg_img)<IMGHEIGHT? IMGHEIGHT/2 - gdImageSY(bg_img)/2: 0;
891
if(gdImageSX(bg_img) > IMGWIDTH || // resize only if too big
892
gdImageSY(bg_img) > IMGHEIGHT) // [and center]
894
gdImageCopyResized(im, bg_img, // dst, src
895
bgxpos, bgypos, // dstX, dstY
897
IMGWIDTH, IMGHEIGHT, // dstW, dstH
898
IMGWIDTH, IMGHEIGHT); // srcW, srcH
901
gdImageCopy(im, bg_img, // dst, src
902
bgxpos, bgypos, // dstX, dstY
904
IMGWIDTH, IMGHEIGHT); // W, H
910
for(j=0; j<num_sets; ++j)
911
for(i=0; i<num_points; ++i)
914
unsigned long ext_clr = *(GDC_ExtColor+num_points*j+i);
916
ExtColor[j][i] = clrallocate(im, ext_clr);
918
ExtColorShd[j][i] = clrshdallocate(im, ext_clr);
920
else if(GDC_SetColor)
922
int set_clr = GDC_SetColor[j];
923
ExtColor[j][i] = clrallocate(im, set_clr);
925
ExtColorShd[j][i] = clrshdallocate(im, set_clr);
929
ExtColor[j][i] = PlotColor;
931
ExtColorShd[j][i] = clrshdallocate(im, GDC_PlotColor);
935
if(GDC_transparent_bg)
936
gdImageColorTransparent(im, BGColor);
941
int titlecolor = clrallocate(im, GDC_TitleColor);
943
cnt_nl(GDC_title, &tlen);
945
&GDC_fontc[GDC_title_size],
946
IMGWIDTH/2 - tlen*GDC_fontc[GDC_title_size].w/2,
954
int titlecolor = GDC_XTitleColor==GDC_DFLTCOLOR?
955
PlotColor: clrallocate(im, GDC_XTitleColor);
957
GDC_fontc[GDC_xtitle_size].f,
958
IMGWIDTH/2 - strlen(GDC_xtitle)*GDC_fontc[GDC_xtitle_size].w/2,
959
IMGHEIGHT-GDC_fontc[GDC_xtitle_size].h-1,
965
/* ----- start drawing ----- */
966
/* ----- backmost first - border, grid & labels ----- */
967
/* if no grid, on 3D, border needs to handle it */
968
if(!GDC_grid && threeD &&
969
((GDC_border == GDC_BORDER_ALL) || (GDC_border & GDC_BORDER_X) || (GDC_border & GDC_BORDER_Y)))
981
gdImageLine(im, x1, y1, x2, y2, LineColor); // depth at origin
982
if((GDC_border == GDC_BORDER_ALL) || (GDC_border & GDC_BORDER_X))
983
gdImageLine(im, x2, y2, PX(num_points-1+(do_bar?2:0)), y2, LineColor);
984
if((GDC_border == GDC_BORDER_ALL) || (GDC_border & GDC_BORDER_Y))
985
gdImageLine(im, x2, PY(highest), x2, y2, LineColor);
988
if(GDC_grid || GDC_ticks || GDC_yaxis)
989
{ /* grid lines & y label(s) */
990
float tmp_y = lowest;
991
int labelcolor = GDC_YLabelColor==GDC_DFLTCOLOR?
992
LineColor: clrallocate(im, GDC_YLabelColor);
993
int label2color = GDC_YLabel2Color==GDC_DFLTCOLOR?
994
VolColor: clrallocate(im, GDC_YLabel2Color);
996
/* step from lowest to highest puting in labels and grid at interval points */
997
/* since now "odd" intervals may be requested, try to step starting at 0, */
998
/* if lowest < 0 < highest */
999
for(i=-1; i<=1; i+=2) // -1, 1
1001
if(i == -1) if(lowest >= 0.0) // all pos plotting
1004
tmp_y = MIN(0, highest); // step down to lowest
1006
if(i == 1) if(highest <= 0.0) // all neg plotting
1009
tmp_y = MAX(0, lowest); // step up to highest
1012
// if(!(highest > 0 && lowest < 0)) // doesn't straddle 0
1014
// if(i == -1) // only do once: normal
1020
do // while((tmp_y (+-)= ylbl_interval) < [highest,lowest])
1023
char *price_to_str(float, int*, int*, int*, char*);
1024
char nmrtr[3+1], dmntr[3+1], whole[8];
1025
char all_whole = ylbl_interval<1.0? FALSE: TRUE;
1027
char *ylbl_str = price_to_str(tmp_y,&n,&d,&w,
1028
do_ylbl_fractions? NULL: GDC_ylabel_fmt);
1029
if(do_ylbl_fractions)
1031
snprintf(nmrtr,sizeof(nmrtr) ,"%d", n);
1032
snprintf(dmntr,sizeof(dmntr), "%d", d);
1033
snprintf(whole,sizeof(whole), "%d", w);
1036
if(GDC_grid || GDC_ticks)
1039
// int gridline_clr = tmp_y == 0.0? LineColor: GridColor;
1041
x1 = PX(0); y1 = PY(tmp_y);
1043
gdImageLine(im, x1-2, y1, x1, y1, GridColor);
1047
x2 = PX(0); y2 = PY(tmp_y); // w/ new setno
1048
gdImageLine(im, x1, y1, x2, y2, GridColor); // depth for 3Ds
1049
gdImageLine(im, x2, y2, PX(num_points-1+(do_bar?2:0)), y2, GridColor);
1050
setno = 0; // set back to foremost
1054
if(do_ylbl_fractions)
1056
if(w || (!w && !n && !d))
1059
GDC_fontc[GDC_yaxisfont_size].f,
1060
PX(0)-2-strlen(whole)*GDC_fontc[GDC_yaxisfont_size].w
1062
(strlen(nmrtr)*GDC_fontc[GDC_yaxisfont_size-1].w +
1063
GDC_fontc[GDC_yaxisfont_size].w +
1064
strlen(nmrtr)*GDC_fontc[GDC_yaxisfont_size-1].w) :
1066
PY(tmp_y)-GDC_fontc[GDC_yaxisfont_size].h/2,
1073
GDC_fontc[GDC_yaxisfont_size-1].f,
1074
PX(0)-2-strlen(nmrtr)*GDC_fontc[GDC_yaxisfont_size-1].w
1075
-GDC_fontc[GDC_yaxisfont_size].w
1076
-strlen(nmrtr)*GDC_fontc[GDC_yaxisfont_size-1].w + 1,
1077
PY(tmp_y)-GDC_fontc[GDC_yaxisfont_size].h/2 + 1,
1081
GDC_fontc[GDC_yaxisfont_size].f,
1082
PX(0)-2-GDC_fontc[GDC_yaxisfont_size].w
1083
-strlen(nmrtr)*GDC_fontc[GDC_yaxisfont_size-1].w,
1084
PY(tmp_y)-GDC_fontc[GDC_yaxisfont_size].h/2,
1088
GDC_fontc[GDC_yaxisfont_size-1].f,
1089
PX(0)-2-strlen(nmrtr)*GDC_fontc[GDC_yaxisfont_size-1].w - 2,
1090
PY(tmp_y)-GDC_fontc[GDC_yaxisfont_size].h/2 + 3,
1097
GDC_fontc[GDC_yaxisfont_size].f,
1098
PX(0)-2-strlen(ylbl_str)*GDC_fontc[GDC_yaxisfont_size].w,
1099
PY(tmp_y)-GDC_fontc[GDC_yaxisfont_size].h/2,
1104
if(do_vol && GDC_yaxis2)
1107
/* opposite of PV(y) */
1108
snprintf(vylbl, sizeof(vylbl),
1109
GDC_ylabel2_fmt? GDC_ylabel2_fmt: "%.0f",
1110
((float)(PY(tmp_y)+(setno*ydepth_3D)-vyorig))/vyscl);
1114
gdImageLine(im, PX(num_points-1+(do_bar?2:0)), PY(tmp_y),
1115
PX(num_points-1+(do_bar?2:0))+3, PY(tmp_y), GridColor);
1116
if(atof(vylbl) == 0.0) /* rounding can cause -0 */
1119
GDC_fontc[GDC_yaxisfont_size].f,
1120
PX(num_points-1+(do_bar?2:0))+6,
1121
PY(tmp_y)-GDC_fontc[GDC_yaxisfont_size].h/2,
1127
while(((i>0) && ((tmp_y += ylbl_interval) < highest)) ||
1128
((i<0) && ((tmp_y -= ylbl_interval) > lowest)));
1131
/* catch last (bottom) grid line - specific to an "off" requested interval */
1132
if(GDC_grid && threeD)
1135
gdImageLine(im, PX(0), PY(lowest), PX(num_points-1+(do_bar?2:0)), PY(lowest), GridColor);
1136
setno = 0; // set back to foremost
1140
if(do_vol && GDC_ytitle2)
1142
int titlecolor = GDC_YTitle2Color==GDC_DFLTCOLOR?
1143
VolColor: clrallocate(im, GDC_YTitle2Color);
1145
GDC_fontc[GDC_ytitle_size].f,
1146
IMGWIDTH-(1+GDC_fontc[GDC_ytitle_size].h),
1147
strlen(GDC_ytitle2)*GDC_fontc[GDC_ytitle_size].w/2 +
1154
if(GDC_yaxis && GDC_ytitle)
1156
int ytit_len = strlen(GDC_ytitle)*GDC_fontc[GDC_ytitle_size].w;
1157
int titlecolor = GDC_YTitleColor==GDC_DFLTCOLOR?
1158
PlotColor: clrallocate(im, GDC_YTitleColor);
1160
GDC_fontc[GDC_ytitle_size].f,
1162
IMGHEIGHT/2 + ytit_len/2,
1168
/* interviening set grids */
1169
/* 0 < setno < num_sets non-inclusive, they've already been covered */
1170
if(GDC_grid && threeD)
1172
for(setno=set_depth - 1;
1176
gdImageLine(im, PX(0), PY(lowest), PX(0), PY(highest), GridColor);
1177
gdImageLine(im, PX(0), PY(lowest), PX(num_points-1+(do_bar?2:0)), PY(lowest), GridColor);
1182
if((GDC_grid || GDC_0Shelf) && /* line color grid at 0 */
1183
((lowest < 0.0 && highest > 0.0) ||
1184
((lowest == 0.0 || highest == 0.0) && !(GDC_border&GDC_BORDER_X))))
1188
x1 = PX(0); y1 = PY(0);
1190
gdImageLine(im, x1-2, y1, x1, y1, LineColor);
1192
x2 = PX(0); y2 = PY(0); // w/ new setno
1193
gdImageLine(im, x1, y1, x2, y2, LineColor); // depth for 3Ds
1194
gdImageLine(im, x2, y2, PX(num_points-1+(do_bar?2:0)), y2, LineColor);
1195
setno = 0; // set back to foremost
1199
/* x ticks and xlables */
1200
if(GDC_grid || GDC_xaxis)
1202
int num_xlbls = /* maximum x lables that'll fit */
1203
/* each xlbl + avg due to num_lf_xlbls */
1205
((GDC_xlabel_spacing==MAXSHORT?0:GDC_xlabel_spacing)+GDC_fontc[GDC_xaxisfont_size].h +
1206
(num_lf_xlbls*(GDC_fontc[GDC_xaxisfont_size].h-1))/num_points);
1207
int labelcolor = GDC_XLabelColor==GDC_DFLTCOLOR?
1208
LineColor: clrallocate(im, GDC_XLabelColor);
1210
for(i=0; i<num_points+(do_bar?2:0); ++i)
1212
int xi = do_bar? i-1: i;
1213
int x1, x2, y1, y2, yh; // ticks & grids
1214
#define DO_TICK(x,y) if(GDC_ticks) \
1215
gdImageLine(im, x, y, x, y+2, GridColor); \
1217
#define DO_GRID(x1,y1,x2,y2) if(GDC_grid) \
1219
gdImageLine(im, x1, y1, x2, y2, GridColor); /* depth */ \
1220
gdImageLine(im, x2, y2, x2, yh, GridColor); \
1224
x1 = PX(i); y1 = PY(lowest);
1226
x2 = PX(i); y2 = PY(lowest); yh = PY(highest);
1227
setno = 0; // reset to foremost
1229
if(i == 0) // catch 3D Y back corner
1230
DO_GRID(x1,y1,x2,y2);
1232
/* labeled points */
1233
if((!GDC_xlabel_ctl && ((i%(1+num_points/num_xlbls) == 0) || // # x labels are regulated
1234
num_xlbls >= num_points ||
1235
GDC_xlabel_spacing == MAXSHORT))
1237
(GDC_xlabel_ctl && xi>=0 && *(GDC_xlabel_ctl+xi)))
1239
DO_TICK(x1,y1); // labeled points tick & grid
1240
DO_GRID(x1,y1,x2,y2);
1242
if(!do_bar || (i>0 && xi<num_points))
1243
if(GDC_xaxis && xlbl && xlbl[xi] && *(xlbl[xi]))
1245
/* waiting for GDCImageStringUpNL() */
1246
#define LBX GDC_fontc[GDC_xaxisfont_size]
1248
short xstrs_num = cnt_nl(xlbl[xi], &xlen);
1249
char sub_xlbl[xlen+1];
1250
// int xlbl_strt = -1+ PX((float)i+(float)(do_bar?((float)num_points/(float)num_xlbls):0.0)) - (int)((float)(LBX.h-2)*((float)xstrs_num/2.0));
1251
int xlbl_strt = -1+ PX(i) - (int)((float)(LBX.h-2)*((float)xstrs_num/2.0));
1260
sub_xlbl[xlen] = xlbl[xi][j];
1261
if(xlbl[xi][j] == '\n' ||
1262
xlbl[xi][j] == '\0')
1264
sub_xlbl[xlen] = '\0';
1268
xlbl_strt + (LBX.h-1)*xstrs_num,
1269
PY(lowest) + 2 + 1 + LBX.w*xlen,
1274
} while(xlbl[xi][j]);
1278
/* every point, on-point */
1281
if(GDC_grid == GDC_TICK_POINTS) /* --- GRID --- */
1282
DO_GRID(x1, y1, x2, y2);
1283
else if(GDC_grid > GDC_TICK_NONE)
1287
int prevx = PX(i-1);
1288
int intrv_dist = (x1-prevx)/(GDC_grid+1);
1289
DO_GRID(x1, y1, x2, y2);
1290
for(k=0, xt=prevx + intrv_dist;
1291
k<GDC_grid && xt<x1;
1292
++k, xt += intrv_dist)
1293
DO_GRID(xt, y1, xt+xdepth_3Dtotal, y2);
1296
if(GDC_ticks == GDC_TICK_POINTS) /* --- TICKS --- */
1298
else if(GDC_ticks > GDC_TICK_NONE)
1303
int intrv_dist = (x1-prevx)/(GDC_ticks+1);
1305
for(k=0, xt=prevx + intrv_dist;
1306
k<GDC_ticks && xt<x1;
1307
++k, xt += intrv_dist)
1314
/* ----- secondard data plotting (volume) ----- */
1315
/* so that grid lines appear under vol */
1319
if(type == GDC_COMBO_HLC_BAR ||
1320
type == GDC_COMBO_LINE_BAR ||
1321
type == GDC_3DCOMBO_LINE_BAR ||
1322
type == GDC_3DCOMBO_HLC_BAR)
1324
if(uvol[0] != GDC_NOVALUE)
1325
draw_3d_bar(im, PX(0), PX(0)+hlf_barwdth,
1330
for(i=1; i<num_points-1; ++i)
1331
if(uvol[i] != GDC_NOVALUE)
1332
draw_3d_bar(im, PX(i)-hlf_barwdth, PX(i)+hlf_barwdth,
1337
if(uvol[i] != GDC_NOVALUE)
1338
draw_3d_bar(im, PX(i)-hlf_barwdth, PX(i),
1345
if(type == GDC_COMBO_HLC_AREA ||
1346
type == GDC_COMBO_LINE_AREA ||
1347
type == GDC_3DCOMBO_LINE_AREA||
1348
type == GDC_3DCOMBO_HLC_AREA)
1350
for(i=1; i<num_points; ++i)
1351
if(uvol[i-1] != GDC_NOVALUE && uvol[i] != GDC_NOVALUE)
1352
draw_3d_area(im, PX(i-1), PX(i),
1353
PV(0), PV(uvol[i-1]), PV(uvol[i]),
1359
if(type == GDC_COMBO_LINE_LINE ||
1360
type == GDC_3DCOMBO_LINE_LINE)
1362
for(i=1; i<num_points; ++i)
1363
if(uvol[i-1] != GDC_NOVALUE && uvol[i] != GDC_NOVALUE)
1364
gdImageLine(im, PX(i-1), PV(uvol[i-1]),
1369
} // volume polys done
1371
if(GDC_annotation && threeD) /* back half of annotation line */
1373
int x1 = PX(GDC_annotation->point+(do_bar?1:0)),
1376
gdImageLine(im, x1, y1, PX(GDC_annotation->point+(do_bar?1:0)), PY(lowest), AnnoteColor);
1377
gdImageLine(im, PX(GDC_annotation->point+(do_bar?1:0)), PY(lowest),
1378
PX(GDC_annotation->point+(do_bar?1:0)), PY(highest)-2, AnnoteColor);
1382
/* ---------- start plotting the data ---------- */
1385
case GDC_3DBAR: /* depth, width, y interval need to allow for whitespace between bars */
1388
switch(GDC_stack_type)
1390
case GDC_STACK_DEPTH:
1391
for(setno=num_sets-1; setno>=0; --setno) // back sets first PX, PY depth
1392
for(i=0; i<num_points; ++i)
1393
if(uvals[setno][i] != GDC_NOVALUE)
1394
draw_3d_bar(im, PX(i+(do_bar?1:0))-hlf_barwdth, PX(i+(do_bar?1:0))+hlf_barwdth,
1395
PY(0), PY(uvals[setno][i]),
1396
xdepth_3D, ydepth_3D,
1398
threeD? ExtColorShd[setno][i]: ExtColor[setno][i]);
1402
case GDC_STACK_LAYER:
1404
float lasty[num_points];
1406
// for(i=0; i<num_points; ++i)
1407
// if(uvals[j][i] != GDC_NOVALUE)
1409
// lasty[i] = uvals[j][i];
1410
// draw_3d_bar(im, PX(i+(do_bar?1:0))-hlf_barwdth, PX(i+(do_bar?1:0))+hlf_barwdth,
1411
// PY(0), PY(uvals[j][i]),
1412
// xdepth_3D, ydepth_3D,
1414
// threeD? ExtColorShd[j][i]: ExtColor[j][i]);
1416
for(i=0; i<num_points; ++i)
1418
struct BS barset[num_sets];
1419
float lasty_pos = 0.0;
1420
float lasty_neg = 0.0;
1423
for(j=0, k=0; j<num_sets; ++j)
1425
if(uvals[j][i] != GDC_NOVALUE)
1427
if(uvals[j][i] < 0.0)
1429
barset[k].y1 = lasty_neg;
1430
barset[k].y2 = uvals[j][i] + lasty_neg;
1431
lasty_neg = barset[k].y2;
1435
barset[k].y1 = lasty_pos;
1436
barset[k].y2 = uvals[j][i] + lasty_pos;
1437
lasty_pos = barset[k].y2;
1439
barset[k].clr = ExtColor[j][i];
1440
barset[k].shclr = threeD? ExtColorShd[j][i]: ExtColor[j][i];
1444
qsort(barset, k, sizeof(struct BS), barcmpr);
1449
PX(i+(do_bar?1:0))-hlf_barwdth, PX(i+(do_bar?1:0))+hlf_barwdth,
1450
PY(barset[j].y1), PY(barset[j].y2),
1451
xdepth_3D, ydepth_3D,
1459
case GDC_STACK_BESIDE:
1460
{ // h/.5, h/1, h/1.5, h/2, ...
1461
int new_barwdth = (int)((float)hlf_barwdth / ((float)num_sets/2.0));
1462
for(i=0; i<num_points; ++i)
1463
for(j=0; j<num_sets; ++j)
1464
if(uvals[j][i] != GDC_NOVALUE)
1465
draw_3d_bar(im, PX(i+(do_bar?1:0))-hlf_barwdth+new_barwdth*j+1,
1466
PX(i+(do_bar?1:0))-hlf_barwdth+new_barwdth*(j+1),
1467
PY(0), PY(uvals[j][i]),
1468
xdepth_3D, ydepth_3D,
1470
threeD? ExtColorShd[j][i]: ExtColor[j][i]);
1476
case GDC_3DFLOATINGBAR:
1477
case GDC_FLOATINGBAR:
1479
switch(GDC_stack_type)
1481
case GDC_STACK_DEPTH:
1482
for(setno=num_groups-1; setno>=0; --setno) // back sets first PX, PY depth
1483
for(i=0; i<num_points; ++i)
1484
if(uvals[0+setno*2][i] != GDC_NOVALUE &&
1485
uvals[1+setno*2][i] != GDC_NOVALUE &&
1486
uvals[1+setno*2][i] > uvals[0+setno*2][i])
1487
draw_3d_bar(im, PX(i+(do_bar?1:0))-hlf_barwdth, PX(i+(do_bar?1:0))+hlf_barwdth,
1488
PY(uvals[0+setno*2][i]), PY(uvals[1+setno*2][i]),
1489
xdepth_3D, ydepth_3D,
1491
threeD? ExtColorShd[setno][i]: ExtColor[setno][i]);
1495
case GDC_STACK_BESIDE:
1496
{ // h/.5, h/1, h/1.5, h/2, ...
1497
int new_barwdth = (int)((float)hlf_barwdth / ((float)num_groups/2.0));
1498
for(i=0; i<num_points; ++i)
1499
for(j=0; j<num_groups; ++j)
1500
if(uvals[0+j*2][i] != GDC_NOVALUE &&
1501
uvals[1+j*2][i] != GDC_NOVALUE &&
1502
uvals[1+j*2][i] > uvals[0+j*2][i])
1503
draw_3d_bar(im, PX(i+(do_bar?1:0))-hlf_barwdth+new_barwdth*j+1,
1504
PX(i+(do_bar?1:0))-hlf_barwdth+new_barwdth*(j+1),
1505
PY(uvals[0+j*2][i]), PY(uvals[1+j*2][i]),
1506
xdepth_3D, ydepth_3D,
1508
threeD? ExtColorShd[j][i]: ExtColor[j][i]);
1515
case GDC_COMBO_LINE_BAR:
1516
case GDC_COMBO_LINE_AREA:
1517
case GDC_COMBO_LINE_LINE:
1518
for(j=num_sets-1; j>=0; --j)
1519
for(i=1; i<num_points; ++i)
1520
if(uvals[j][i-1] != GDC_NOVALUE && uvals[j][i] != GDC_NOVALUE)
1522
gdImageLine(im, PX(i-1), PY(uvals[j][i-1]), PX(i), PY(uvals[j][i]), ExtColor[j][i]);
1523
gdImageLine(im, PX(i-1), PY(uvals[j][i-1])+1, PX(i), PY(uvals[j][i])+1, ExtColor[j][i]);
1527
if(uvals[j][i-1] != GDC_NOVALUE)
1528
gdImageSetPixel(im, PX(i-1), PY(uvals[j][i-1]), ExtColor[j][i]);
1529
if(uvals[j][i] != GDC_NOVALUE)
1530
gdImageSetPixel(im, PX(i), PY(uvals[j][i]), ExtColor[j][i]);
1535
case GDC_3DCOMBO_LINE_BAR:
1536
case GDC_3DCOMBO_LINE_AREA:
1537
case GDC_3DCOMBO_LINE_LINE:
1542
for(i=1; i<num_points; ++i)
1544
if(GDC_stack_type == GDC_STACK_DEPTH)
1546
for(j=num_sets-1; j>=0; --j)
1547
if(uvals[j][i-1] != GDC_NOVALUE &&
1548
uvals[j][i] != GDC_NOVALUE)
1551
y1[j] = PY(uvals[j][i-1]);
1552
y2[j] = PY(uvals[j][i]);
1558
xdepth_3D, ydepth_3D,
1561
&(ExtColorShd[j][i]));
1566
if(GDC_stack_type == GDC_STACK_BESIDE ||
1567
GDC_stack_type == GDC_STACK_SUM) // all same plane
1574
for(j=0,set=0; j<num_sets; ++j)
1575
if(uvals[j][i-1] != GDC_NOVALUE &&
1576
uvals[j][i] != GDC_NOVALUE)
1578
if(GDC_stack_type == GDC_STACK_SUM)
1580
usey1 += uvals[j][i-1];
1581
usey2 += uvals[j][i];
1585
usey1 = uvals[j][i-1];
1586
usey2 = uvals[j][i];
1588
y1[set] = PY(usey1);
1589
y2[set] = PY(usey2);
1590
clr[set] = ExtColor[j][i];
1591
clrshd[set] = ExtColorShd[j][i]; /* fred */
1598
xdepth_3D, ydepth_3D,
1609
switch(GDC_stack_type)
1613
float lasty[num_points];
1615
for(i=1; i<num_points; ++i)
1616
if(uvals[j][i] != GDC_NOVALUE)
1618
lasty[i] = uvals[j][i];
1619
if(uvals[j][i-1] != GDC_NOVALUE)
1620
draw_3d_area(im, PX(i-1), PX(i),
1621
PY(0), PY(uvals[j][i-1]), PY(uvals[j][i]),
1622
xdepth_3D, ydepth_3D,
1624
threeD? ExtColorShd[j][i]: ExtColor[j][i]);
1626
for(j=1; j<num_sets; ++j)
1627
for(i=1; i<num_points; ++i)
1628
if(uvals[j][i] != GDC_NOVALUE && uvals[j][i-1] != GDC_NOVALUE)
1630
draw_3d_area(im, PX(i-1), PX(i),
1631
PY(lasty[i]), PY(lasty[i-1]+uvals[j][i-1]), PY(lasty[i]+uvals[j][i]),
1632
xdepth_3D, ydepth_3D,
1634
threeD? ExtColorShd[j][i]: ExtColor[j][i]);
1635
lasty[i] += uvals[j][i];
1640
case GDC_STACK_BESIDE: // behind w/o depth
1641
for(j=num_sets-1; j>=0; --j) // back sets 1st (setno = 0)
1642
for(i=1; i<num_points; ++i)
1643
if(uvals[j][i-1] != GDC_NOVALUE && uvals[j][i] != GDC_NOVALUE)
1644
draw_3d_area(im, PX(i-1), PX(i),
1645
PY(0), PY(uvals[j][i-1]), PY(uvals[j][i]),
1646
xdepth_3D, ydepth_3D,
1648
threeD? ExtColorShd[j][i]: ExtColor[j][i]);
1651
case GDC_STACK_DEPTH:
1653
for(setno=num_sets-1; setno>=0; --setno) // back sets first PX, PY depth
1654
for(i=1; i<num_points; ++i)
1655
if(uvals[setno][i-1] != GDC_NOVALUE && uvals[setno][i] != GDC_NOVALUE)
1656
draw_3d_area(im, PX(i-1), PX(i),
1657
PY(0), PY(uvals[setno][i-1]), PY(uvals[setno][i]),
1658
xdepth_3D, ydepth_3D,
1660
threeD? ExtColorShd[setno][i]: ExtColor[setno][i]);
1665
case GDC_3DHILOCLOSE:
1666
case GDC_3DCOMBO_HLC_BAR:
1667
case GDC_3DCOMBO_HLC_AREA:
1670
for(j=num_groups-1; j>=0; --j)
1672
for(i=1; i<num_points+1; ++i)
1673
if(uvals[CLOSESET+j*3][i-1] != GDC_NOVALUE)
1675
if((GDC_HLC_style & GDC_HLC_I_CAP) && // bottom half of 'I'
1676
uvals[LOWSET+j*3][i-1] != GDC_NOVALUE)
1678
SET_3D_POLY(poly, PX(i-1)-hlf_hlccapwdth, PX(i-1)+hlf_hlccapwdth,
1679
PY(uvals[LOWSET+j*3][i-1]), PY(uvals[LOWSET+j*3][i-1]),
1680
xdepth_3D, ydepth_3D);
1681
gdImageFilledPolygon(im, poly, 4, ExtColor[LOWSET+j*3][i-1]);
1682
gdImagePolygon(im, poly, 4, ExtColorShd[LOWSET+j*3][i-1]);
1684
// all HLC have vert line
1685
if(uvals[LOWSET+j*3][i-1] != GDC_NOVALUE)
1687
SET_3D_POLY(poly, PX(i-1), PX(i-1),
1688
PY(uvals[LOWSET+j*3][i-1]), PY(uvals[CLOSESET+j*3][i-1]),
1689
xdepth_3D, ydepth_3D);
1690
gdImageFilledPolygon(im, poly, 4, ExtColor[LOWSET+j*3][i-1]);
1691
gdImagePolygon(im, poly, 4, ExtColorShd[LOWSET+j*3][i-1]);
1693
if(uvals[HIGHSET+j*3][i-1] != GDC_NOVALUE)
1695
SET_3D_POLY(poly, PX(i-1), PX(i-1),
1696
PY(uvals[CLOSESET+j*3][i-1]), PY(uvals[HIGHSET+j*3][i-1]),
1697
xdepth_3D, ydepth_3D);
1698
gdImageFilledPolygon(im, poly, 4, ExtColor[HIGHSET+j*3][i-1]);
1699
gdImagePolygon(im, poly, 4, ExtColorShd[HIGHSET+j*3][i-1]);
1702
gdImageLine(im, PX(i-1), PY(uvals[CLOSESET+j*3][i-1]),
1703
PX(i-1)+xdepth_3D, PY(uvals[CLOSESET+j*3][i-1])-ydepth_3D,
1704
ExtColorShd[CLOSESET+j*3][i-1]);
1706
if(!((GDC_HLC_style & GDC_HLC_DIAMOND) &&
1707
(PY(uvals[HIGHSET+j*3][i-1]) > PY(uvals[CLOSESET+j*3][i-1])-hlf_hlccapwdth)) &&
1708
uvals[HIGHSET+j*3][i-1] != GDC_NOVALUE)
1709
if(GDC_HLC_style & GDC_HLC_I_CAP)
1711
SET_3D_POLY(poly, PX(i-1)-hlf_hlccapwdth, PX(i-1)+hlf_hlccapwdth,
1712
PY(uvals[HIGHSET+j*3][i-1]), PY(uvals[HIGHSET+j*3][i-1]),
1713
xdepth_3D, ydepth_3D);
1714
gdImageFilledPolygon(im, poly, 4, ExtColor[HIGHSET+j*3][i-1]);
1715
gdImagePolygon(im, poly, 4, ExtColorShd[HIGHSET+j*3][i-1]);
1718
if(i < num_points &&
1719
uvals[CLOSESET+j*3][i] != GDC_NOVALUE)
1721
if(GDC_HLC_style & GDC_HLC_CLOSE_CONNECTED) /* line from prev close */
1723
SET_3D_POLY(poly, PX(i-1), PX(i),
1724
PY(uvals[CLOSESET+j*3][i-1]), PY(uvals[CLOSESET+j*3][i-1]),
1725
xdepth_3D, ydepth_3D);
1726
gdImageFilledPolygon(im, poly, 4, ExtColor[CLOSESET+j*3][i]);
1727
gdImagePolygon(im, poly, 4, ExtColorShd[CLOSESET+j*3][i]);
1729
else // CLOSE_CONNECTED and CONNECTING are mutually exclusive
1730
if(GDC_HLC_style & GDC_HLC_CONNECTING) /* thin connecting line */
1732
int y1 = PY(uvals[CLOSESET+j*3][i-1]),
1733
y2 = PY(uvals[CLOSESET+j*3][i]);
1737
&y1, &y2, // rem only 1 set
1738
xdepth_3D, ydepth_3D,
1740
&(ExtColor[CLOSESET+j*3][i]),
1741
&(ExtColorShd[CLOSESET+j*3][i]));
1743
gdImageLine(im, PX(i-1), PY(uvals[CLOSESET+j*3][i-1]),
1744
PX(i), PY(uvals[CLOSESET+j*3][i]),
1745
ExtColorShd[CLOSESET+j*3][i]);
1747
// top half 'I' again
1748
if(PY(uvals[CLOSESET+j*3][i-1]) <= PY(uvals[CLOSESET+j*3][i]) &&
1749
uvals[HIGHSET+j*3][i-1] != GDC_NOVALUE )
1750
if(GDC_HLC_style & GDC_HLC_I_CAP)
1752
SET_3D_POLY(poly, PX(i-1)-hlf_hlccapwdth, PX(i-1)+hlf_hlccapwdth,
1753
PY(uvals[HIGHSET+j*3][i-1]), PY(uvals[HIGHSET+j*3][i-1]),
1754
xdepth_3D, ydepth_3D);
1755
gdImageFilledPolygon(im, poly, 4, ExtColor[HIGHSET+j*3][i-1]);
1756
gdImagePolygon(im, poly, 4, ExtColorShd[HIGHSET+j*3][i-1]);
1759
if(GDC_HLC_style & GDC_HLC_DIAMOND)
1761
poly[0].x = PX(i-1)-hlf_hlccapwdth;
1762
poly[0].y = PY(uvals[CLOSESET+j*3][i-1]);
1763
poly[1].x = PX(i-1);
1764
poly[1].y = PY(uvals[CLOSESET+j*3][i-1])+hlf_hlccapwdth;
1765
poly[2].x = PX(i-1)+hlf_hlccapwdth;
1766
poly[2].y = PY(uvals[CLOSESET+j*3][i-1]);
1767
poly[3].x = PX(i-1);
1768
poly[3].y = PY(uvals[CLOSESET+j*3][i-1])-hlf_hlccapwdth;
1769
gdImageFilledPolygon(im, poly, 4, ExtColor[CLOSESET+j*3][i-1]);
1770
gdImagePolygon(im, poly, 4, ExtColorShd[CLOSESET+j*3][i-1]);
1772
SET_3D_POLY(poly, PX(i-1), PX(i-1)+hlf_hlccapwdth,
1773
PY(uvals[CLOSESET+j*3][i-1])+hlf_hlccapwdth,
1774
PY(uvals[CLOSESET+j*3][i-1]),
1775
xdepth_3D, ydepth_3D);
1776
gdImageFilledPolygon(im, poly, 4, ExtColorShd[CLOSESET+j*3][i-1]);
1777
// gdImagePolygon(im, poly, 4, ExtColor[CLOSESET+j*3][i-1]);
1779
SET_3D_POLY(poly, PX(i-1), PX(i-1)+hlf_hlccapwdth,
1780
PY(uvals[CLOSESET+j*3][i-1])-hlf_hlccapwdth,
1781
PY(uvals[CLOSESET+j*3][i-1]),
1782
xdepth_3D, ydepth_3D);
1783
gdImageFilledPolygon(im, poly, 4, ExtColor[CLOSESET+j*3][i-1]);
1784
gdImagePolygon(im, poly, 4, ExtColorShd[CLOSESET+j*3][i-1]);
1792
case GDC_COMBO_HLC_BAR:
1793
case GDC_COMBO_HLC_AREA:
1794
for(j=num_groups-1; j>=0; --j)
1796
for(i=0; i<num_points; ++i)
1797
if(uvals[CLOSESET+j*3][i] != GDC_NOVALUE)
1798
{ /* all HLC have vert line */
1799
if(uvals[LOWSET+j*3][i] != GDC_NOVALUE)
1800
gdImageLine(im, PX(i), PY(uvals[CLOSESET+j*3][i]),
1801
PX(i), PY(uvals[LOWSET+j*3][i]),
1802
ExtColor[LOWSET+(j*3)][i]);
1803
if(uvals[HIGHSET+j*3][i] != GDC_NOVALUE)
1804
gdImageLine(im, PX(i), PY(uvals[HIGHSET+j*3][i]),
1805
PX(i), PY(uvals[CLOSESET+j*3][i]),
1806
ExtColor[HIGHSET+j*3][i]);
1808
if(GDC_HLC_style & GDC_HLC_I_CAP)
1810
if(uvals[LOWSET+j*3][i] != GDC_NOVALUE)
1811
gdImageLine(im, PX(i)-hlf_hlccapwdth, PY(uvals[LOWSET+j*3][i]),
1812
PX(i)+hlf_hlccapwdth, PY(uvals[LOWSET+j*3][i]),
1813
ExtColor[LOWSET+j*3][i]);
1814
if(uvals[HIGHSET+j*3][i] != GDC_NOVALUE)
1815
gdImageLine(im, PX(i)-hlf_hlccapwdth, PY(uvals[HIGHSET+j*3][i]),
1816
PX(i)+hlf_hlccapwdth, PY(uvals[HIGHSET+j*3][i]),
1817
ExtColor[HIGHSET+j*3][i]);
1819
if(GDC_HLC_style & GDC_HLC_DIAMOND)
1823
cd[0].x = PX(i)-hlf_hlccapwdth; cd[0].y = PY(uvals[CLOSESET+j*3][i]);
1824
cd[1].x = PX(i); cd[1].y = PY(uvals[CLOSESET+j*3][i])+hlf_hlccapwdth;
1825
cd[2].x = PX(i)+hlf_hlccapwdth; cd[2].y = PY(uvals[CLOSESET+j*3][i]);
1826
cd[3].x = PX(i); cd[3].y = PY(uvals[CLOSESET+j*3][i])-hlf_hlccapwdth;
1827
gdImageFilledPolygon(im, cd, 4, ExtColor[CLOSESET+j*3][i]);
1830
for(i=1; i<num_points; ++i)
1831
if(uvals[CLOSESET+j*3][i-1] != GDC_NOVALUE && uvals[CLOSESET+j*3][i] != GDC_NOVALUE)
1833
if(GDC_HLC_style & GDC_HLC_CLOSE_CONNECTED) // line from prev close
1834
gdImageLine(im, PX(i-1), PY(uvals[CLOSESET+j*3][i-1]),
1835
PX(i), PY(uvals[CLOSESET+j*3][i-1]),
1836
ExtColor[CLOSESET+j*3][i]);
1837
else // CLOSE_CONNECTED and CONNECTING are mutually exclusive
1838
if(GDC_HLC_style & GDC_HLC_CONNECTING) // thin connecting line
1839
gdImageLine(im, PX(i-1), PY(uvals[CLOSESET+j*3][i-1]),
1840
PX(i), PY(uvals[CLOSESET+j*3][i]),
1841
ExtColor[CLOSESET+j*3][i]);
1848
/* ---------- scatter points over all other plots ---------- */
1849
/* scatters, by their very nature, don't lend themselves to standard array of points */
1850
/* also, this affords the opportunity to include scatter points onto any type of chart */
1851
/* drawing of the scatter point should be an exposed function, so the user can */
1852
/* use it to draw a legend, and/or add their own */
1855
int scatter_clr[GDC_num_scatter_pts];
1858
for(i=0; i<GDC_num_scatter_pts; ++i)
1860
int hlf_scatterwdth = (int)((float)(PX(2)-PX(1))
1861
* (((float)((GDC_scatter+i)->width)/100.0)/2.0));
1862
int scat_x = PX((GDC_scatter+i)->point + (do_bar?1:0)),
1863
scat_y = PY((GDC_scatter+i)->val);
1865
if((GDC_scatter+i)->point >= num_points || // invalid point
1866
(GDC_scatter+i)->point < 0)
1868
scatter_clr[i] = clrallocate(im, (GDC_scatter+i)->color);
1870
switch((GDC_scatter+i)->ind)
1872
case GDC_SCATTER_CIRCLE:
1874
long uniq_clr = get_uniq_color(im);
1880
if((GDC_scatter+i)->point == 0)
1881
{ s = 270; e = 270+180; fo = 1; }
1883
if((GDC_scatter+i)->point == num_points-1)
1884
{ s = 90; e = 90+180; fo = -1; }
1885
if(uniq_clr != -1L) // the safe way
1887
int uc = gdImageColorAllocate(im, l2gdcal(uniq_clr));
1888
gdImageArc(im, scat_x, scat_y,
1889
hlf_scatterwdth*2, hlf_scatterwdth*2,
1892
if(fo) // close off semi-circle case
1893
gdImageLine(im, scat_x, scat_y+hlf_scatterwdth,
1894
scat_x, scat_y-hlf_scatterwdth,
1896
gdImageFillToBorder(im, scat_x+fo, scat_y, uc, scatter_clr[i]);
1897
gdImageArc(im, scat_x, scat_y,
1898
hlf_scatterwdth*2, hlf_scatterwdth*2,
1902
gdImageLine(im, scat_x, scat_y+hlf_scatterwdth,
1903
scat_x, scat_y-hlf_scatterwdth,
1905
gdImageColorDeallocate(im, uc);
1909
gdImageArc(im, scat_x, scat_y,
1910
hlf_scatterwdth*2, hlf_scatterwdth*2,
1914
gdImageLine(im, scat_x, scat_y+hlf_scatterwdth,
1915
scat_x, scat_y-hlf_scatterwdth,
1917
gdImageFillToBorder(im, scat_x+fo, scat_y,
1918
scatter_clr[i], scatter_clr[i]);
1922
case GDC_SCATTER_TRIANGLE_UP:
1925
ct[1].x = scat_x - hlf_scatterwdth;
1926
ct[1].y = scat_y + hlf_scatterwdth;;
1927
ct[2].x = scat_x + hlf_scatterwdth;
1928
ct[2].y = scat_y + hlf_scatterwdth;
1930
if((GDC_scatter+i)->point == 0)
1933
if((GDC_scatter+i)->point == num_points-1)
1935
gdImageFilledPolygon(im, ct, 3, scatter_clr[i]);
1937
case GDC_SCATTER_TRIANGLE_DOWN:
1940
ct[1].x = scat_x - hlf_scatterwdth;
1941
ct[1].y = scat_y - hlf_scatterwdth;;
1942
ct[2].x = scat_x + hlf_scatterwdth;
1943
ct[2].y = scat_y - hlf_scatterwdth;
1945
if((GDC_scatter+i)->point == 0)
1948
if((GDC_scatter+i)->point == num_points-1)
1950
gdImageFilledPolygon(im, ct, 3, scatter_clr[i]);
1957
// overlay with a value and an arrow (e.g., total daily change)
1959
/* put thmbl and thumbval over vol and plot lines */
1964
char *price_to_str(float, int*, int*, int*);
1965
char nmrtr[3+1], dmntr[3+1], whole[8];
1967
char *dbg = price_to_str(ABS(thumbval),&n,&d,&w);
1968
snprintf(nmrtr,sizeof(nmrtr), "%d", n);
1969
snprintf(dmntr,sizeof(dmntr), "%d", d);
1970
snprintf(whole,sizeof(whole), "%d", w);
1973
graphwidth/2-strlen(thumblabel)*SFONTWDTH/2,
1979
int chgcolor = thumbval>0.0? ThumbUColor: ThumbDColor;
1980
int thmbvalwidth = SFONTWDTH + // up/down arrow
1981
(w?strlen(whole)*SFONTWDTH: 0) + // whole
1982
(n?strlen(nmrtr)*TFONTWDTH + // numerator
1984
strlen(dmntr)*TFONTWDTH: // denominator
1987
smallarrow(im, graphwidth/2-thmbvalwidth/2, SFONTHGT, thumbval>0.0, chgcolor);
1992
(graphwidth/2-thmbvalwidth/2)+SFONTWDTH,
2001
(graphwidth/2-thmbvalwidth/2) + // start
2002
SFONTWDTH + // arrow
2003
(w? strlen(whole)*SFONTWDTH: 0) + // whole
2010
(graphwidth/2-thmbvalwidth/2) + // start
2011
SFONTWDTH + // arrow
2012
(w? strlen(whole)*SFONTWDTH: 0) + // whole
2013
strlen(nmrtr)*TFONTWDTH, // numerator
2019
(graphwidth/2-thmbvalwidth/2) + // start
2020
SFONTWDTH + // arrow
2021
(w? strlen(whole)*SFONTWDTH: 0) + // whole
2022
strlen(nmrtr)*TFONTWDTH + // numerator
2029
} // thumblabel, thumbval
2033
/* after plotting so the outline covers any plot lines */
2035
if(GDC_border == GDC_BORDER_ALL || (GDC_border & GDC_BORDER_X))
2036
gdImageLine(im, PX(0), PY(lowest), PX(num_points-1+(do_bar?2:0)), PY(lowest), LineColor);
2038
if(GDC_border == GDC_BORDER_ALL || (GDC_border & GDC_BORDER_TOP))
2041
gdImageLine(im, PX(0), PY(highest), PX(num_points-1+(do_bar?2:0)), PY(highest), LineColor);
2051
x2 = PX(num_points-1+(do_bar?2:0));
2053
if(GDC_border == GDC_BORDER_ALL || (GDC_border & GDC_BORDER_Y))
2054
gdImageLine(im, x1, PY(lowest), x1, y1, LineColor);
2057
if(GDC_border == GDC_BORDER_ALL || (GDC_border & GDC_BORDER_Y) || (GDC_border & GDC_BORDER_TOP))
2058
gdImageLine(im, x1, y1, PX(0), PY(highest), LineColor);
2059
// if(!GDC_grid || do_vol || GDC_thumbnail) // grid leaves right side Y open
2061
if(GDC_border == GDC_BORDER_ALL || (GDC_border & GDC_BORDER_X) || (GDC_border & GDC_BORDER_Y2))
2062
gdImageLine(im, x2, y2, PX(num_points-1+(do_bar?2:0)), PY(lowest), LineColor);
2063
if(GDC_border == GDC_BORDER_ALL || (GDC_border & GDC_BORDER_Y2))
2064
gdImageLine(im, PX(num_points-1+(do_bar?2:0)), PY(lowest),
2065
PX(num_points-1+(do_bar?2:0)), PY(highest), LineColor);
2070
if(GDC_0Shelf && threeD && /* front of 0 shelf */
2071
((lowest < 0.0 && highest > 0.0) ||
2072
((lowest == 0.0 || highest == 0.0) && !(GDC_border&GDC_BORDER_X))))
2074
int x2 = PX(num_points-1+(do_bar?2:0)),
2077
gdImageLine(im, PX(0), PY(0), x2, y2, LineColor); // front line
2080
gdImageLine(im, x2, y2, PX(num_points-1+(do_bar?2:0)), PY(0), LineColor);
2081
setno = 0; // set back to foremost
2084
if(GDC_annotation) /* front half of annotation line */
2086
int x1 = PX(GDC_annotation->point+(do_bar?1:0)),
2090
gdImageLine(im, x1, PY(lowest)+1, x1, y1, AnnoteColor);
2094
x2 = PX(GDC_annotation->point+(do_bar?1:0));
2096
gdImageLine(im, x1, y1, x2, PY(highest), AnnoteColor);
2098
else // for 3D done with back line
2100
x2 = PX(GDC_annotation->point+(do_bar?1:0));
2101
gdImageLine(im, x1, y1, x1, y1-2, AnnoteColor);
2103
/* line-to and note */
2104
if(*(GDC_annotation->note)) // any note?
2106
if(GDC_annotation->point >= (num_points/2)) /* note to the left */
2108
gdImageLine(im, x2, PY(highest)-2,
2109
x2-annote_hgt/2, PY(highest)-2-annote_hgt/2,
2111
GDCImageStringNL(im,
2112
&GDC_fontc[GDC_annotation_font],
2113
x2-annote_hgt/2-1-annote_len - 1,
2114
PY(highest)-annote_hgt+1,
2115
GDC_annotation->note,
2119
else /* note to right */
2121
gdImageLine(im, x2, PY(highest)-2,
2122
x2+annote_hgt/2, PY(highest)-2-annote_hgt/2,
2124
GDCImageStringNL(im,
2125
&GDC_fontc[GDC_annotation_font],
2126
x2+annote_hgt/2+1 + 1,
2127
PY(highest)-annote_hgt+1,
2128
GDC_annotation->note,
2137
/* usually GDC_generate_img is used in conjunction with hard or hold options */
2138
if(GDC_generate_img)
2140
fflush(img_fptr); // clear anything buffered
2141
switch(GDC_image_type)
2143
case GDC_PNG: gdImagePng(im, img_fptr); break;
2145
case GDC_JPEG: gdImageJpeg(im, img_fptr, GDC_jpeg_quality); break;
2147
case GDC_WBMP: gdImageWBMP(im, PlotColor, img_fptr); break;
2149
default: gdImagePng(im, img_fptr); /* gdImageGif(im, img_fptr); */
2154
gdImageDestroy(bg_img);
2155
if(GDC_hold_img & GDC_EXPOSE_IMAGE)
2156
GDC_image = (void*)im;