~ubuntu-branches/ubuntu/maverick/ntop/maverick

« back to all changes in this revision

Viewing changes to gdchart0.94c/gdchart.c

  • Committer: Bazaar Package Importer
  • Author(s): Dennis Schoen
  • Date: 2002-04-12 11:38:47 UTC
  • Revision ID: james.westby@ubuntu.com-20020412113847-4k4yydw0pzybc6g8
Tags: upstream-2.0.0
ImportĀ upstreamĀ versionĀ 2.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* GDCHART 0.10.0dev  GDCHART.C  2 Nov 2000 */
 
2
/* Copyright Bruce Verderaime 1998, 1999, 2000 */
 
3
 
 
4
/* vi:set tabstop=4 */
 
5
 
 
6
#include <stdio.h>
 
7
#include <stdlib.h>
 
8
#include <math.h>
 
9
#include <string.h>
 
10
#include <stdarg.h>
 
11
#include <errno.h>
 
12
 
 
13
#define GDC_INCL
 
14
#define GDC_LIB
 
15
#include "gdc.h"
 
16
#include "gdchart.h"
 
17
 
 
18
 
 
19
#define HIGHSET         0
 
20
#define LOWSET          1
 
21
#define CLOSESET        2
 
22
 
 
23
/* L. Deri */
 
24
#ifndef MAXINT
 
25
#define MAXINT 99999999
 
26
#endif /* MAXINT */
 
27
 
 
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)
 
32
 
 
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
 
37
 
 
38
#ifdef THUMB_VALS
 
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
// -------------------------------------------------------------------
 
43
void
 
44
smallarrow(gdImagePtr  im,
 
45
            int                 x,
 
46
            int                 y,
 
47
            char                up,
 
48
            int                 arrwclr)
 
49
{
 
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);
 
52
  if(up)                                                                                                                                /*   oo   */
 
53
    {                                                                                                                                   /*  uoou  */
 
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   */
 
60
    }                                                                                                                                   /*   oo   */
 
61
  else                                                                                                                                  /* ddoodd */
 
62
    {                                                                                                                                   /*  dood  */
 
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);
 
69
    }
 
70
}
 
71
#endif
 
72
 
 
73
 
 
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
/* ------------------------------------------------------------------------- */
 
80
/* vals in pixels */
 
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;
 
88
 return 0; }
 
89
void
 
90
draw_3d_line(gdImagePtr im,
 
91
              int                       y0,
 
92
              int                       x1,
 
93
              int                       x2,
 
94
              int                       y1[],
 
95
              int                       y2[],
 
96
              int                       xdepth,
 
97
              int                       ydepth,
 
98
              int                       num_sets,
 
99
              int                       clr[],
 
100
              int                       clrshd[])
 
101
{
 
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];
 
105
  int                   lnclr[num_sets],
 
106
    shclr[num_sets];
 
107
  int                   i;
 
108
  int                   x;
 
109
  gdPoint               poly[4];
 
110
  struct YS     ypts[num_sets];
 
111
 
 
112
  for(i=0; i<num_sets; ++i)
 
113
    {
 
114
      // lnclr[i] = clr[i];
 
115
      // shclr[i] = clrshd[i];
 
116
      slope[i] = x2==x1? MAXFLOAT: (float)(y2[i]-y1[i])/(float)(x2-x1);
 
117
    }
 
118
 
 
119
  for(x=x1+1; x<=x2; ++x)
 
120
    {
 
121
      for(i=0; i<num_sets; ++i)                                         // load set of points
 
122
        {
 
123
          ypts[i].y1    = F(x-1,i);
 
124
          ypts[i].y2    = F(x,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)
 
132
        {                                                                                       // top
 
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
 
137
            gdImageLine(im,
 
138
                         x-1, ypts[i].y2,
 
139
                         x-1+xdepth, ypts[i].y2-ydepth,
 
140
                         -ypts[i].slope<=depth_slope? ypts[i].shclr: ypts[i].lnclr);
 
141
        }
 
142
    }
 
143
}
 
144
 
 
145
/* ------------------------------------------------------------------------- */
 
146
/* vals in pixels */
 
147
/* ref is front plane */
 
148
void
 
149
draw_3d_area(gdImagePtr         im,
 
150
              int                               x1,
 
151
              int                               x2,
 
152
              int                               y0,                     // drawn from 0
 
153
              int                               y1,
 
154
              int                               y2,
 
155
              int                               xdepth,
 
156
              int                               ydepth,
 
157
              int                               clr,
 
158
              int                               clrshd)
 
159
{
 
160
 
 
161
  gdPoint     poly[4];
 
162
  int                   y_intercept = 0;                                                                        // if xdepth || ydepth
 
163
 
 
164
  if(xdepth || ydepth)
 
165
    {
 
166
      float             line_slope   = x2==x1?    MAXFLOAT: (float)-(y2-y1) / (float)(x2-x1);
 
167
      float             depth_slope  = xdepth==0? MAXFLOAT: (float)ydepth/(float)xdepth;
 
168
 
 
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:
 
172
        0;                                                                              // never
 
173
 
 
174
      // edging along y0 depth
 
175
      gdImageLine(im, x1+xdepth, y0-ydepth, x2+xdepth, y0-ydepth, clrshd);
 
176
 
 
177
      SET_3D_POLY(poly, x1, x2, y1, y2, xdepth, ydepth);                // top
 
178
      gdImageFilledPolygon(im, poly, 4, line_slope>depth_slope? clrshd: clr);
 
179
 
 
180
      SET_3D_POLY(poly, x1, x2, y0, y0, xdepth, ydepth+1);      // along y axis
 
181
      gdImageFilledPolygon(im, poly, 4, clr);
 
182
 
 
183
      SET_3D_POLY(poly, x2, x2, y0, y2, xdepth, ydepth);                // side
 
184
      gdImageFilledPolygon(im, poly, 4, clrshd);
 
185
 
 
186
      if(y_intercept)
 
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
 
191
 
 
192
      // SET_3D_POLY(poly, x2, x2, y0, y2, xdepth, ydepth);     // side
 
193
      // gdImageFilledPolygon(im, poly, 4, clrshd);
 
194
 
 
195
      gdImageLine(im, x1, y1, x1+xdepth, y1-ydepth, clrshd);    // edging
 
196
      gdImageLine(im, x2, y2, x2+xdepth, y2-ydepth, clrshd);    // edging
 
197
    }
 
198
 
 
199
  if(y1 == y2)                                                                                          // bar rect
 
200
    SET_RECT(poly, x1, x2, y0, y1);                                                     // front
 
201
  else
 
202
    {
 
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;
 
207
    }
 
208
  gdImageFilledPolygon(im, poly, 4, clr);
 
209
 
 
210
  gdImageLine(im, x1, y0, x2, y0, clrshd);                      // edging along y0
 
211
 
 
212
  if((xdepth || ydepth) &&                                                              // front edging only on 3D
 
213
      (y1<y0 || y2<y0))                                                                 // and only above y0
 
214
    {
 
215
      if(y1 > y0 && y2 < y0)                                                    // line crosses from below y0
 
216
        gdImageLine(im, y_intercept, y0, x2, y2, clrshd);
 
217
      else
 
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);
 
222
    }
 
223
}
 
224
 
 
225
/* ------------------------------------------------------------------------- */
 
226
/* vals in pixels */
 
227
/* ref is front plane */
 
228
void
 
229
draw_3d_bar(gdImagePtr                  im,
 
230
             int                                x1,
 
231
             int                                x2,
 
232
             int                                y0,
 
233
             int                                yhigh,
 
234
             int                                xdepth,
 
235
             int                                ydepth,
 
236
             int                                clr,
 
237
             int                                clrshd)
 
238
{
 
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
 
244
 
 
245
  gdPoint     poly[4];
 
246
  int                   usd = MIN(y0, yhigh);                                                           // up-side-down bars
 
247
 
 
248
 
 
249
  if(xdepth || ydepth)
 
250
    {
 
251
      if(y0 != yhigh)                                                                                   // 0 height?
 
252
        {
 
253
          SET_3D_BAR(poly, x2, x2, y0, yhigh, xdepth, ydepth);  // side
 
254
          gdImageFilledPolygon(im, poly, 4, clrshd);
 
255
        }
 
256
 
 
257
      SET_3D_BAR(poly, x1, x2, usd, usd, xdepth, ydepth);               // top
 
258
      gdImageFilledPolygon(im, poly, 4, clr);
 
259
    }
 
260
 
 
261
  SET_RECT(poly, x1, x2, y0, yhigh);                                                    // front
 
262
  gdImageFilledPolygon(im, poly, 4, clr);
 
263
 
 
264
  if(xdepth || ydepth)
 
265
    gdImageLine(im, x1, usd, x2, usd, clrshd);
 
266
}
 
267
 
 
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;
 
273
 return 0; }
 
274
 
 
275
/* ------------------------------------------------------------------------- */
 
276
// simple two-point linear interpolation
 
277
// attempts between first, then nearest
 
278
void
 
279
do_interpolations(int           num_points,
 
280
                   int          interp_point,
 
281
                   float        vals[])
 
282
{
 
283
  int           i, j;
 
284
  float v1 = GDC_NOVALUE,
 
285
    v2 = GDC_NOVALUE;
 
286
  int           p1 = -1,
 
287
    p2 = -1;
 
288
 
 
289
  // find backwards
 
290
  for(i=interp_point-1; i>=0 && p1==-1; --i)
 
291
    if(vals[i] != GDC_NOVALUE && vals[i] != GDC_INTERP_VALUE)
 
292
      {
 
293
        v1 = vals[i];
 
294
        p1 = i;
 
295
      }
 
296
  // find forwards
 
297
  for(j=interp_point+1; j<num_points && p2==-1; ++j)
 
298
    if(vals[j] != GDC_NOVALUE && vals[j] != GDC_INTERP_VALUE)
 
299
      {
 
300
        v2 = vals[j];
 
301
        p2 = j;
 
302
      }
 
303
  // no forward sample, find backwards
 
304
  for(; i>=0 && p2==-1; --i)
 
305
    if(vals[i] != GDC_NOVALUE && vals[i] != GDC_INTERP_VALUE)
 
306
      {
 
307
        v2 = vals[i];
 
308
        p2 = i;
 
309
      }
 
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)
 
313
      {
 
314
        v1 = vals[j];
 
315
        p1 = j;
 
316
      }
 
317
  if(p1==-1 || p2==-1 ||                                                        // need both
 
318
      p1 == p2)                                                                 // idiot
 
319
    {
 
320
      vals[interp_point] = GDC_NOVALUE;
 
321
      return;
 
322
    }
 
323
 
 
324
  // Point-slope formula
 
325
  vals[interp_point] = ((v2-v1)/(float)(p2-p1)) * (float)(interp_point-p1) + v1;
 
326
  return;
 
327
}
 
328
 
 
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
 
336
int
 
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)
 
340
           GDC_CHART_T  type,
 
341
           int                  num_points,     // points along x axis (even iterval)
 
342
           //   all arrays dependant on this
 
343
           char                 *xlbl[],                // array of xlabels
 
344
           int                  num_sets,
 
345
           ...)
 
346
{
 
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);
 
353
 
 
354
  char  do_fb  = (type == GDC_FLOATINGBAR ||
 
355
                   type == GDC_3DFLOATINGBAR);
 
356
 
 
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);
 
367
 
 
368
  int           num_arrays = num_sets * (do_hlc? 3:
 
369
                                         do_fb?  2: 1);
 
370
 
 
371
  float data[num_arrays*num_points],
 
372
    *combo_data = (float*)NULL;
 
373
 
 
374
  va_list       ap;
 
375
  int           i;
 
376
 
 
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));
 
380
  if(do_vol)
 
381
    combo_data = va_arg(ap, float*);
 
382
  va_end(ap);
 
383
 
 
384
  return GDC_out_graph(IMGWIDTH,
 
385
                        IMGHEIGHT,
 
386
                        img_fptr,
 
387
                        type,
 
388
                        num_points,
 
389
                        xlbl,
 
390
                        num_sets,
 
391
                        data,
 
392
                        combo_data);
 
393
}
 
394
 
 
395
/* ------------------------------------------------------------------------- */
 
396
// multi array interface
 
397
int
 
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)
 
401
               GDC_CHART_T      type,
 
402
               int                      num_points,     // points along x axis (even iterval)
 
403
               //       all arrays dependant on this
 
404
               char                     *xlbl[],                // array of xlabels
 
405
               int                      num_sets,
 
406
               float            *data,                  // (float*) cast on multi-dim array (num_sets > 1)
 
407
               float            *combo_data)    // only used on COMBO chart types
 
408
{
 
409
  int                   i, j, k;
 
410
 
 
411
  int                   graphwidth;
 
412
  int                   grapheight;
 
413
  gdImagePtr    im;
 
414
  gdImagePtr    bg_img = NULL;
 
415
 
 
416
  float         xorig, yorig, vyorig;
 
417
  float         yscl     = 0.0;
 
418
  float         vyscl    = 0.0;
 
419
  float         xscl     = 0.0;
 
420
  float         vhighest = -MAXFLOAT;
 
421
  float         vlowest  = MAXFLOAT;
 
422
  float         highest  = -MAXFLOAT;
 
423
  float         lowest   = MAXFLOAT;
 
424
  gdPoint     volpoly[4];
 
425
 
 
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          ||
 
438
                           type == GDC_3DBAR           ||
 
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:
 
448
    1;
 
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
 
464
  int                   annote_len     = 0,
 
465
    annote_hgt     = 0;
 
466
 
 
467
  /* args */
 
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
 
483
                      num_sets ];
 
484
  float         *uvol;
 
485
 
 
486
  int                   BGColor,
 
487
    LineColor,
 
488
    PlotColor,
 
489
    GridColor,
 
490
    VolColor,
 
491
    ExtVolColor[num_points],
 
492
    ThumbDColor,
 
493
    ThumbLblColor,
 
494
    ThumbUColor,
 
495
    //                          ArrowDColor,
 
496
    //                          ArrowUColor,
 
497
    AnnoteColor,
 
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];
 
502
 
 
503
  /* idiot checks */
 
504
  if(IMGWIDTH<=0 || IMGHEIGHT<=0 || (!img_fptr && GDC_generate_img))
 
505
    return -1;
 
506
  if(num_points <= 0)
 
507
    {
 
508
      out_err(IMGWIDTH, IMGHEIGHT, img_fptr, GDC_BGColor, GDC_LineColor, "No Data Available");
 
509
      return 1;
 
510
    }
 
511
 
 
512
  load_font_conversions();
 
513
  if(GDC_thumbnail)
 
514
    {
 
515
      GDC_grid = FALSE;
 
516
      GDC_xaxis = FALSE;
 
517
      GDC_yaxis = FALSE;
 
518
    }
 
519
 
 
520
  // ----- get args -----
 
521
  for(i=0; i<num_sets; ++i)
 
522
    uvals[i] = data+i*num_points;
 
523
  if(do_vol)
 
524
    if(!combo_data)
 
525
      return -2;
 
526
    else
 
527
      uvol = combo_data;
 
528
 
 
529
  /* ----- calculate interpretations first ----- */
 
530
  if(GDC_interpolations)
 
531
    {
 
532
      for(i=0; i<num_sets; ++i)
 
533
        for(j=0; j<num_points; ++j)
 
534
          if(uvals[i][j] == GDC_INTERP_VALUE)
 
535
            {
 
536
              do_interpolations(num_points, j, uvals[i]);
 
537
            }
 
538
      if(do_vol)
 
539
        for(j=0; j<num_points; ++j)
 
540
          if(uvol[j] == GDC_INTERP_VALUE)
 
541
            {
 
542
              do_interpolations(num_points, j, uvol);
 
543
            }
 
544
    }
 
545
 
 
546
  /* ----- highest & lowest values ----- */
 
547
  if(GDC_stack_type == GDC_STACK_SUM)           // need to walk sideways
 
548
    for(j=0; j<num_points; ++j)
 
549
      {
 
550
        float   set_sum = 0.0;
 
551
        for(i=0; i<num_sets; ++i)
 
552
          if(uvals[i][j] != GDC_NOVALUE)
 
553
            {
 
554
              set_sum += uvals[i][j];
 
555
              highest = MAX(highest, set_sum);
 
556
              lowest  = MIN(lowest,  set_sum);
 
557
            }
 
558
      }
 
559
  else
 
560
    if(GDC_stack_type == GDC_STACK_LAYER)               // need to walk sideways
 
561
      for(j=0; j<num_points; ++j)
 
562
        {
 
563
          float neg_set_sum = 0.0,
 
564
            pos_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];
 
569
              else
 
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));
 
573
        }
 
574
    else
 
575
      for(i=0; i<num_sets; ++i)
 
576
        for(j=0; j<num_points; ++j)
 
577
          if(uvals[i][j] != GDC_NOVALUE)
 
578
            {
 
579
              highest = MAX(uvals[i][j], highest);
 
580
              lowest  = MIN(uvals[i][j], lowest);
 
581
            }
 
582
  if(GDC_scatter)
 
583
    for(i=0; i<GDC_num_scatter_pts; ++i)
 
584
      {
 
585
        highest = MAX((GDC_scatter+i)->val, highest);
 
586
        lowest  = MIN((GDC_scatter+i)->val, lowest );
 
587
      }
 
588
  if(do_vol)                                                            // for now only one combo set allowed
 
589
    {
 
590
      // vhighest = 1.0;
 
591
      // vlowest  = 0.0;
 
592
      for(j=0; j<num_points; ++j)
 
593
        if(uvol[j] != GDC_NOVALUE)
 
594
          {
 
595
            vhighest = MAX(uvol[j], vhighest);
 
596
            vlowest  = MIN(uvol[j], vlowest);
 
597
          }
 
598
      if(vhighest == -MAXFLOAT)                         // no values
 
599
        vhighest = 1.0;                                         // for scaling, need a range
 
600
      if(vlowest == MAXFLOAT)
 
601
        vlowest = 0.0;
 
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)
 
610
        if(vhighest < 0.0)
 
611
          vhighest = 0.0;
 
612
        else
 
613
          if(vlowest > 0.0)
 
614
            vlowest = 0.0;                                              // bar, area should always start at 0
 
615
    }
 
616
 
 
617
  if(lowest == MAXFLOAT)
 
618
    lowest = 0.0;
 
619
  if(highest == -MAXFLOAT)
 
620
    highest = 1.0;                                                      // need a range
 
621
  if(type == GDC_AREA  ||                                       // bars and area should always start at 0
 
622
      type == GDC_BAR   ||
 
623
      type == GDC_3DBAR ||
 
624
      type == GDC_3DAREA)
 
625
    if(highest < 0.0)
 
626
      highest = 0.0;
 
627
    else
 
628
      if(lowest > 0.0)                                          // negs should be drawn from 0
 
629
        lowest = 0.0;
 
630
 
 
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;
 
635
 
 
636
 
 
637
  /* ----- graph height and width within the img height width ----- */
 
638
  /* grapheight/height is the actual size of the scalable graph */
 
639
  {
 
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
 
642
      + 2:
 
643
      2;
 
644
    int xlabel_hgt = 0;
 
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;
 
648
    int ylabel_wth = 0;
 
649
    int vlabel_wth = 0;
 
650
 
 
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;
 
654
 
 
655
 
 
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;
 
669
 
 
670
    if(GDC_xaxis && xlbl)
 
671
      {
 
672
        int biggest     = -MAXINT;
 
673
 
 
674
        for(i=0; i<num_points; ++i)
 
675
          {
 
676
            int         len = 0;
 
677
            // longest "...\n" segment
 
678
            for(len=0, j=0; xlbl[i][j]; ++len, ++j)
 
679
              if(xlbl[i][j] == '\n')
 
680
                {
 
681
                  biggest = MAX(len, biggest);
 
682
                  ++num_lf_xlbls;
 
683
                  len = 0;
 
684
                }
 
685
            biggest = MAX(len, biggest);                                // last seg
 
686
          }
 
687
        xlabel_hgt = 1+ biggest*GDC_fontc[GDC_xaxisfont_size].w +1;
 
688
      }
 
689
 
 
690
    grapheight = IMGHEIGHT - (xtics          +
 
691
                               xtitle_hgt     +
 
692
                               xlabel_hgt     +
 
693
                               title_hgt      +
 
694
                               annote_hgt     +
 
695
                               ydepth_3Dtotal +
 
696
                               2);
 
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 ----- */
 
702
    {
 
703
      float     tmp_highest;
 
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,
 
709
                              10000000 };
 
710
#define NUM_YPOINTS     (sizeof(ypoints) / sizeof(float))
 
711
      int               max_num_ylbls;
 
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)
 
717
        {
 
718
          /* gdImageDestroy(im);                haven't yet created it */
 
719
          out_err(IMGWIDTH, IMGHEIGHT,
 
720
                   img_fptr,
 
721
                   GDC_BGColor, GDC_LineColor,
 
722
                   "Insificient Height");
 
723
          return 2;
 
724
        }
 
725
 
 
726
      /* one "space" interval above + below */
 
727
      for(i=1; i<NUM_YPOINTS; ++i)
 
728
        // if(ypoints[i] > ylbl_interval)
 
729
        //      break;
 
730
        if((highest-lowest)/ypoints[i] < ((float)max_num_ylbls-(1.0+1.0))
 
731
            * (float)GDC_ylabel_density/100.0)
 
732
          break;
 
733
      /* gotta go through the above loop to catch the 'tweeners :-| */
 
734
 
 
735
      ylbl_interval = GDC_requested_yinterval != GDC_NOVALUE &&
 
736
        GDC_requested_yinterval > ypoints[i-1]?   GDC_requested_yinterval:
 
737
        ypoints[i-1];
 
738
 
 
739
      /* perform floating point remainders */
 
740
      /* gonculate largest interval-point < lowest */
 
741
      if(lowest != 0.0 &&
 
742
          lowest != GDC_requested_ymin)
 
743
        {
 
744
          if(lowest < 0.0)
 
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);
 
750
        }
 
751
      /* find smallest interval-point > highest */
 
752
      tmp_highest = lowest;
 
753
      do        // while((tmp_highest += ylbl_interval) <= highest)
 
754
        {
 
755
          int           nmrtr, dmntr, whole;
 
756
          char  *price_to_str(float, int*, int*, int*, char*);
 
757
          int           lbl_len;
 
758
          char  foo[32];
 
759
 
 
760
          if(GDC_yaxis)
 
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,
 
764
                                                                &nmrtr,
 
765
                                                                &dmntr,
 
766
                                                                &whole,
 
767
                                                                do_ylbl_fractions? NULL: GDC_ylabel_fmt)):
 
768
                strlen(foo);
 
769
              longest_ylblen = MAX(longest_ylblen, lbl_len);
 
770
            }
 
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);
 
775
 
 
776
      if(do_vol)
 
777
        {
 
778
          float num_yintrvls = (highest-lowest) / ylbl_interval;
 
779
          /* no skyscrapers */
 
780
          if(vhighest != 0.0)
 
781
            vhighest += (vhighest-vlowest) / (num_yintrvls*2.0);
 
782
          if(vlowest != 0.0)
 
783
            vlowest -= (vhighest-vlowest) / (num_yintrvls*2.0);
 
784
 
 
785
          if(GDC_yaxis2)
 
786
            {
 
787
              char      svlongest[32];
 
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);
 
790
 
 
791
              vlabel_wth = 1
 
792
                + MAX(lbl_len_low,lbl_len_high) * GDC_fontc[GDC_yaxisfont_size].w;
 
793
            }
 
794
        }
 
795
    }
 
796
 
 
797
    graphwidth = IMGWIDTH - (((GDC_hard_size && GDC_hard_xorig)? GDC_hard_xorig:
 
798
                                (ytitle_hgt +
 
799
                                  ylabel_wth +
 
800
                                  ytics))
 
801
                              + vtics
 
802
                              + vtitle_hgt
 
803
                              + vlabel_wth
 
804
                              + xdepth_3Dtotal);
 
805
    if(GDC_hard_size && GDC_hard_graphwidth)                            /* user wants to use his */
 
806
      graphwidth = GDC_hard_graphwidth;
 
807
    GDC_hard_graphwidth = graphwidth;
 
808
 
 
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);
 
813
    if(do_vol)
 
814
      {
 
815
        float   hilow_diff = vhighest-vlowest==0.0? 1.0: vhighest-vlowest;
 
816
 
 
817
        vyscl = -((float)grapheight) / hilow_diff;
 
818
        vyorig = (float)grapheight
 
819
          + ABS(vyscl) * MIN(vlowest,vhighest)
 
820
          + ydepth_3Dtotal
 
821
          + title_hgt
 
822
          + annote_hgt;
 
823
      }
 
824
    xorig = (float)(IMGWIDTH - (graphwidth +
 
825
                                  vtitle_hgt +
 
826
                                  vtics      +
 
827
                                  vlabel_wth));
 
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)
 
834
      + ydepth_3Dtotal
 
835
      + title_hgt
 
836
      + annote_hgt;
 
837
    //????      if(GDC_hard_size && GDC_hard_yorig)                                     /* vyorig too? */
 
838
    //????              yorig = GDC_hard_yorig;
 
839
    GDC_hard_yorig = yorig;
 
840
 
 
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));
 
843
  }
 
844
  // scaled, sized, ready
 
845
 
 
846
 
 
847
  /* ----- OK start the graphic ----- */
 
848
  if((GDC_hold_img & GDC_REUSE_IMAGE) &&
 
849
      GDC_image != (void*)NULL)
 
850
    im = GDC_image;
 
851
  else
 
852
    im = gdImageCreate(IMGWIDTH, IMGHEIGHT);
 
853
 
 
854
 
 
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);
 
859
  if(do_vol)
 
860
    {
 
861
      VolColor     = clrallocate(im, GDC_VolColor);
 
862
      for(i=0; i<num_points; ++i)
 
863
        if(GDC_ExtVolColor)
 
864
          ExtVolColor[i] = clrallocate(im, GDC_ExtVolColor[i]);
 
865
        else
 
866
          ExtVolColor[i] = VolColor;
 
867
    }
 
868
  //    ArrowDColor    = gdImageColorAllocate(im, 0xFF,    0, 0);
 
869
  //    ArrowUColor    = gdImageColorAllocate(im,    0, 0xFF, 0);
 
870
  if(GDC_annotation)
 
871
    AnnoteColor = clrallocate(im, GDC_annotation->color);
 
872
 
 
873
  /* attempt to import optional background image */
 
874
  if(GDC_BGImage)
 
875
    {
 
876
      FILE      *in = fopen(GDC_BGImage, "rb");
 
877
      if(!in)
 
878
        {
 
879
          ; // Cant load background image, drop it
 
880
        }
 
881
      else
 
882
        {
 
883
          // assume PNG
 
884
          // should determine type by file extension, option, ...
 
885
          if(bg_img = gdImageCreateFromPng(in))                                 // =
 
886
            {
 
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;
 
889
 
 
890
 
 
891
              if(gdImageSX(bg_img) > IMGWIDTH ||                                // resize only if too big
 
892
                  gdImageSY(bg_img) > IMGHEIGHT)                                //  [and center]
 
893
                {
 
894
                  gdImageCopyResized(im, bg_img,                                // dst, src
 
895
                                      bgxpos, bgypos,                   // dstX, dstY
 
896
                                      0, 0,                                     // srcX, srcY
 
897
                                      IMGWIDTH, IMGHEIGHT,      // dstW, dstH
 
898
                                      IMGWIDTH, IMGHEIGHT);     // srcW, srcH
 
899
                }
 
900
              else                                                                                      // just center
 
901
                gdImageCopy(im, bg_img,                                 // dst, src
 
902
                             bgxpos, bgypos,                            // dstX, dstY
 
903
                             0, 0,                                                      // srcX, srcY
 
904
                             IMGWIDTH, IMGHEIGHT);                      // W, H
 
905
            }
 
906
          fclose(in);
 
907
        }
 
908
    }
 
909
 
 
910
  for(j=0; j<num_sets; ++j)
 
911
    for(i=0; i<num_points; ++i)
 
912
      if(GDC_ExtColor)
 
913
        {
 
914
          unsigned long ext_clr = *(GDC_ExtColor+num_points*j+i);
 
915
 
 
916
          ExtColor[j][i]            = clrallocate(im, ext_clr);
 
917
          if(threeD)
 
918
            ExtColorShd[j][i]     = clrshdallocate(im, ext_clr);
 
919
        }
 
920
      else if(GDC_SetColor)
 
921
        {
 
922
          int   set_clr = GDC_SetColor[j];
 
923
          ExtColor[j][i]     = clrallocate(im, set_clr);
 
924
          if(threeD)
 
925
            ExtColorShd[j][i] = clrshdallocate(im, set_clr);
 
926
        }
 
927
      else
 
928
        {
 
929
          ExtColor[j][i]     = PlotColor;
 
930
          if(threeD)
 
931
            ExtColorShd[j][i] = clrshdallocate(im, GDC_PlotColor);
 
932
        }
 
933
 
 
934
 
 
935
  if(GDC_transparent_bg)
 
936
    gdImageColorTransparent(im, BGColor);
 
937
 
 
938
  if(GDC_title)
 
939
    {
 
940
      int       tlen;
 
941
      int       titlecolor = clrallocate(im, GDC_TitleColor);
 
942
 
 
943
      cnt_nl(GDC_title, &tlen);
 
944
      GDCImageStringNL(im,
 
945
                        &GDC_fontc[GDC_title_size],
 
946
                        IMGWIDTH/2 - tlen*GDC_fontc[GDC_title_size].w/2,
 
947
                        0,
 
948
                        GDC_title,
 
949
                        titlecolor,
 
950
                        GDC_JUSTIFY_CENTER);
 
951
    }
 
952
  if(GDC_xtitle)
 
953
    {
 
954
      int       titlecolor = GDC_XTitleColor==GDC_DFLTCOLOR?
 
955
        PlotColor: clrallocate(im, GDC_XTitleColor);
 
956
      gdImageString(im,
 
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,
 
960
                     GDC_xtitle,
 
961
                     titlecolor);
 
962
    }
 
963
 
 
964
 
 
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)))
 
970
    {
 
971
      int       x1, x2,
 
972
        y1, y2;
 
973
 
 
974
      x1 = PX(0);
 
975
      y1 = PY(lowest);
 
976
 
 
977
      setno = set_depth;
 
978
      x2 = PX(0);
 
979
      y2 = PY(lowest);
 
980
 
 
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);
 
986
      setno = 0;
 
987
    }
 
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);
 
995
 
 
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
 
1000
        {
 
1001
          if(i == -1)   if(lowest >= 0.0)                                       //      all pos plotting
 
1002
            continue;
 
1003
          else
 
1004
            tmp_y = MIN(0, highest);            //      step down to lowest
 
1005
 
 
1006
          if(i == 1)    if(highest <= 0.0)                              //      all neg plotting
 
1007
            continue;
 
1008
          else
 
1009
            tmp_y = MAX(0, lowest);             //      step up to highest
 
1010
 
 
1011
 
 
1012
          //                    if(!(highest > 0 && lowest < 0))                                        // doesn't straddle 0
 
1013
          //                            {
 
1014
          //                            if(i == -1)                                                                     // only do once: normal
 
1015
          //                                    continue;
 
1016
          //                            }
 
1017
          //                    else
 
1018
          //                            tmp_y = 0;
 
1019
 
 
1020
          do    // while((tmp_y (+-)= ylbl_interval) < [highest,lowest])
 
1021
            {
 
1022
              int               n, d, w;
 
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;
 
1026
 
 
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)
 
1030
                {
 
1031
                  snprintf(nmrtr,sizeof(nmrtr) ,"%d", n);
 
1032
                  snprintf(dmntr,sizeof(dmntr), "%d", d);
 
1033
                  snprintf(whole,sizeof(whole), "%d", w);
 
1034
                }
 
1035
 
 
1036
              if(GDC_grid || GDC_ticks)
 
1037
                {
 
1038
                  int   x1, x2, y1, y2;
 
1039
                  // int        gridline_clr = tmp_y == 0.0? LineColor: GridColor;
 
1040
                  // tics
 
1041
                  x1 = PX(0);           y1 = PY(tmp_y);
 
1042
                  if(GDC_ticks)
 
1043
                    gdImageLine(im, x1-2, y1, x1, y1, GridColor);
 
1044
                  if(GDC_grid)
 
1045
                    {
 
1046
                      setno = set_depth;
 
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
 
1051
                    }
 
1052
                }
 
1053
              if(GDC_yaxis)
 
1054
                if(do_ylbl_fractions)
 
1055
                  {
 
1056
                    if(w || (!w && !n && !d))
 
1057
                      {
 
1058
                        gdImageString(im,
 
1059
                                       GDC_fontc[GDC_yaxisfont_size].f,
 
1060
                                       PX(0)-2-strlen(whole)*GDC_fontc[GDC_yaxisfont_size].w
 
1061
                                       - ((!all_whole)?
 
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) :
 
1065
                                           1),
 
1066
                                       PY(tmp_y)-GDC_fontc[GDC_yaxisfont_size].h/2,
 
1067
                                       whole,
 
1068
                                       labelcolor);
 
1069
                      }
 
1070
                    if(n)
 
1071
                      {
 
1072
                        gdImageString(im,
 
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,
 
1078
                                       nmrtr,
 
1079
                                       labelcolor);
 
1080
                        gdImageString(im,
 
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,
 
1085
                                       "/",
 
1086
                                       labelcolor);
 
1087
                        gdImageString(im,
 
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,
 
1091
                                       dmntr,
 
1092
                                       labelcolor);
 
1093
                      }
 
1094
                  }
 
1095
                else
 
1096
                  gdImageString(im,
 
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,
 
1100
                                 ylbl_str,
 
1101
                                 labelcolor);
 
1102
 
 
1103
 
 
1104
              if(do_vol && GDC_yaxis2)
 
1105
                {
 
1106
                  char  vylbl[16];
 
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);
 
1111
 
 
1112
                  setno = set_depth;
 
1113
                  if(GDC_ticks)
 
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 */
 
1117
                    strcpy(vylbl, "0");
 
1118
                  gdImageString(im,
 
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,
 
1122
                                 vylbl,
 
1123
                                 label2color);
 
1124
                  setno = 0;
 
1125
                }
 
1126
            }
 
1127
          while(((i>0) && ((tmp_y += ylbl_interval) < highest)) ||
 
1128
                 ((i<0) && ((tmp_y -= ylbl_interval) > lowest)));
 
1129
        }
 
1130
 
 
1131
      /* catch last (bottom) grid line - specific to an "off" requested interval */
 
1132
      if(GDC_grid && threeD)
 
1133
        {
 
1134
          setno = set_depth;
 
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
 
1137
        }
 
1138
 
 
1139
      /* vy axis title */
 
1140
      if(do_vol && GDC_ytitle2)
 
1141
        {
 
1142
          int   titlecolor = GDC_YTitle2Color==GDC_DFLTCOLOR?
 
1143
            VolColor: clrallocate(im, GDC_YTitle2Color);
 
1144
          gdImageStringUp(im,
 
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 +
 
1148
                           grapheight/2,
 
1149
                           GDC_ytitle2,
 
1150
                           titlecolor);
 
1151
        }
 
1152
 
 
1153
      /* y axis title */
 
1154
      if(GDC_yaxis && GDC_ytitle)
 
1155
        {
 
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);
 
1159
          gdImageStringUp(im,
 
1160
                           GDC_fontc[GDC_ytitle_size].f,
 
1161
                           0,
 
1162
                           IMGHEIGHT/2 + ytit_len/2,
 
1163
                           GDC_ytitle,
 
1164
                           titlecolor);
 
1165
        }
 
1166
    }
 
1167
 
 
1168
  /* interviening set grids */
 
1169
  /*  0 < setno < num_sets   non-inclusive, they've already been covered */
 
1170
  if(GDC_grid && threeD)
 
1171
    {
 
1172
      for(setno=set_depth - 1;
 
1173
           setno > 0;
 
1174
           --setno)
 
1175
        {
 
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);
 
1178
        }
 
1179
      setno = 0;
 
1180
    }
 
1181
 
 
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))))
 
1185
    {
 
1186
      int       x1, x2, y1, y2;
 
1187
      // tics
 
1188
      x1 = PX(0);               y1 = PY(0);
 
1189
      if(GDC_ticks)
 
1190
        gdImageLine(im, x1-2, y1, x1, y1, LineColor);
 
1191
      setno = set_depth;
 
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
 
1196
    }
 
1197
 
 
1198
 
 
1199
  /* x ticks and xlables */
 
1200
  if(GDC_grid || GDC_xaxis)
 
1201
    {
 
1202
      int               num_xlbls =                                                                             /* maximum x lables that'll fit */
 
1203
        /* each xlbl + avg due to num_lf_xlbls */
 
1204
        graphwidth /
 
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);
 
1209
 
 
1210
      for(i=0; i<num_points+(do_bar?2:0); ++i)
 
1211
        {
 
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);                              \
 
1216
                                                                                        else
 
1217
#define DO_GRID(x1,y1,x2,y2)    if(GDC_grid)                                                                                                    \
 
1218
                                                                                                {                                                                                                                       \
 
1219
                                                                                                gdImageLine(im, x1, y1, x2,  y2, GridColor); /* depth */        \
 
1220
                                                                                                gdImageLine(im, x2, y2, x2,  yh, GridColor);                            \
 
1221
                                                                                                }                                                                                                                       \
 
1222
                                                                                        else
 
1223
 
 
1224
          x1 = PX(i);           y1 = PY(lowest);
 
1225
          setno = set_depth;
 
1226
          x2 = PX(i);           y2 = PY(lowest);        yh = PY(highest);
 
1227
          setno = 0;                                                                                            // reset to foremost
 
1228
 
 
1229
          if(i == 0)                                                                                    // catch 3D Y back corner
 
1230
            DO_GRID(x1,y1,x2,y2);
 
1231
 
 
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))
 
1236
              ||
 
1237
              (GDC_xlabel_ctl && xi>=0 && *(GDC_xlabel_ctl+xi)))
 
1238
            {
 
1239
              DO_TICK(x1,y1);                                                                                   // labeled points tick & grid
 
1240
              DO_GRID(x1,y1,x2,y2);
 
1241
 
 
1242
              if(!do_bar || (i>0 && xi<num_points))
 
1243
                if(GDC_xaxis && xlbl && xlbl[xi] && *(xlbl[xi]))
 
1244
                  {
 
1245
                    /* waiting for GDCImageStringUpNL() */
 
1246
#define LBX             GDC_fontc[GDC_xaxisfont_size]
 
1247
                    int         xlen = 0;
 
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));
 
1252
 
 
1253
                    xlen      = -1;
 
1254
                    xstrs_num = -1;
 
1255
                    j = -1;
 
1256
                    do
 
1257
                      {
 
1258
                        ++j;
 
1259
                        ++xlen;
 
1260
                        sub_xlbl[xlen] = xlbl[xi][j];
 
1261
                        if(xlbl[xi][j] == '\n' ||
 
1262
                            xlbl[xi][j] == '\0')
 
1263
                          {
 
1264
                            sub_xlbl[xlen] = '\0';
 
1265
                            ++xstrs_num;
 
1266
                            gdImageStringUp(im,
 
1267
                                             LBX.f,
 
1268
                                             xlbl_strt + (LBX.h-1)*xstrs_num,
 
1269
                                             PY(lowest) + 2 + 1 + LBX.w*xlen,
 
1270
                                             sub_xlbl,
 
1271
                                             labelcolor);
 
1272
                            xlen = -1;
 
1273
                          }
 
1274
                      } while(xlbl[xi][j]);
 
1275
#undef LBX
 
1276
                  }
 
1277
            }
 
1278
          /* every point, on-point */
 
1279
          if(i>0)
 
1280
            {
 
1281
              if(GDC_grid == GDC_TICK_POINTS)                           /* --- GRID --- */
 
1282
                DO_GRID(x1, y1, x2, y2);
 
1283
              else if(GDC_grid > GDC_TICK_NONE)
 
1284
                {
 
1285
                  int k;
 
1286
                  int   xt;
 
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);
 
1294
                }
 
1295
 
 
1296
              if(GDC_ticks == GDC_TICK_POINTS)                          /* --- TICKS --- */
 
1297
                DO_TICK(x1,y1);
 
1298
              else if(GDC_ticks > GDC_TICK_NONE)
 
1299
                {
 
1300
                  int k;
 
1301
                  int   xt;
 
1302
                  int   prevx=PX(i-1);
 
1303
                  int   intrv_dist = (x1-prevx)/(GDC_ticks+1);
 
1304
                  DO_TICK(x1, y1);
 
1305
                  for(k=0,           xt=prevx + intrv_dist;
 
1306
                       k<GDC_ticks && xt<x1;
 
1307
                       ++k,           xt += intrv_dist)
 
1308
                    DO_TICK(xt, y1);
 
1309
                }
 
1310
            }
 
1311
        }
 
1312
    }
 
1313
 
 
1314
  /* ----- secondard data plotting (volume) ----- */
 
1315
  /*  so that grid lines appear under vol */
 
1316
  if(do_vol)
 
1317
    {
 
1318
      setno = set_depth;
 
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)
 
1323
        {
 
1324
          if(uvol[0] != GDC_NOVALUE)
 
1325
            draw_3d_bar(im, PX(0), PX(0)+hlf_barwdth,
 
1326
                         PV(0), PV(uvol[0]),
 
1327
                         0, 0,
 
1328
                         ExtVolColor[0],
 
1329
                         ExtVolColor[0]);
 
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,
 
1333
                           PV(0), PV(uvol[i]),
 
1334
                           0, 0,
 
1335
                           ExtVolColor[i],
 
1336
                           ExtVolColor[i]);
 
1337
          if(uvol[i] != GDC_NOVALUE)
 
1338
            draw_3d_bar(im, PX(i)-hlf_barwdth, PX(i),
 
1339
                         PV(0), PV(uvol[i]),
 
1340
                         0, 0,
 
1341
                         ExtVolColor[i],
 
1342
                         ExtVolColor[i]);
 
1343
        }
 
1344
      else
 
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)
 
1349
          {
 
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]),
 
1354
                              0, 0,
 
1355
                              ExtVolColor[i],
 
1356
                              ExtVolColor[i]);
 
1357
          }
 
1358
        else
 
1359
          if(type == GDC_COMBO_LINE_LINE ||
 
1360
              type == GDC_3DCOMBO_LINE_LINE)
 
1361
            {
 
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]),
 
1365
                               PX(i),   PV(uvol[i]),
 
1366
                               ExtVolColor[i]);
 
1367
            }
 
1368
      setno = 0;
 
1369
    }           // volume polys done
 
1370
 
 
1371
  if(GDC_annotation && threeD)          /* back half of annotation line */
 
1372
    {
 
1373
      int       x1 = PX(GDC_annotation->point+(do_bar?1:0)),
 
1374
        y1 = PY(lowest);
 
1375
      setno = set_depth;
 
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);
 
1379
      setno = 0;
 
1380
    }
 
1381
 
 
1382
  /* ---------- start plotting the data ---------- */
 
1383
  switch(type)
 
1384
    {
 
1385
    case GDC_3DBAR:                                     /* depth, width, y interval need to allow for whitespace between bars */
 
1386
    case GDC_BAR:
 
1387
      /* --------- */
 
1388
      switch(GDC_stack_type)
 
1389
        {
 
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,
 
1397
                             ExtColor[setno][i],
 
1398
                             threeD? ExtColorShd[setno][i]: ExtColor[setno][i]);
 
1399
          setno = 0;
 
1400
          break;
 
1401
 
 
1402
        case GDC_STACK_LAYER:
 
1403
          {
 
1404
            float       lasty[num_points];
 
1405
            j = 0;
 
1406
            //                          for(i=0; i<num_points; ++i)
 
1407
            //                                  if(uvals[j][i] != GDC_NOVALUE)
 
1408
            //                                          {
 
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,
 
1413
            //                                                                           ExtColor[j][i],
 
1414
            //                                                                           threeD? ExtColorShd[j][i]: ExtColor[j][i]);
 
1415
            //                                          }
 
1416
            for(i=0; i<num_points; ++i)
 
1417
              {
 
1418
                struct BS       barset[num_sets];
 
1419
                float           lasty_pos = 0.0;
 
1420
                float           lasty_neg = 0.0;
 
1421
                int                     k;
 
1422
 
 
1423
                for(j=0, k=0; j<num_sets; ++j)
 
1424
                  {
 
1425
                    if(uvals[j][i] != GDC_NOVALUE)
 
1426
                      {
 
1427
                        if(uvals[j][i] < 0.0)
 
1428
                          {
 
1429
                            barset[k].y1 = lasty_neg;
 
1430
                            barset[k].y2 = uvals[j][i] + lasty_neg;
 
1431
                            lasty_neg    = barset[k].y2;
 
1432
                          }
 
1433
                        else
 
1434
                          {
 
1435
                            barset[k].y1 = lasty_pos;
 
1436
                            barset[k].y2 = uvals[j][i] + lasty_pos;
 
1437
                            lasty_pos    = barset[k].y2;
 
1438
                          }
 
1439
                        barset[k].clr   = ExtColor[j][i];
 
1440
                        barset[k].shclr = threeD? ExtColorShd[j][i]: ExtColor[j][i];
 
1441
                        ++k;
 
1442
                      }
 
1443
                  }
 
1444
                qsort(barset, k, sizeof(struct BS), barcmpr);
 
1445
 
 
1446
                for(j=0; j<k; ++j)
 
1447
                  {
 
1448
                    draw_3d_bar(im,
 
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,
 
1452
                                 barset[j].clr,
 
1453
                                 barset[j].shclr);
 
1454
                  }
 
1455
              }
 
1456
          }
 
1457
          break;
 
1458
 
 
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,
 
1469
                               ExtColor[j][i],
 
1470
                               threeD? ExtColorShd[j][i]: ExtColor[j][i]);
 
1471
          }
 
1472
          break;
 
1473
        }
 
1474
      break;
 
1475
 
 
1476
    case GDC_3DFLOATINGBAR:
 
1477
    case GDC_FLOATINGBAR:
 
1478
      /* --------- */
 
1479
      switch(GDC_stack_type)
 
1480
        {
 
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,
 
1490
                             ExtColor[setno][i],
 
1491
                             threeD? ExtColorShd[setno][i]: ExtColor[setno][i]);
 
1492
          setno = 0;
 
1493
          break;
 
1494
 
 
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,
 
1507
                               ExtColor[j][i],
 
1508
                               threeD? ExtColorShd[j][i]: ExtColor[j][i]);
 
1509
          }
 
1510
          break;
 
1511
        }
 
1512
      break;
 
1513
 
 
1514
    case GDC_LINE:
 
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)
 
1521
            {
 
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]);
 
1524
            }
 
1525
          else
 
1526
            {
 
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]);
 
1531
            }
 
1532
      break;
 
1533
 
 
1534
    case GDC_3DLINE:
 
1535
    case GDC_3DCOMBO_LINE_BAR:
 
1536
    case GDC_3DCOMBO_LINE_AREA:
 
1537
    case GDC_3DCOMBO_LINE_LINE:
 
1538
      {
 
1539
        int     y1[num_sets],
 
1540
          y2[num_sets];
 
1541
 
 
1542
        for(i=1; i<num_points; ++i)
 
1543
          {
 
1544
            if(GDC_stack_type == GDC_STACK_DEPTH)
 
1545
              {
 
1546
                for(j=num_sets-1; j>=0; --j)
 
1547
                  if(uvals[j][i-1] != GDC_NOVALUE &&
 
1548
                      uvals[j][i]   != GDC_NOVALUE)
 
1549
                    {
 
1550
                      setno = j;
 
1551
                      y1[j] = PY(uvals[j][i-1]);
 
1552
                      y2[j] = PY(uvals[j][i]);
 
1553
 
 
1554
                      draw_3d_line(im,
 
1555
                                    PY(0),
 
1556
                                    PX(i-1), PX(i),
 
1557
                                    &(y1[j]), &(y2[j]),
 
1558
                                    xdepth_3D, ydepth_3D,
 
1559
                                    1,
 
1560
                                    &(ExtColor[j][i]),
 
1561
                                    &(ExtColorShd[j][i]));
 
1562
                      setno = 0;
 
1563
                    }
 
1564
              }
 
1565
            else
 
1566
              if(GDC_stack_type == GDC_STACK_BESIDE ||
 
1567
                  GDC_stack_type == GDC_STACK_SUM)                      // all same plane
 
1568
                {
 
1569
                  int           set;
 
1570
                  int           clr[num_sets],
 
1571
                    clrshd[num_sets];
 
1572
                  float usey1 = 0.0,
 
1573
                    usey2 = 0.0;
 
1574
                  for(j=0,set=0; j<num_sets; ++j)
 
1575
                    if(uvals[j][i-1] != GDC_NOVALUE &&
 
1576
                        uvals[j][i]   != GDC_NOVALUE)
 
1577
                      {
 
1578
                        if(GDC_stack_type == GDC_STACK_SUM)
 
1579
                          {
 
1580
                            usey1 += uvals[j][i-1];
 
1581
                            usey2 += uvals[j][i];
 
1582
                          }
 
1583
                        else
 
1584
                          {
 
1585
                            usey1 = uvals[j][i-1];
 
1586
                            usey2 = uvals[j][i];
 
1587
                          }
 
1588
                        y1[set]     = PY(usey1);
 
1589
                        y2[set]     = PY(usey2);
 
1590
                        clr[set]    = ExtColor[j][i];
 
1591
                        clrshd[set] = ExtColorShd[j][i];        /* fred */
 
1592
                        ++set;
 
1593
                      }
 
1594
                  draw_3d_line(im,
 
1595
                                PY(0),
 
1596
                                PX(i-1), PX(i),
 
1597
                                y1, y2,
 
1598
                                xdepth_3D, ydepth_3D,
 
1599
                                set,
 
1600
                                clr,
 
1601
                                clrshd);
 
1602
                }
 
1603
          }
 
1604
      }
 
1605
      break;
 
1606
 
 
1607
    case GDC_AREA:
 
1608
    case GDC_3DAREA:
 
1609
      switch(GDC_stack_type)
 
1610
        {
 
1611
        case GDC_STACK_SUM:
 
1612
          {
 
1613
            float       lasty[num_points];
 
1614
            j = 0;
 
1615
            for(i=1; i<num_points; ++i)
 
1616
              if(uvals[j][i] != GDC_NOVALUE)
 
1617
                {
 
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,
 
1623
                                  ExtColor[j][i],
 
1624
                                  threeD? ExtColorShd[j][i]: ExtColor[j][i]);
 
1625
                }
 
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)
 
1629
                  {
 
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,
 
1633
                                  ExtColor[j][i],
 
1634
                                  threeD? ExtColorShd[j][i]: ExtColor[j][i]);
 
1635
                    lasty[i] += uvals[j][i];
 
1636
                  }
 
1637
          }
 
1638
          break;
 
1639
 
 
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,
 
1647
                              ExtColor[j][i],
 
1648
                              threeD? ExtColorShd[j][i]: ExtColor[j][i]);
 
1649
          break;
 
1650
 
 
1651
        case GDC_STACK_DEPTH:
 
1652
        default:
 
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,
 
1659
                              ExtColor[setno][i],
 
1660
                              threeD? ExtColorShd[setno][i]: ExtColor[setno][i]);
 
1661
          setno = 0;
 
1662
        }
 
1663
      break;
 
1664
 
 
1665
    case GDC_3DHILOCLOSE:
 
1666
    case GDC_3DCOMBO_HLC_BAR:
 
1667
    case GDC_3DCOMBO_HLC_AREA:
 
1668
      {
 
1669
        gdPoint     poly[4];
 
1670
        for(j=num_groups-1; j>=0; --j)
 
1671
          {
 
1672
            for(i=1; i<num_points+1; ++i)
 
1673
              if(uvals[CLOSESET+j*3][i-1] != GDC_NOVALUE)
 
1674
                {
 
1675
                  if((GDC_HLC_style & GDC_HLC_I_CAP) &&                 // bottom half of 'I'
 
1676
                      uvals[LOWSET+j*3][i-1] != GDC_NOVALUE)
 
1677
                    {
 
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]);
 
1683
                    }
 
1684
                  // all HLC have vert line
 
1685
                  if(uvals[LOWSET+j*3][i-1] != GDC_NOVALUE)
 
1686
                    {                                                                                   // bottom 'half'
 
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]);
 
1692
                    }
 
1693
                  if(uvals[HIGHSET+j*3][i-1] != GDC_NOVALUE)
 
1694
                    {                                                                                   // top 'half'
 
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]);
 
1700
                    }
 
1701
                  // line at close
 
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]);
 
1705
                  // top half 'I'
 
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)
 
1710
                      {
 
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]);
 
1716
                      }
 
1717
 
 
1718
                  if(i < num_points &&
 
1719
                      uvals[CLOSESET+j*3][i] != GDC_NOVALUE)
 
1720
                    {
 
1721
                      if(GDC_HLC_style & GDC_HLC_CLOSE_CONNECTED)       /* line from prev close */
 
1722
                        {
 
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]);
 
1728
                        }
 
1729
                      else      // CLOSE_CONNECTED and CONNECTING are mutually exclusive
 
1730
                        if(GDC_HLC_style & GDC_HLC_CONNECTING)  /* thin connecting line */
 
1731
                          {
 
1732
                            int y1 = PY(uvals[CLOSESET+j*3][i-1]),
 
1733
                              y2 = PY(uvals[CLOSESET+j*3][i]);
 
1734
                            draw_3d_line(im,
 
1735
                                          PY(0),
 
1736
                                          PX(i-1), PX(i),
 
1737
                                          &y1, &y2,                                     // rem only 1 set
 
1738
                                          xdepth_3D, ydepth_3D,
 
1739
                                          1,
 
1740
                                          &(ExtColor[CLOSESET+j*3][i]),
 
1741
                                          &(ExtColorShd[CLOSESET+j*3][i]));
 
1742
                            // edge font of it
 
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]);
 
1746
                          }
 
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)
 
1751
                          {
 
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]);
 
1757
                          }
 
1758
                    }
 
1759
                  if(GDC_HLC_style & GDC_HLC_DIAMOND)
 
1760
                    {                                                                   // front
 
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]);
 
1771
                      // bottom side
 
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]);
 
1778
                      // top side
 
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]);
 
1785
                    }
 
1786
                }
 
1787
          }
 
1788
      }
 
1789
      break;
 
1790
 
 
1791
    case GDC_HILOCLOSE:
 
1792
    case GDC_COMBO_HLC_BAR:
 
1793
    case GDC_COMBO_HLC_AREA:
 
1794
      for(j=num_groups-1; j>=0; --j)
 
1795
        {
 
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]);
 
1807
 
 
1808
                if(GDC_HLC_style & GDC_HLC_I_CAP)
 
1809
                  {
 
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]);
 
1818
                  }
 
1819
                if(GDC_HLC_style & GDC_HLC_DIAMOND)
 
1820
                  {
 
1821
                    gdPoint         cd[4];
 
1822
 
 
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]);
 
1828
                  }
 
1829
              }
 
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)
 
1832
              {
 
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]);
 
1842
              }
 
1843
        }
 
1844
      break;
 
1845
    }
 
1846
  setno = 0;
 
1847
 
 
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 */
 
1853
  if(GDC_scatter)
 
1854
    {
 
1855
      int               scatter_clr[GDC_num_scatter_pts];
 
1856
      gdPoint   ct[3];
 
1857
 
 
1858
      for(i=0; i<GDC_num_scatter_pts; ++i)
 
1859
        {
 
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);
 
1864
 
 
1865
          if((GDC_scatter+i)->point >= num_points ||                            // invalid point
 
1866
              (GDC_scatter+i)->point <  0)
 
1867
            continue;
 
1868
          scatter_clr[i] = clrallocate(im, (GDC_scatter+i)->color);
 
1869
 
 
1870
          switch((GDC_scatter+i)->ind)
 
1871
            {
 
1872
            case GDC_SCATTER_CIRCLE:
 
1873
              {
 
1874
                long    uniq_clr = get_uniq_color(im);
 
1875
                int             s        = 0,
 
1876
                  e        = 360,
 
1877
                  fo       = 0;
 
1878
 
 
1879
                if(!do_bar)
 
1880
                  if((GDC_scatter+i)->point == 0)
 
1881
                    { s = 270; e = 270+180; fo = 1; }
 
1882
                  else
 
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
 
1886
                  {
 
1887
                    int uc = gdImageColorAllocate(im, l2gdcal(uniq_clr));
 
1888
                    gdImageArc(im, scat_x, scat_y,
 
1889
                                hlf_scatterwdth*2, hlf_scatterwdth*2,
 
1890
                                s, e,
 
1891
                                uc);
 
1892
                    if(fo)                                                                      // close off  semi-circle case
 
1893
                      gdImageLine(im, scat_x, scat_y+hlf_scatterwdth,
 
1894
                                   scat_x, scat_y-hlf_scatterwdth,
 
1895
                                   uc);
 
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,
 
1899
                                s, e,
 
1900
                                scatter_clr[i]);
 
1901
                    if(fo)
 
1902
                      gdImageLine(im, scat_x, scat_y+hlf_scatterwdth,
 
1903
                                   scat_x, scat_y-hlf_scatterwdth,
 
1904
                                   scatter_clr[i]);
 
1905
                    gdImageColorDeallocate(im, uc);
 
1906
                  }
 
1907
                else                                                                                    // chance it
 
1908
                  {
 
1909
                    gdImageArc(im, scat_x, scat_y,
 
1910
                                hlf_scatterwdth*2, hlf_scatterwdth*2,
 
1911
                                s, e,
 
1912
                                scatter_clr[i]);
 
1913
                    if(fo)
 
1914
                      gdImageLine(im, scat_x, scat_y+hlf_scatterwdth,
 
1915
                                   scat_x, scat_y-hlf_scatterwdth,
 
1916
                                   scatter_clr[i]);
 
1917
                    gdImageFillToBorder(im, scat_x+fo, scat_y,
 
1918
                                         scatter_clr[i], scatter_clr[i]);
 
1919
                  }
 
1920
              }
 
1921
              break;
 
1922
            case GDC_SCATTER_TRIANGLE_UP:
 
1923
              ct[0].x = scat_x;
 
1924
              ct[0].y = scat_y;
 
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;
 
1929
              if(!do_bar)
 
1930
                if((GDC_scatter+i)->point == 0)
 
1931
                  ct[1].x = scat_x;
 
1932
                else
 
1933
                  if((GDC_scatter+i)->point == num_points-1)
 
1934
                    ct[2].x = scat_x;
 
1935
              gdImageFilledPolygon(im, ct, 3, scatter_clr[i]);
 
1936
              break;
 
1937
            case GDC_SCATTER_TRIANGLE_DOWN:
 
1938
              ct[0].x = scat_x;
 
1939
              ct[0].y = scat_y;
 
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;
 
1944
              if(!do_bar)
 
1945
                if((GDC_scatter+i)->point == 0)
 
1946
                  ct[1].x = scat_x;
 
1947
                else
 
1948
                  if((GDC_scatter+i)->point == num_points-1)
 
1949
                    ct[2].x = scat_x;
 
1950
              gdImageFilledPolygon(im, ct, 3, scatter_clr[i]);
 
1951
              break;
 
1952
            }
 
1953
        }
 
1954
    }
 
1955
 
 
1956
 
 
1957
  // overlay with a value and an arrow (e.g., total daily change)
 
1958
#ifdef THUMB_VALS
 
1959
  /* put thmbl and thumbval over vol and plot lines */
 
1960
  if(thumbnail)
 
1961
    {
 
1962
      int     n, d, w;
 
1963
      char      thmbl[32];
 
1964
      char      *price_to_str(float, int*, int*, int*);
 
1965
      char      nmrtr[3+1], dmntr[3+1], whole[8];
 
1966
 
 
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);
 
1971
      gdImageString(im,
 
1972
                     gdFontSmall,
 
1973
                     graphwidth/2-strlen(thumblabel)*SFONTWDTH/2,
 
1974
                     1,
 
1975
                     thumblabel,
 
1976
                     ThumbLblColor);
 
1977
      if(w || n)
 
1978
        {
 
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
 
1983
             SFONTWDTH                                    +     // /
 
1984
             strlen(dmntr)*TFONTWDTH:           // denominator
 
1985
             0);                                                        // no frac part
 
1986
 
 
1987
          smallarrow(im, graphwidth/2-thmbvalwidth/2, SFONTHGT, thumbval>0.0, chgcolor);
 
1988
          if(w)
 
1989
            {
 
1990
              gdImageString(im,
 
1991
                             gdFontSmall,
 
1992
                             (graphwidth/2-thmbvalwidth/2)+SFONTWDTH,
 
1993
                             SFONTHGT+2,
 
1994
                             whole,
 
1995
                             chgcolor);
 
1996
            }
 
1997
          if(n)
 
1998
            {
 
1999
              gdImageString(im,
 
2000
                             gdFontTiny,
 
2001
                             (graphwidth/2-thmbvalwidth/2)   +  // start
 
2002
                             SFONTWDTH                                     +    // arrow
 
2003
                             (w? strlen(whole)*SFONTWDTH: 0) +  // whole
 
2004
                             2,
 
2005
                             SFONTHGT+2-2,
 
2006
                             nmrtr,
 
2007
                             chgcolor);
 
2008
              gdImageChar  (im,
 
2009
                             gdFontSmall,
 
2010
                             (graphwidth/2-thmbvalwidth/2)  +           // start
 
2011
                             SFONTWDTH                                    +             // arrow
 
2012
                             (w? strlen(whole)*SFONTWDTH: 0) +  // whole
 
2013
                             strlen(nmrtr)*TFONTWDTH,                           // numerator
 
2014
                             SFONTHGT+2,
 
2015
                             '/',
 
2016
                             chgcolor);
 
2017
              gdImageString(im,
 
2018
                             gdFontTiny,
 
2019
                             (graphwidth/2-thmbvalwidth/2)  +           // start
 
2020
                             SFONTWDTH                                    +             // arrow
 
2021
                             (w? strlen(whole)*SFONTWDTH: 0) +          // whole
 
2022
                             strlen(nmrtr)*TFONTWDTH              +             // numerator
 
2023
                             SFONTWDTH - 3,                                             // /
 
2024
                             SFONTHGT+2+4,
 
2025
                             dmntr,
 
2026
                             chgcolor);
 
2027
            }
 
2028
        }
 
2029
    }           // thumblabel, thumbval
 
2030
#endif
 
2031
 
 
2032
  /* box it off */
 
2033
  /*  after plotting so the outline covers any plot lines */
 
2034
  {
 
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);
 
2037
 
 
2038
    if(GDC_border == GDC_BORDER_ALL || (GDC_border & GDC_BORDER_TOP))
 
2039
      {
 
2040
        setno = set_depth;
 
2041
        gdImageLine(im,          PX(0),   PY(highest), PX(num_points-1+(do_bar?2:0)),  PY(highest), LineColor);
 
2042
        setno = 0;
 
2043
      }
 
2044
  }
 
2045
  if(GDC_border)
 
2046
    {
 
2047
      int       x1, y1, x2, y2;
 
2048
 
 
2049
      x1 = PX(0);
 
2050
      y1 = PY(highest);
 
2051
      x2 = PX(num_points-1+(do_bar?2:0));
 
2052
      y2 = PY(lowest);
 
2053
      if(GDC_border == GDC_BORDER_ALL || (GDC_border & GDC_BORDER_Y))
 
2054
        gdImageLine(im, x1, PY(lowest), x1, y1, LineColor);
 
2055
 
 
2056
      setno = set_depth;
 
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
 
2060
      {
 
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);
 
2066
      }
 
2067
      setno = 0;
 
2068
    }
 
2069
 
 
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))))
 
2073
    {
 
2074
      int       x2 = PX(num_points-1+(do_bar?2:0)),
 
2075
        y2 = PY(0);
 
2076
 
 
2077
      gdImageLine(im, PX(0), PY(0), x2, y2, LineColor);         // front line
 
2078
      setno = set_depth;
 
2079
      // depth for 3Ds
 
2080
      gdImageLine(im, x2, y2, PX(num_points-1+(do_bar?2:0)), PY(0), LineColor);
 
2081
      setno = 0;                                                                                                // set back to foremost
 
2082
    }
 
2083
 
 
2084
  if(GDC_annotation)                    /* front half of annotation line */
 
2085
    {
 
2086
      int               x1 = PX(GDC_annotation->point+(do_bar?1:0)),
 
2087
        y1 = PY(highest);
 
2088
      int               x2;
 
2089
      // front line
 
2090
      gdImageLine(im, x1, PY(lowest)+1, x1, y1, AnnoteColor);
 
2091
      if(threeD)
 
2092
        {                                                                                               // on back plane
 
2093
          setno = set_depth;
 
2094
          x2 = PX(GDC_annotation->point+(do_bar?1:0));
 
2095
          // prspective line
 
2096
          gdImageLine(im, x1, y1, x2, PY(highest), AnnoteColor);
 
2097
        }
 
2098
      else                                                                                              // for 3D done with back line
 
2099
        {
 
2100
          x2 = PX(GDC_annotation->point+(do_bar?1:0));
 
2101
          gdImageLine(im, x1, y1, x1, y1-2, AnnoteColor);
 
2102
        }
 
2103
      /* line-to and note */
 
2104
      if(*(GDC_annotation->note))                                               // any note?
 
2105
        {
 
2106
          if(GDC_annotation->point >= (num_points/2))           /* note to the left */
 
2107
            {
 
2108
              gdImageLine(im, x2,              PY(highest)-2,
 
2109
                           x2-annote_hgt/2, PY(highest)-2-annote_hgt/2,
 
2110
                           AnnoteColor);
 
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,
 
2116
                                AnnoteColor,
 
2117
                                GDC_JUSTIFY_RIGHT);
 
2118
            }
 
2119
          else                                                                                          /* note to right */
 
2120
            {
 
2121
              gdImageLine(im, x2,              PY(highest)-2,
 
2122
                           x2+annote_hgt/2, PY(highest)-2-annote_hgt/2,
 
2123
                           AnnoteColor);
 
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,
 
2129
                                AnnoteColor,
 
2130
                                GDC_JUSTIFY_LEFT);
 
2131
            }
 
2132
        }
 
2133
      setno = 0;
 
2134
    }
 
2135
 
 
2136
 
 
2137
  /* usually GDC_generate_img is used in conjunction with hard or hold options */
 
2138
  if(GDC_generate_img)
 
2139
    {
 
2140
      fflush(img_fptr);                 // clear anything buffered
 
2141
      switch(GDC_image_type)
 
2142
        {
 
2143
        case GDC_PNG:   gdImagePng(im, img_fptr);                                               break;
 
2144
#ifdef HAVE_JPEG
 
2145
        case GDC_JPEG:  gdImageJpeg(im, img_fptr, GDC_jpeg_quality);    break;
 
2146
#endif
 
2147
        case GDC_WBMP:  gdImageWBMP(im, PlotColor, img_fptr);                   break;
 
2148
        case GDC_GIF:
 
2149
        default:                gdImagePng(im, img_fptr);       /* gdImageGif(im, img_fptr); */
 
2150
        }
 
2151
    }
 
2152
 
 
2153
  if(bg_img)
 
2154
    gdImageDestroy(bg_img);
 
2155
  if(GDC_hold_img & GDC_EXPOSE_IMAGE)
 
2156
    GDC_image = (void*)im;
 
2157
  else
 
2158
    gdImageDestroy(im);
 
2159
  return 0;
 
2160
}