ruby-gdchart 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,158 @@
1
+ /* GDCHART 0.10.0dev GDC.H 2 Nov 2000 */
2
+ /* Copyright Bruce Verderaime 1998-2004 */
3
+
4
+ /*
5
+ General header common to chart (xy[z]) and pie
6
+ */
7
+
8
+ #ifndef _GDC_H
9
+ #define _GDC_H
10
+
11
+ #ifndef _USE_MATH_DEFINES
12
+ #define _USE_MATH_DEFINES
13
+ #endif
14
+ #ifndef _XOPEN_SOURCE
15
+ #define _XOPEN_SOURCE
16
+ #endif
17
+ #include <math.h>
18
+ /* uncle */
19
+ #ifndef M_PI
20
+ #define M_PI 3.14159265358979323846
21
+ #define M_PI_2 1.57079632679489661923
22
+ #endif
23
+
24
+ #include <limits.h>
25
+ #include <float.h>
26
+ #ifdef GDC_INCL
27
+ #include "gd.h"
28
+ #include "gdfonts.h"
29
+ #include "gdfontt.h"
30
+ #include "gdfontmb.h"
31
+ #include "gdfontg.h"
32
+ #include "gdfontl.h"
33
+ #include "array_alloc.h"
34
+ #endif
35
+
36
+ /* --- backward compatibility --- */
37
+ /* may be removed at a later date */
38
+ #define GDC_generate_gif GDC_generate_img
39
+ #define pie_gif GDC_out_pie
40
+ /* ------------------------------ */
41
+
42
+ #ifndef TRUE
43
+ #define TRUE 1
44
+ #define FALSE 0
45
+ #endif
46
+
47
+ #define GDC_NOVALUE -FLT_MAX
48
+ #define GDC_NULL GDC_NOVALUE
49
+
50
+ #define ABS( x ) ( (x)<0.0? -(x): (x) )
51
+ #define MAX( x, y ) ( (x)>(y)?(x):(y) )
52
+ #define MIN( x, y ) ( (x)<(y)?(x):(y) )
53
+ #define TO_RAD(o) ( (o)/360.0*(2.0*M_PI) )
54
+
55
+ #define GDC_NOCOLOR 0x1000000L
56
+ #define GDC_DFLTCOLOR 0x2000000L
57
+ #define PVRED 0x00FF0000
58
+ #define PVGRN 0x0000FF00
59
+ #define PVBLU 0x000000FF
60
+ #define l2gdcal( c ) ((c)&PVRED)>>16 , ((c)&PVGRN)>>8 , ((c)&0x000000FF)
61
+ #define l2gdshd( c ) (((c)&PVRED)>>16)/2 , (((c)&PVGRN)>>8)/2 , (((c)&0x000000FF))/2
62
+ static int _gdccfoo1;
63
+ static unsigned long _gdccfoo2;
64
+ #define _gdcntrst(bg) ( ((bg)&0x800000?0x000000:0xFF0000)| \
65
+ ((bg)&0x008000?0x000000:0x00FF00)| \
66
+ ((bg)&0x000080?0x000000:0x0000FF) )
67
+ #define _clrallocate( im, rawclr, bgc ) \
68
+ ( (_gdccfoo2=rawclr==GDC_DFLTCOLOR? _gdcntrst(bgc): rawclr), \
69
+ (_gdccfoo1=gdImageColorExact(im,l2gdcal(_gdccfoo2))) != -1? \
70
+ _gdccfoo1: \
71
+ gdImageColorsTotal(im) == gdMaxColors? \
72
+ gdImageColorClosest(im,l2gdcal(_gdccfoo2)): \
73
+ gdImageColorAllocate(im,l2gdcal(_gdccfoo2)) )
74
+ #define _clrshdallocate( im, rawclr, bgc ) \
75
+ ( (_gdccfoo2=rawclr==GDC_DFLTCOLOR? _gdcntrst(bgc): rawclr), \
76
+ (_gdccfoo1=gdImageColorExact(im,l2gdshd(_gdccfoo2))) != -1? \
77
+ _gdccfoo1: \
78
+ gdImageColorsTotal(im) == gdMaxColors? \
79
+ gdImageColorClosest(im,l2gdshd(_gdccfoo2)): \
80
+ gdImageColorAllocate(im,l2gdshd(_gdccfoo2)) )
81
+
82
+ typedef enum {
83
+ GDC_GIF = 0,
84
+ #ifdef HAVE_JPEG
85
+ GDC_JPEG = 1,
86
+ #endif
87
+ GDC_PNG = 2,
88
+ GDC_WBMP = 3 /* as of gd1.8.3 WBMP is black and white only. */
89
+ } GDC_image_type_t;
90
+
91
+ /* ordered by size */
92
+ enum GDC_font_size { GDC_pad = 0,
93
+ GDC_TINY = 1,
94
+ GDC_SMALL = 2,
95
+ GDC_MEDBOLD = 3,
96
+ GDC_LARGE = 4,
97
+ GDC_GIANT = 5,
98
+ GDC_numfonts= 6 }; /* GDC[PIE]_fontc depends on this */
99
+
100
+ typedef enum {
101
+ GDC_DESTROY_IMAGE = 0, /* default */
102
+ GDC_EXPOSE_IMAGE = 1, /* user must call GDC_destroy_image() */
103
+ GDC_REUSE_IMAGE = 2 /* i.e., paint on top of */
104
+ } GDC_HOLD_IMAGE_T; /* EXPOSE & REUSE */
105
+
106
+ #ifdef GDC_INCL
107
+ struct GDC_FONT_T {
108
+ gdFontPtr f;
109
+ char h;
110
+ char w;
111
+ };
112
+
113
+ typedef enum { GDC_JUSTIFY_RIGHT,
114
+ GDC_JUSTIFY_CENTER,
115
+ GDC_JUSTIFY_LEFT } GDC_justify_t;
116
+
117
+
118
+ struct fnt_sz_t {
119
+ int w;
120
+ int h;
121
+ } GDCfnt_sz( char* str, enum GDC_font_size gdfontsz, char* ttfont, double ttfptsz, double angle, char **status );
122
+
123
+ int GDCImageStringNL( gdImagePtr, struct GDC_FONT_T*, char*, double, double, int, int, char*, int, GDC_justify_t, char** );
124
+ void load_font_conversions();
125
+ short cnt_nl( char*, int* );
126
+ #endif
127
+
128
+ #ifdef GDC_LIB
129
+ #define EXTERND extern
130
+ #define DEFAULTO(val)
131
+ extern struct GDC_FONT_T GDC_fontc[];
132
+ #else
133
+ #define EXTERND
134
+ #define DEFAULTO(val) = val
135
+ #endif
136
+
137
+ /**** COMMON OPTIONS ********************************/
138
+ #ifndef _GDC_COMMON_OPTIONS
139
+ #define _GDC_COMMON_OPTIONS
140
+ EXTERND GDC_image_type_t GDC_image_type DEFAULTO( GDC_PNG );
141
+ EXTERND int GDC_jpeg_quality DEFAULTO( -1 ); /* 0-95 */
142
+ EXTERND char GDC_generate_img DEFAULTO( TRUE );
143
+
144
+ EXTERND GDC_HOLD_IMAGE_T GDC_hold_img DEFAULTO( GDC_DESTROY_IMAGE );
145
+ EXTERND void *GDC_image DEFAULTO( (void*)NULL ); /* in/out */
146
+ #endif
147
+ /****************************************************/
148
+
149
+
150
+ void GDC_destroy_image( void* );
151
+ void out_err( int IMGWIDTH,
152
+ int IMGHEIGHT,
153
+ FILE*,
154
+ unsigned long BGColor,
155
+ unsigned long LineColor,
156
+ char *str );
157
+
158
+ #endif /*!_GDC_H*/
@@ -0,0 +1,691 @@
1
+ /* GDCHART 0.11.3dev GDC_PIE.C 11 Mar 2003 */
2
+ /* Copyright Bruce Verderaime 1998-2004 */
3
+
4
+ /* ELLIPSE (angled/perspective view) */
5
+ /* TODO */
6
+ /* view on/off/%/deg option should be tied to 3d_angle? */
7
+ /* limited in two directions only - by X axis, or Y axis */
8
+ /* ELIPSEY [scaling] only one that make sense? */
9
+ /* connections between face&background (effects Y axis only?) */
10
+ /* sizing still doesn't work - DONE */
11
+ /* label distances need to be variable about the ellipse/3d_angle */
12
+ /* #define ELLIPSEX 1.00 */
13
+ /* #define ELLIPSEY 0.30 */
14
+
15
+ #define GDC_INCL
16
+ #define GDC_LIB
17
+ #include "gdc.h" /* gdc.h before system includes to pick up features */
18
+
19
+ #include <stdio.h>
20
+
21
+ #include "gdcpie.h"
22
+
23
+ /* rem circle: x = rcos(@), y = rsin(@) */
24
+
25
+ extern struct GDC_FONT_T GDC_fontc[];
26
+
27
+ #define SET_RECT( gdp, x1, x2, y1, y2 ) gdp[0].x = gdp[3].x = x1, \
28
+ gdp[0].y = gdp[1].y = y1, \
29
+ gdp[1].x = gdp[2].x = x2, \
30
+ gdp[2].y = gdp[3].y = y2
31
+
32
+ #define PX( x ) ( cx + (int)( ((float)rad*ellipsex)*sin(pscl*(double)(x)) ) ) /* expects a val */
33
+ #define PY( x ) ( cy - (int)( ((float)rad*ellipsey)*cos(pscl*(double)(x)) ) ) /* expects a val */
34
+
35
+ #define CX( i,d ) ( cx + \
36
+ (d? xdepth_3D: 0) + \
37
+ (int)( (double)(GDCPIE_explode?GDCPIE_explode[(i)]:0) * sin((double)(slice_angle[0][i])) ) )
38
+ #define CY( i,d ) ( cy - \
39
+ (d? ydepth_3D: 0) - \
40
+ (int)( (double)(GDCPIE_explode?GDCPIE_explode[(i)]:0) * cos((double)(slice_angle[0][i])) ) )
41
+ /* expect slice number: i (index into slice_angle array) *\
42
+ * and position on slice: f (0: slice middle, *
43
+ * 1: leading (clockwise), *
44
+ * 2: trailing edge) *
45
+ * and 3D depth: d (0: do depth, *
46
+ * 1: no depth adjustment) *
47
+ \* adjusts for explosion */
48
+ #define IX( i,f,d ) ( CX(i,d) + (int)( (double)rad*ellipsex * sin((double)(slice_angle[f][i])) ) )
49
+ #define IY( i,f,d ) ( CY(i,d) - (int)( (double)rad*ellipsey * cos((double)(slice_angle[f][i])) ) )
50
+ /* same as above except o is angle */
51
+ #define OX( i,o,d ) ( CX(i,d) + (int)( (double)rad*ellipsex * sin((double)(o)) ) )
52
+ #define OY( i,o,d ) ( CY(i,d) - (int)( (double)rad*ellipsey * cos((double)(o)) ) )
53
+
54
+ #define TO_INT_DEG(o) (int)rint( (double)((o)/(2.0*M_PI)*360.0) )
55
+ #define TO_INT_DEG_FLOOR(o) (int)floor( (double)((o)/(2.0*M_PI)*360.0) )
56
+ #define TO_INT_DEG_CEIL(o) (int)ceil( (double)((o)/(2.0*M_PI)*360.0) )
57
+ #define TO_RAD(o) ( (o)/360.0*(2.0*M_PI) )
58
+ /* assume !> 4*PI */
59
+ #define MOD_2PI(o) ( (o)>=(2.0*M_PI)? ((o)-(2.0*M_PI)): (((o)<0)? ((o)+(2.0*M_PI)): (o)) )
60
+ #define MOD_360(o) ( (o)>=360? (o)-360: (o) ) /* assume !> 720 */
61
+
62
+ struct tmp_slice_t { int i; /* original index */
63
+ char hidden; /* 'behind' top [3D] pie */
64
+ float angle; /* radian */
65
+ float slice; }; /* radian */
66
+ static float pie_3D_rad; /* user requested 3D angle in radians */
67
+
68
+ /* degrees (radians) between angle a, and depth angle */
69
+ /* 1&2, so comparisons can be done. */
70
+ #define RAD_DIST1( a ) ( (dist_foo1=ABS(((a>-.00001&&a<.00001)?0.00001:a)-pie_3D_rad)), ((dist_foo1>M_PI)? ABS(dist_foo1-2.0*M_PI): dist_foo1) )
71
+ #define RAD_DIST2( a ) ( (dist_foo2=ABS(((a>-.00001&&a<.00001)?0.00001:a)-pie_3D_rad)), ((dist_foo2>M_PI)? ABS(dist_foo2-2.0*M_PI): dist_foo2) )
72
+ static float dist_foo1, dist_foo2;
73
+
74
+ /* ------------------------------------------------------- *\
75
+ * oof! cleaner way???
76
+ * order by angle opposite (180) of depth angle
77
+ * comparing across 0-360 line
78
+ \* ------------------------------------------------------- */
79
+ static int
80
+ ocmpr( struct tmp_slice_t *a,
81
+ struct tmp_slice_t *b )
82
+ {
83
+ if( RAD_DIST1(a->angle) < RAD_DIST2(b->angle) )
84
+ return 1;
85
+ if( RAD_DIST1(a->angle) > RAD_DIST2(b->angle) )
86
+ return -1;
87
+
88
+ /* a tie (will happen between each slice) */
89
+ /* are we within pie_3D_rad */
90
+ if( (a->angle < pie_3D_rad) && (pie_3D_rad < a->slice) ||
91
+ (a->slice < pie_3D_rad) && (pie_3D_rad < a->angle) )
92
+ return 1;
93
+ if( (b->slice < pie_3D_rad) && (pie_3D_rad < b->angle) ||
94
+ (b->angle < pie_3D_rad) && (pie_3D_rad < b->slice) )
95
+ return -1;
96
+
97
+ /* let slice angle decide */
98
+ if( RAD_DIST1(a->slice) < RAD_DIST2(b->slice) )
99
+ return 1;
100
+ if( RAD_DIST1(a->slice) > RAD_DIST2(b->slice) )
101
+ return -1;
102
+
103
+ return 0;
104
+ }
105
+
106
+ /* ======================================================= *\
107
+ * PIE
108
+ *
109
+ * Notes:
110
+ * always drawn from 12:00 position clockwise
111
+ * 'missing' slices don't get labels
112
+ * sum(val[0], ... val[num_points-1]) is assumed to be 100%
113
+ \* ======================================================= */
114
+ void
115
+ GDC_out_pie( short IMGWIDTH,
116
+ short IMGHEIGHT,
117
+ FILE *img_fptr, /* open file pointer */
118
+ GDCPIE_TYPE type,
119
+ int num_points,
120
+ char *lbl[], /* data labels */
121
+ float val[] ) /* data */
122
+ {
123
+ int i;
124
+
125
+ gdImagePtr im;
126
+ int BGColor,
127
+ LineColor,
128
+ PlotColor,
129
+ EdgeColor,
130
+ EdgeColorShd;
131
+ CREATE_ARRAY1( SliceColor, int, num_points ); /* int SliceColor[num_points] */
132
+ CREATE_ARRAY1( SliceColorShd, int, num_points ); /* int SliceColorShd[num_points] */
133
+
134
+ float rad = 0.0; /* radius */
135
+ float ellipsex = 1.0;
136
+ float ellipsey = 1.0 - (float)GDCPIE_perspective/100.0;
137
+ float tot_val = 0.0;
138
+ float pscl;
139
+ int cx, /* affects PX() */
140
+ cy; /* affects PY() */
141
+ /* ~ 1% for a size of 100 pixs */
142
+ /* label sizes will more dictate this */
143
+ float min_grphable = ( GDCPIE_other_threshold < 0?
144
+ 100.0/(float)MIN(IMGWIDTH,IMGHEIGHT):
145
+ (float)GDCPIE_other_threshold )/100.0;
146
+ short num_slices1 = 0,
147
+ num_slices2 = 0;
148
+ char any_too_small = FALSE;
149
+ CREATE_ARRAY1( others, char, num_points ); /* char others[num_points] */
150
+ CREATE_ARRAY2( slice_angle, float, 3, num_points ); /* float slice_angle[3][num_points] */
151
+ /* must be used with others[] */
152
+ char threeD = ( type == GDC_3DPIE );
153
+
154
+ int xdepth_3D = 0, /* affects PX() */
155
+ ydepth_3D = 0; /* affects PY() */
156
+ int do3Dx = 0, /* reserved for macro use */
157
+ do3Dy = 0;
158
+
159
+ CREATE_ARRAY2( pct_lbl, char, num_points, 16 ); /* sizeof or strlen (GDCPIE_percent_fmt)? */
160
+ CREATE_ARRAY1( pct_ftsz, struct fnt_sz_t, num_points ); /* struct fnt_sz_t lbl_ftsz[num_points] */
161
+ CREATE_ARRAY1( lbl_ftsz, struct fnt_sz_t, num_points ); /* struct fnt_sz_t lbl_ftsz[num_points] */
162
+
163
+
164
+ #ifdef HAVE_LIBFREETYPE
165
+ char *gdcpie_title_font = GDCPIE_title_font;
166
+ char *gdcpie_label_font = GDCPIE_label_font;
167
+ double gdcpie_title_ptsize = GDCPIE_title_ptsize;
168
+ double gdcpie_label_ptsize = GDCPIE_label_ptsize;
169
+ #else
170
+ char *gdcpie_title_font = NULL;
171
+ char *gdcpie_label_font = NULL;
172
+ double gdcpie_title_ptsize = 0.0;
173
+ double gdcpie_label_ptsize = 0.0;
174
+ #endif
175
+
176
+ /* GDCPIE_3d_angle = MOD_360(90-GDCPIE_3d_angle+360); */
177
+ pie_3D_rad = TO_RAD( GDCPIE_3d_angle );
178
+
179
+ xdepth_3D = threeD? (int)( cos((double)MOD_2PI(M_PI_2-pie_3D_rad+2.0*M_PI)) * GDCPIE_3d_depth ): 0;
180
+ ydepth_3D = threeD? (int)( sin((double)MOD_2PI(M_PI_2-pie_3D_rad+2.0*M_PI)) * GDCPIE_3d_depth ): 0;
181
+ /* xdepth_3D = threeD? (int)( cos(pie_3D_rad) * GDCPIE_3d_depth ): 0; */
182
+ /* ydepth_3D = threeD? (int)( sin(pie_3D_rad) * GDCPIE_3d_depth ): 0; */
183
+
184
+ load_font_conversions();
185
+
186
+ /* ----- get total value ----- */
187
+ for( i=0; i<num_points; ++i )
188
+ tot_val += val[i];
189
+
190
+ /* ----- pie sizing ----- */
191
+ /* ----- make width room for labels, depth, etc.: ----- */
192
+ /* ----- determine pie's radius ----- */
193
+ {
194
+ int title_hgt = GDCPIE_title? 1 /* title? horizontal text line */
195
+ + GDCfnt_sz( GDCPIE_title,
196
+ GDCPIE_title_size,
197
+ gdcpie_title_font, gdcpie_title_ptsize, 0.0, NULL ).h
198
+
199
+ + 2:
200
+ 0;
201
+ float last = 0.0;
202
+ float label_explode_limit = 0.0;
203
+ int cheight,
204
+ cwidth;
205
+
206
+ /* maximum: no labels, explosions */
207
+ /* gotta start somewhere */
208
+ rad = (float)MIN( (IMGWIDTH/2)/ellipsex-(1+ABS(xdepth_3D)), (IMGHEIGHT/2)/ellipsey-(1+ABS(ydepth_3D))-title_hgt );
209
+
210
+ /* ok fix center, i.e., no floating re labels, explosion, etc. */
211
+ cx = IMGWIDTH/2 /* - xdepth_3D */ ;
212
+ cy = (IMGHEIGHT-title_hgt)/2 + title_hgt /* + ydepth_3D */ ;
213
+
214
+ cheight = (IMGHEIGHT- title_hgt)/2 /* - ydepth_3D */ ;
215
+ cwidth = cx;
216
+
217
+ /* walk around pie. determine spacing to edge */
218
+ for( i=0; i<num_points; ++i )
219
+ {
220
+ float this_pct = val[i]/tot_val; /* should never be > 100% */
221
+ float this = this_pct*(2.0*M_PI); /* pie-portion */
222
+ if( (this_pct > min_grphable) || /* too small */
223
+ (!GDCPIE_missing || !GDCPIE_missing[i]) ) /* still want angles */
224
+ {
225
+ int this_explode = GDCPIE_explode? GDCPIE_explode[i]: 0;
226
+ double this_sin;
227
+ double this_cos;
228
+ slice_angle[0][i] = this/2.0+last; /* mid-point on full pie */
229
+ slice_angle[1][i] = last; /* 1st on full pie */
230
+ slice_angle[2][i] = this+last; /* 2nd on full pie */
231
+ this_sin = ellipsex*sin( (double)slice_angle[0][i] );
232
+ this_cos = ellipsey*cos( (double)slice_angle[0][i] );
233
+
234
+ if( !GDCPIE_missing || !(GDCPIE_missing[i]) )
235
+ {
236
+ short lbl_wdth = 0,
237
+ lbl_hgt = 0;
238
+ float this_y_explode_limit,
239
+ this_x_explode_limit;
240
+
241
+ /* start slice label height, width */
242
+ /* accounting for PCT placement, font */
243
+ pct_ftsz[i].h = 0;
244
+ pct_ftsz[i].w = 0;
245
+ if( GDCPIE_percent_fmt &&
246
+ GDCPIE_percent_labels != GDCPIE_PCT_NONE )
247
+ {
248
+ sprintf( pct_lbl[i], GDCPIE_percent_fmt, this_pct * 100.0 );
249
+ pct_ftsz[i] = GDCfnt_sz( pct_lbl[i],
250
+ GDCPIE_label_size,
251
+ gdcpie_label_font, gdcpie_label_ptsize, 0.0, NULL );
252
+ lbl_wdth = pct_ftsz[i].w;
253
+ lbl_hgt = pct_ftsz[i].h;
254
+ }
255
+
256
+ if( lbl && lbl[i] )
257
+ {
258
+ lbl_ftsz[i] = GDCfnt_sz( lbl[i],
259
+ GDCPIE_label_size,
260
+ gdcpie_label_font, gdcpie_label_ptsize, 0.0, NULL );
261
+
262
+ if( GDCPIE_percent_labels == GDCPIE_PCT_ABOVE ||
263
+ GDCPIE_percent_labels == GDCPIE_PCT_BELOW )
264
+ {
265
+ lbl_wdth = MAX( pct_ftsz[i].w, lbl_ftsz[i].w );
266
+ lbl_hgt = pct_ftsz[i].h + lbl_ftsz[i].h + 1;
267
+ }
268
+ else
269
+ if( GDCPIE_percent_labels == GDCPIE_PCT_RIGHT ||
270
+ GDCPIE_percent_labels == GDCPIE_PCT_LEFT )
271
+ {
272
+ lbl_wdth = pct_ftsz[i].w + lbl_ftsz[i].w + 1;
273
+ lbl_hgt = MAX( pct_ftsz[i].h, lbl_ftsz[i].h );
274
+ }
275
+ else /* GDCPIE_PCT_NONE */
276
+ {
277
+ lbl_wdth = lbl_ftsz[i].w;
278
+ lbl_hgt = lbl_ftsz[i].h;
279
+ }
280
+ }
281
+ else
282
+ lbl_wdth = lbl_hgt = 0;
283
+ /* end label height, width */
284
+
285
+ /* diamiter limited by this point's: explosion, label */
286
+ /* (radius to box @ slice_angle) - (explode) - (projected label size) */
287
+ /* radius constraint due to labels */
288
+ this_y_explode_limit = (float)this_cos==0.0? FLT_MAX:
289
+ ( (float)( (double)cheight/ABS(this_cos) ) -
290
+ (float)( this_explode + (lbl&&lbl[i]? GDCPIE_label_dist: 0) ) -
291
+ (float)( lbl_hgt/2 ) / (float)ABS(this_cos) );
292
+ this_x_explode_limit = (float)this_sin==0.0? FLT_MAX:
293
+ ( (float)( (double)cwidth/ABS(this_sin) ) -
294
+ (float)( this_explode + (lbl&&lbl[i]? GDCPIE_label_dist: 0) ) -
295
+ (float)( lbl_wdth ) / (float)ABS(this_sin) );
296
+
297
+ rad = MIN( rad, this_y_explode_limit );
298
+ rad = MIN( rad, this_x_explode_limit );
299
+
300
+ /* ok at this radius (which is most likely larger than final) */
301
+ /* adjust for inter-label spacing */
302
+ /* if( lbl[i] && *lbl[i] ) */
303
+ /* { */
304
+ /* char which_edge = slice_angle[0][i] > M_PI? +1: -1; // which semi */
305
+ /* last_label_yedge = cheight - (int)( (rad + // top or bottom of label */
306
+ /* (float)(this_explode + */
307
+ /* (float)GDCPIE_label_dist)) * (float)this_cos ) + */
308
+ /* ( (GDC_fontc[GDCPIE_label_size].h+1)/2 + */
309
+ /* GDC_label_spacing )*which_edge; */
310
+ /* } */
311
+
312
+ /* radius constriant due to exploded depth */
313
+ /* at each edge of the slice, and the middle */
314
+ /* this is really stupid */
315
+ /* this section uses a different algorithm then above, but does the same thing */
316
+ /* could be combined, but each is ugly enough! */
317
+ /* PROTECT /0 */
318
+ if( threeD )
319
+ {
320
+ short j;
321
+ int this_y_explode_pos;
322
+ int this_x_explode_pos;
323
+
324
+ /* first N E S W (actually no need for N) */
325
+ if( (slice_angle[1][i] < M_PI_2 && M_PI_2 < slice_angle[2][i]) && /* E */
326
+ (this_x_explode_pos=OX(i,M_PI_2,1)) > cx+cwidth )
327
+ rad -= (float)ABS( (double)(1+this_x_explode_pos-(cx+cwidth))/sin(M_PI_2) );
328
+ if( (slice_angle[1][i] < 3.0*M_PI_2 && 3.0*M_PI_2 < slice_angle[2][i]) && /* W */
329
+ (this_x_explode_pos=OX(i,3.0*M_PI_2,1)) < cx-cwidth )
330
+ rad -= (float)ABS( (double)(this_x_explode_pos-(cx+cwidth))/sin(3.0*M_PI_2) );
331
+ if( (slice_angle[1][i] < M_PI && M_PI < slice_angle[2][i]) && /* S */
332
+ (this_y_explode_pos=OY(i,M_PI,1)) > cy+cheight )
333
+ rad -= (float)ABS( (double)(1+this_y_explode_pos-(cy+cheight))/cos(M_PI) );
334
+
335
+ for( j=0; j<3; ++j )
336
+ {
337
+ this_y_explode_pos = IY(i,j,1);
338
+ if( this_y_explode_pos < cy-cheight )
339
+ rad -= (float)ABS( (double)((cy-cheight)-this_y_explode_pos)/cos((double)slice_angle[j][i]) );
340
+ if( this_y_explode_pos > cy+cheight )
341
+ rad -= (float)ABS( (double)(1+this_y_explode_pos-(cy+cheight))/cos((double)slice_angle[j][i]) );
342
+
343
+ this_x_explode_pos = IX(i,j,1);
344
+ if( this_x_explode_pos < cx-cwidth )
345
+ rad -= (float)ABS( (double)((cx-cwidth)-this_x_explode_pos)/sin((double)slice_angle[j][i]) );
346
+ if( this_x_explode_pos > cx+cwidth )
347
+ rad -= (float)ABS( (double)(1+this_x_explode_pos-(cx+cwidth))/sin((double)slice_angle[j][i]) );
348
+ }
349
+ }
350
+ }
351
+ others[i] = FALSE;
352
+ }
353
+ else
354
+ {
355
+ others[i] = TRUE;
356
+ slice_angle[0][i] = -FLT_MAX;
357
+ }
358
+ last += this;
359
+ }
360
+ }
361
+
362
+ /* ----- go ahead and start the image ----- */
363
+ im = gdImageCreate( IMGWIDTH, IMGHEIGHT );
364
+
365
+ /* --- allocate the requested colors --- */
366
+ BGColor = clrallocate( im, GDCPIE_BGColor );
367
+ LineColor = clrallocate( im, GDCPIE_LineColor );
368
+ PlotColor = clrallocate( im, GDCPIE_PlotColor );
369
+ if( GDCPIE_EdgeColor != GDC_NOCOLOR )
370
+ {
371
+ EdgeColor = clrallocate( im, GDCPIE_EdgeColor );
372
+ if( threeD )
373
+ EdgeColorShd = clrshdallocate( im, GDCPIE_EdgeColor );
374
+ }
375
+
376
+ /* --- set color for each slice --- */
377
+ for( i=0; i<num_points; ++i )
378
+ if( GDCPIE_Color )
379
+ {
380
+ unsigned long slc_clr = GDCPIE_Color[i];
381
+
382
+ SliceColor[i] = clrallocate( im, slc_clr );
383
+ if( threeD )
384
+ SliceColorShd[i] = clrshdallocate( im, slc_clr );
385
+ }
386
+ else
387
+ {
388
+ SliceColor[i] = PlotColor;
389
+ if( threeD )
390
+ SliceColorShd[i] = clrshdallocate( im, GDCPIE_PlotColor );
391
+ }
392
+
393
+ pscl = (2.0*M_PI)/tot_val;
394
+
395
+ /* ----- calc: smallest a slice can be ----- */
396
+ /* 1/2 circum / num slices per side. */
397
+ /* determined by number of labels that'll fit (height) */
398
+ /* scale to user values */
399
+ /* ( M_PI / (IMGHEIGHT / (SFONTHGT+1)) ) */
400
+ /* min_grphable = tot_val / */
401
+ /* ( 2.0 * (float)IMGHEIGHT / (float)(SFONTHGT+1+TFONTHGT+2) ); */
402
+
403
+
404
+ if( threeD )
405
+ {
406
+ /* draw background shaded pie */
407
+ {
408
+ float rad1 = rad * 3.0/4.0;
409
+ for( i=0; i<num_points; ++i )
410
+ if( !(others[i]) &&
411
+ (!GDCPIE_missing || !GDCPIE_missing[i]) )
412
+ {
413
+ int edge_color = GDCPIE_EdgeColor == GDC_NOCOLOR? SliceColorShd[i]:
414
+ EdgeColorShd;
415
+
416
+ gdImageLine( im, CX(i,1), CY(i,1), IX(i,1,1), IY(i,1,1), edge_color );
417
+ gdImageLine( im, CX(i,1), CY(i,1), IX(i,2,1), IY(i,2,1), edge_color );
418
+ gdImageArc( im, CX(i,1), CY(i,1),
419
+ (int)(rad*ellipsex*2.0), (int)(rad*ellipsey*2.0),
420
+ TO_INT_DEG_FLOOR(slice_angle[1][i])+270,
421
+ TO_INT_DEG_CEIL(slice_angle[2][i])+270,
422
+ edge_color );
423
+
424
+ /* gdImageFilledArc( im, CX(i,1), CY(i,1), */
425
+ /* rad*ellipsex*2, rad*ellipsey*2, */
426
+ /* TO_INT_DEG_FLOOR(slice_angle[1][i])+270, */
427
+ /* TO_INT_DEG_CEIL(slice_angle[2][i])+270, */
428
+ /* SliceColorShd[i], */
429
+ /* gdPie ); */
430
+ /* attempt to fill, if slice is wide enough */
431
+ if( (ABS(IX(i,1,1)-IX(i,2,1)) + ABS(IY(i,1,1)-IY(i,2,1))) > 2 )
432
+ {
433
+ float rad = rad1; /* local override */
434
+ gdImageFillToBorder( im, IX(i,0,1), IY(i,0,1), edge_color, SliceColorShd[i] );
435
+ }
436
+ }
437
+ }
438
+ /* fill in connection to foreground pie */
439
+ /* this is where we earn our keep */
440
+ {
441
+ int t,
442
+ num_slice_angles = 0;
443
+ CREATE_ARRAY1( tmp_slice, struct tmp_slice_t, 4*num_points+4 ); /* should only need 2*num_points+2 */
444
+
445
+ for( i=0; i<num_points; ++i )
446
+ if( !GDCPIE_missing || !GDCPIE_missing[i] )
447
+ {
448
+ if( RAD_DIST1(slice_angle[1][i]) < RAD_DIST2(slice_angle[0][i]) )
449
+ tmp_slice[num_slice_angles].hidden = FALSE;
450
+ else
451
+ tmp_slice[num_slice_angles].hidden = TRUE;
452
+ tmp_slice[num_slice_angles].i = i;
453
+ tmp_slice[num_slice_angles].slice = slice_angle[0][i];
454
+ tmp_slice[num_slice_angles++].angle = slice_angle[1][i];
455
+ if( RAD_DIST1(slice_angle[2][i]) < RAD_DIST2(slice_angle[0][i]) )
456
+ tmp_slice[num_slice_angles].hidden = FALSE;
457
+ else
458
+ tmp_slice[num_slice_angles].hidden = TRUE;
459
+ tmp_slice[num_slice_angles].i = i;
460
+ tmp_slice[num_slice_angles].slice = slice_angle[0][i];
461
+ tmp_slice[num_slice_angles++].angle = slice_angle[2][i];
462
+ /* identify which 2 slices (i) have a tangent parallel to depth angle */
463
+ if( slice_angle[1][i]<MOD_2PI(pie_3D_rad+M_PI_2) && slice_angle[2][i]>MOD_2PI(pie_3D_rad+M_PI_2) )
464
+ {
465
+ tmp_slice[num_slice_angles].i = i;
466
+ tmp_slice[num_slice_angles].hidden = FALSE;
467
+ tmp_slice[num_slice_angles].slice = slice_angle[0][i];
468
+ tmp_slice[num_slice_angles++].angle = MOD_2PI( pie_3D_rad+M_PI_2 );
469
+ }
470
+ if( slice_angle[1][i]<MOD_2PI(pie_3D_rad+3.0*M_PI_2) && slice_angle[2][i]>MOD_2PI(pie_3D_rad+3.0*M_PI_2) )
471
+ {
472
+ tmp_slice[num_slice_angles].i = i;
473
+ tmp_slice[num_slice_angles].hidden = FALSE;
474
+ tmp_slice[num_slice_angles].slice = slice_angle[0][i];
475
+ tmp_slice[num_slice_angles++].angle = MOD_2PI( pie_3D_rad+3.0*M_PI_2 );
476
+ }
477
+ }
478
+
479
+ qsort( tmp_slice, num_slice_angles, sizeof(struct tmp_slice_t), ocmpr );
480
+ for( t=0; t<num_slice_angles; ++t )
481
+ {
482
+ gdPoint gdp[4];
483
+
484
+ i = tmp_slice[t].i;
485
+
486
+ gdp[0].x = CX(i,0); gdp[0].y = CY(i,0);
487
+ gdp[1].x = CX(i,1); gdp[1].y = CY(i,1);
488
+ gdp[2].x = OX(i,tmp_slice[t].angle,1); gdp[2].y = OY(i,tmp_slice[t].angle,1);
489
+ gdp[3].x = OX(i,tmp_slice[t].angle,0); gdp[3].y = OY(i,tmp_slice[t].angle,0);
490
+
491
+ if( !(tmp_slice[t].hidden) )
492
+ gdImageFilledPolygon( im, gdp, 4, SliceColorShd[i] );
493
+ else
494
+ {
495
+ rad -= 2.0; /* no peeking */
496
+ gdp[0].x = OX(i,slice_angle[0][i],0); gdp[0].y = OY(i,slice_angle[0][i],0);
497
+ gdp[1].x = OX(i,slice_angle[0][i],1); gdp[1].y = OY(i,slice_angle[0][i],1);
498
+ rad += 2.0;
499
+ gdp[2].x = OX(i,slice_angle[1][i],1); gdp[2].y = OY(i,slice_angle[1][i],1);
500
+ gdp[3].x = OX(i,slice_angle[1][i],0); gdp[3].y = OY(i,slice_angle[1][i],0);
501
+ gdImageFilledPolygon( im, gdp, 4, SliceColorShd[i] );
502
+ gdp[2].x = OX(i,slice_angle[2][i],1); gdp[2].y = OY(i,slice_angle[2][i],1);
503
+ gdp[3].x = OX(i,slice_angle[2][i],0); gdp[3].y = OY(i,slice_angle[2][i],0);
504
+ gdImageFilledPolygon( im, gdp, 4, SliceColorShd[i] );
505
+ }
506
+
507
+
508
+ if( GDCPIE_EdgeColor != GDC_NOCOLOR )
509
+ {
510
+ gdImageLine( im, CX(i,0), CY(i,0), CX(i,1), CY(i,1), EdgeColorShd );
511
+ gdImageLine( im, OX(i,tmp_slice[t].angle,0), OY(i,tmp_slice[t].angle,0),
512
+ OX(i,tmp_slice[t].angle,1), OY(i,tmp_slice[t].angle,1),
513
+ EdgeColorShd );
514
+ }
515
+ }
516
+ FREE_ARRAY1( tmp_slice );
517
+ }
518
+ }
519
+
520
+
521
+ /* ----- pie face ----- */
522
+ {
523
+ /* float last = 0.0; */
524
+ float rad1 = rad * 3.0/4.0;
525
+ for( i=0; i<num_points; ++i )
526
+ if( !others[i] &&
527
+ (!GDCPIE_missing || !GDCPIE_missing[i]) )
528
+ {
529
+ int edge_color = GDCPIE_EdgeColor == GDC_NOCOLOR? SliceColor[i]:
530
+ EdgeColorShd;
531
+ /* last += val[i]; */
532
+ /* EXPLODE_CX_CY( slice_angle[0][i], i ); */
533
+ gdImageLine( im, CX(i,0), CY(i,0), IX(i,1,0), IY(i,1,0), edge_color );
534
+ gdImageLine( im, CX(i,0), CY(i,0), IX(i,2,0), IY(i,2,0), edge_color );
535
+ gdImageArc( im, CX(i,0), CY(i,0),
536
+ (int)(rad*ellipsex*2.0), (int)(rad*ellipsey*2.0),
537
+ (TO_INT_DEG_FLOOR(slice_angle[1][i])+270)%360,
538
+ (TO_INT_DEG_CEIL(slice_angle[2][i])+270)%360,
539
+ edge_color );
540
+ /* antialiasing here */
541
+ /* likely only on the face? */
542
+ /* bugs in gd2.0.0 */
543
+ /* arc doesn't honor deg>360 */
544
+ /* arcs from gdImageFilledArc() don't match with gdImageArc() */
545
+ /* angles are off */
546
+ /* doesn't always fill completely */
547
+ /* gdImageFilledArc( im, CX(i,0), CY(i,0), */
548
+ /* (int)(rad*ellipsex*2.0), (int)(rad*ellipsey*2.0), */
549
+ /* (TO_INT_DEG_FLOOR(slice_angle[1][i])+270)%360, */
550
+ /* (TO_INT_DEG_CEIL(slice_angle[2][i])+270)%360, */
551
+ /* SliceColor[i], */
552
+ /* gdPie ); */
553
+ /* attempt to fill, if slice is wide enough */
554
+ {
555
+ float rad = rad1; /* local override */
556
+ if( (ABS(IX(i,1,1)-IX(i,2,1)) + ABS(IY(i,1,1)-IY(i,2,1))) > 2 )
557
+ {
558
+ gdImageFillToBorder( im, IX(i,0,0), IY(i,0,0), edge_color, SliceColor[i] );
559
+ }
560
+ /* catch missed pixels on narrow slices */
561
+ gdImageLine( im, CX(i,0), CY(i,0), IX(i,0,0), IY(i,0,0), SliceColor[i] );
562
+ }
563
+ }
564
+ }
565
+
566
+ if( GDCPIE_title )
567
+ {
568
+ struct fnt_sz_t tftsz = GDCfnt_sz( GDCPIE_title,
569
+ GDCPIE_title_size,
570
+ gdcpie_title_font, gdcpie_title_ptsize, 0.0, NULL );
571
+ GDCImageStringNL( im,
572
+ &GDC_fontc[GDCPIE_title_size],
573
+ gdcpie_title_font, gdcpie_title_ptsize,
574
+ 0.0,
575
+ IMGWIDTH/2 - tftsz.w/2,
576
+ 1,
577
+ GDCPIE_title,
578
+ LineColor,
579
+ GDC_JUSTIFY_CENTER,
580
+ NULL );
581
+ }
582
+
583
+ /* labels */
584
+ if( lbl )
585
+ {
586
+ float liner = rad;
587
+
588
+ rad += GDCPIE_label_dist;
589
+ for( i=0; i<num_points; ++i )
590
+ {
591
+ if( !others[i] &&
592
+ (!GDCPIE_missing || !GDCPIE_missing[i]) )
593
+ {
594
+ int lblx, pctx,
595
+ lbly, pcty,
596
+ linex, liney;
597
+
598
+ lbly = (liney = IY(i,0,0))-lbl_ftsz[i].h / 2;
599
+ lblx = pctx = linex = IX(i,0,0);
600
+
601
+ if( slice_angle[0][i] > M_PI ) /* which semicircle */
602
+ {
603
+ lblx -= lbl_ftsz[i].w;
604
+ pctx = lblx;
605
+ ++linex;
606
+ }
607
+ else
608
+ --linex;
609
+
610
+ switch( GDCPIE_percent_labels )
611
+ {
612
+ case GDCPIE_PCT_LEFT: if( slice_angle[0][i] > M_PI )
613
+ pctx -= lbl_ftsz[i].w-1;
614
+ else
615
+ lblx += pct_ftsz[i].w+1;
616
+ pcty = IY(i,0,0) - ( 1+pct_ftsz[i].h ) / 2;
617
+ break;
618
+ case GDCPIE_PCT_RIGHT: if( slice_angle[0][i] > M_PI )
619
+ lblx -= pct_ftsz[i].w-1;
620
+ else
621
+ pctx += lbl_ftsz[i].w+1;
622
+ pcty = IY(i,0,0) - ( 1+pct_ftsz[i].h ) / 2;
623
+ break;
624
+ case GDCPIE_PCT_ABOVE: lbly += (1+pct_ftsz[i].h) / 2;
625
+ pcty = lbly - pct_ftsz[i].h;
626
+ break;
627
+ case GDCPIE_PCT_BELOW: lbly -= (1+pct_ftsz[i].h) / 2;
628
+ pcty = lbly + lbl_ftsz[i].h;
629
+ break;
630
+ case GDCPIE_PCT_NONE:
631
+ default:;
632
+ }
633
+
634
+ if( GDCPIE_percent_labels != GDCPIE_PCT_NONE )
635
+ GDCImageStringNL( im,
636
+ &GDC_fontc[GDCPIE_label_size],
637
+ gdcpie_label_font, gdcpie_label_ptsize,
638
+ 0.0,
639
+ slice_angle[0][i] <= M_PI? pctx:
640
+ pctx+lbl_ftsz[i].w-pct_ftsz[i].w,
641
+ pcty,
642
+ pct_lbl[i],
643
+ LineColor,
644
+ GDC_JUSTIFY_CENTER,
645
+ NULL );
646
+ if( lbl[i] )
647
+ GDCImageStringNL( im,
648
+ &GDC_fontc[GDCPIE_label_size],
649
+ gdcpie_label_font, gdcpie_label_ptsize,
650
+ 0.0,
651
+ lblx,
652
+ lbly,
653
+ lbl[i],
654
+ LineColor,
655
+ slice_angle[0][i] <= M_PI? GDC_JUSTIFY_LEFT:
656
+ GDC_JUSTIFY_RIGHT,
657
+ NULL );
658
+ if( GDCPIE_label_line )
659
+ {
660
+ float rad = liner;
661
+ gdImageLine( im, linex, liney, IX(i,0,0), IY(i,0,0), LineColor );
662
+ }
663
+ }
664
+ }
665
+ rad -= GDCPIE_label_dist;
666
+ }
667
+
668
+ fflush( img_fptr );
669
+ switch( GDC_image_type )
670
+ {
671
+ #ifdef HAVE_JPEG
672
+ case GDC_JPEG: gdImageJpeg( im, img_fptr, GDC_jpeg_quality ); break;
673
+ #endif
674
+ case GDC_WBMP: gdImageWBMP( im, PlotColor, img_fptr ); break;
675
+ case GDC_GIF: gdImageGif( im, img_fptr); break;
676
+ case GDC_PNG:
677
+ default: gdImagePng( im, img_fptr );
678
+ }
679
+
680
+ FREE_ARRAY1( lbl_ftsz );
681
+ FREE_ARRAY1( pct_ftsz );
682
+ FREE_ARRAY2( pct_lbl );
683
+
684
+ FREE_ARRAY2( slice_angle );
685
+ FREE_ARRAY1( others );
686
+
687
+ FREE_ARRAY1( SliceColorShd );
688
+ FREE_ARRAY1( SliceColor );
689
+ gdImageDestroy(im);
690
+ return;
691
+ }