/* GDCHART 0.10.0dev GDC_PIE.C 2 Nov 2000 */ /* Copyright Bruce Verderaime 1998, 1999, 2000 */ #include #include #define GDC_INCL #define GDC_LIB #include "gdc.h" #include "gdcpie.h" /* rem circle: x = rcos(@), y = rsin(@) */ extern struct GDC_FONT_T GDC_fontc[]; #define SET_RECT( gdp, x1, x2, y1, y2 ) gdp[0].x = gdp[3].x = x1, \ gdp[0].y = gdp[1].y = y1, \ gdp[1].x = gdp[2].x = x2, \ gdp[2].y = gdp[3].y = y2 #define PX( x ) ( cx + (int)( ((float)rad)*sin(pscl*(double)(x)) ) ) /* expects a val */ #define PY( x ) ( cy - (int)( ((float)rad)*cos(pscl*(double)(x)) ) ) /* expects a val */ #define CX( i,d ) ( cx + \ (d? xdepth_3D: 0) + \ (int)( (double)(GDCPIE_explode?GDCPIE_explode[(i)]:0) * sin((double)(slice_angle[0][i])) ) ) #define CY( i,d ) ( cy - \ (d? ydepth_3D: 0) - \ (int)( (double)(GDCPIE_explode?GDCPIE_explode[(i)]:0) * cos((double)(slice_angle[0][i])) ) ) /* expect slice number: i (index into slice_angle array) *\ * and position on slice: f (0: slice middle, * * 1: leading (clockwise), * * 2: trailing edge) * * and 3D depth: d (0: do depth, * * 1: no depth adjustment) * \* adjusts for explosion */ #define IX( i,f,d ) ( CX(i,d) + (int)( (double)rad * sin((double)(slice_angle[f][i])) ) ) #define IY( i,f,d ) ( CY(i,d) - (int)( (double)rad * cos((double)(slice_angle[f][i])) ) ) /* same as above except o is angle */ #define OX( i,o,d ) ( CX(i,d) + (int)( (double)rad * sin((double)(o)) ) ) #define OY( i,o,d ) ( CY(i,d) - (int)( (double)rad * cos((double)(o)) ) ) #define TO_INT_DEG(o) (int)rint( (double)((o)/(2.0*M_PI)*360.0) ) #define TO_INT_DEG_FLOOR(o) (int)floor( (double)((o)/(2.0*M_PI)*360.0) ) #define TO_INT_DEG_CEIL(o) (int)ceil( (double)((o)/(2.0*M_PI)*360.0) ) #define TO_RAD(o) ( (o)/360.0*(2.0*M_PI) ) /* assume !> 4*PI */ #define MOD_2PI(o) ( (o)>=(2.0*M_PI)? ((o)-(2.0*M_PI)): (((o)<0)? ((o)+(2.0*M_PI)): (o)) ) #define MOD_360(o) ( (o)>=360? (o)-360: (o) ) /* assume !> 720 */ struct tmp_slice_t { int i; // original index char hidden; // 'behind' top [3D] pie float angle; // radian float slice; }; // radian static float pie_3D_rad; // user requested 3D angle in radians // degrees (radians) between angle a, and depth angle // 1&2, so comparisons can be done. #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) ) #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) ) static float dist_foo1, dist_foo2; /* ------------------------------------------------------- *\ * oof! cleaner way??? * order by angle opposite (180) of depth angle * comparing across 0-360 line \* ------------------------------------------------------- */ static int ocmpr( struct tmp_slice_t *a, struct tmp_slice_t *b ) { if( RAD_DIST1(a->angle) < RAD_DIST2(b->angle) ) return 1; if( RAD_DIST1(a->angle) > RAD_DIST2(b->angle) ) return -1; /* a tie (will happen between each slice) */ /* are we within pie_3D_rad */ if( (a->angle < pie_3D_rad) && (pie_3D_rad < a->slice) || (a->slice < pie_3D_rad) && (pie_3D_rad < a->angle) ) return 1; if( (b->slice < pie_3D_rad) && (pie_3D_rad < b->angle) || (b->angle < pie_3D_rad) && (pie_3D_rad < b->slice) ) return -1; /* let slice angle decide */ if( RAD_DIST1(a->slice) < RAD_DIST2(b->slice) ) return 1; if( RAD_DIST1(a->slice) > RAD_DIST2(b->slice) ) return -1; return 0; } /* ======================================================= *\ * PIE * * Notes: * always drawn from 12:00 position clockwise * 'missing' slices don't get labels * sum(val[0], ... val[num_points-1]) is assumed to be 100% \* ======================================================= */ void GDC_out_pie( short IMGWIDTH, short IMGHEIGHT, FILE *img_fptr, /* open file pointer */ GDCPIE_TYPE type, int num_points, char *lbl[], /* data labels */ float val[] ) /* data */ { int i; gdImagePtr im; int BGColor, LineColor, PlotColor, EdgeColor, EdgeColorShd, SliceColor[num_points], SliceColorShd[num_points]; float rad = 0.0; // radius float tot_val = 0.0; float pscl; int cx, // affects PX() cy; // affects PY() /* ~ 1% for a size of 100 pixs */ /* label sizes will more dictate this */ float min_grphable = ( GDCPIE_other_threshold < 0? 100.0/(float)MIN(IMGWIDTH,IMGHEIGHT): (float)GDCPIE_other_threshold )/100.0; short num_slices1 = 0, num_slices2 = 0; char any_too_small = FALSE; char others[num_points]; float slice_angle[3][num_points]; // must be used with others[] char threeD = ( type == GDC_3DPIE ); int xdepth_3D = 0, // affects PX() ydepth_3D = 0; // affects PY() int do3Dx = 0, // reserved for macro use do3Dy = 0; // GDCPIE_3d_angle = MOD_360(90-GDCPIE_3d_angle+360); pie_3D_rad = TO_RAD( GDCPIE_3d_angle ); xdepth_3D = threeD? (int)( cos((double)MOD_2PI(M_PI_2-pie_3D_rad+2.0*M_PI)) * GDCPIE_3d_depth ): 0; ydepth_3D = threeD? (int)( sin((double)MOD_2PI(M_PI_2-pie_3D_rad+2.0*M_PI)) * GDCPIE_3d_depth ): 0; // xdepth_3D = threeD? (int)( cos(pie_3D_rad) * GDCPIE_3d_depth ): 0; // ydepth_3D = threeD? (int)( sin(pie_3D_rad) * GDCPIE_3d_depth ): 0; load_font_conversions(); /* ----- get total value ----- */ for( i=0; i 100% */ float this = this_pct*(2.0*M_PI); /* pie-portion */ if( (this_pct > min_grphable) || /* too small */ (!GDCPIE_missing || !GDCPIE_missing[i]) ) /* still want angles */ { int this_explode = GDCPIE_explode? GDCPIE_explode[i]: 0; double this_sin; double this_cos; slice_angle[0][i] = this/2.0+last; /* mid-point on full pie */ slice_angle[1][i] = last; /* 1st on full pie */ slice_angle[2][i] = this+last; /* 2nd on full pie */ this_sin = sin( (double)slice_angle[0][i] ); this_cos = cos( (double)slice_angle[0][i] ); if( !GDCPIE_missing || !(GDCPIE_missing[i]) ) { short lbl_wdth, lbl_hgt; float this_y_explode_limit, this_x_explode_limit; /* start slice label height, width */ /* accounting for PCT placement, font */ if( lbl && lbl[i] ) { char foo[ 16 ]; /* XPG2 compatibility */ int pct_len; int lbl_len = 0; lbl_hgt = ( cnt_nl(lbl[i], &lbl_len) + (GDCPIE_percent_labels == GDCPIE_PCT_ABOVE || GDCPIE_percent_labels == GDCPIE_PCT_BELOW? 1: 0) ) * (GDC_fontc[GDCPIE_label_size].h+1); sprintf( foo, GDCPIE_percent_fmt? GDCPIE_percent_fmt: "", this_pct * 100.0 ); pct_len = GDCPIE_percent_labels == GDCPIE_PCT_NONE? 0: strlen(foo); lbl_wdth = ( GDCPIE_percent_labels == GDCPIE_PCT_RIGHT || GDCPIE_percent_labels == GDCPIE_PCT_LEFT? lbl_len+1+pct_len: MAX(lbl_len,pct_len) ) * GDC_fontc[GDCPIE_label_size].w; } else lbl_wdth = lbl_hgt = 0; /* end label height, width */ /* diamiter limited by this piont's: explosion, label */ /* (radius to box @ slice_angle) - (explode) - (projected label size) */ /* radius constraint due to labels */ this_y_explode_limit = (float)this_cos==0.0? MAXFLOAT: ( (float)( (double)cheight/ABS(this_cos) ) - (float)( this_explode + (lbl[i]? GDCPIE_label_dist: 0) ) - (float)( lbl_hgt/2 ) / (float)ABS(this_cos) ); this_x_explode_limit = (float)this_sin==0.0? MAXFLOAT: ( (float)( (double)cwidth/ABS(this_sin) ) - (float)( this_explode + (lbl[i]? GDCPIE_label_dist: 0) ) - (float)( lbl_wdth ) / (float)ABS(this_sin) ); rad = MIN( rad, this_y_explode_limit ); rad = MIN( rad, this_x_explode_limit ); // ok at this radius (which is most likely larger than final) // adjust for inter-label spacing // if( lbl[i] && *lbl[i] ) // { // char which_edge = slice_angle[0][i] > M_PI? +1: -1; // which semi // last_label_yedge = cheight - (int)( (rad + // top or bottom of label // (float)(this_explode + // (float)GDCPIE_label_dist)) * (float)this_cos ) + // ( (GDC_fontc[GDCPIE_label_size].h+1)/2 + // GDC_label_spacing )*which_edge; // } /* radius constriant due to exploded depth */ /* at each edge of the slice, and the middle */ /* this is really stupid */ /* this section uses a different algorithm then above, but does the same thing */ /* could be combined, but each is ugly enough! */ // PROTECT /0 if( threeD ) { short j; int this_y_explode_pos; int this_x_explode_pos; // first N E S W (actually no need for N) if( (slice_angle[1][i] < M_PI_2 && M_PI_2 < slice_angle[2][i]) && // E (this_x_explode_pos=OX(i,M_PI_2,1)) > cx+cwidth ) rad -= (float)ABS( (double)(1+this_x_explode_pos-(cx+cwidth))/sin(M_PI_2) ); if( (slice_angle[1][i] < 3.0*M_PI_2 && 3.0*M_PI_2 < slice_angle[2][i]) && // W (this_x_explode_pos=OX(i,3.0*M_PI_2,1)) < cx-cwidth ) rad -= (float)ABS( (double)(this_x_explode_pos-(cx+cwidth))/sin(3.0*M_PI_2) ); if( (slice_angle[1][i] < M_PI && M_PI < slice_angle[2][i]) && // S (this_y_explode_pos=OY(i,M_PI,1)) > cy+cheight ) rad -= (float)ABS( (double)(1+this_y_explode_pos-(cy+cheight))/cos(M_PI) ); for( j=0; j<3; ++j ) { this_y_explode_pos = IY(i,j,1); if( this_y_explode_pos < cy-cheight ) rad -= (float)ABS( (double)((cy-cheight)-this_y_explode_pos)/cos((double)slice_angle[j][i]) ); if( this_y_explode_pos > cy+cheight ) rad -= (float)ABS( (double)(1+this_y_explode_pos-(cy+cheight))/cos((double)slice_angle[j][i]) ); this_x_explode_pos = IX(i,j,1); if( this_x_explode_pos < cx-cwidth ) rad -= (float)ABS( (double)((cx-cwidth)-this_x_explode_pos)/sin((double)slice_angle[j][i]) ); if( this_x_explode_pos > cx+cwidth ) rad -= (float)ABS( (double)(1+this_x_explode_pos-(cx+cwidth))/sin((double)slice_angle[j][i]) ); } } } others[i] = FALSE; } else { others[i] = TRUE; slice_angle[0][i] = -MAXFLOAT; } last += this; } } /* ----- go ahead and start the image ----- */ im = gdImageCreate( IMGWIDTH, IMGHEIGHT ); /* --- allocate the requested colors --- */ BGColor = clrallocate( im, GDCPIE_BGColor ); LineColor = clrallocate( im, GDCPIE_LineColor ); PlotColor = clrallocate( im, GDCPIE_PlotColor ); if( GDCPIE_EdgeColor != GDC_NOCOLOR ) { EdgeColor = clrallocate( im, GDCPIE_EdgeColor ); if( threeD ) EdgeColorShd = clrshdallocate( im, GDCPIE_EdgeColor ); } /* --- set color for each slice --- */ for( i=0; iMOD_2PI(pie_3D_rad+M_PI_2) ) { tmp_slice[num_slice_angles].i = i; tmp_slice[num_slice_angles].hidden = FALSE; tmp_slice[num_slice_angles].slice = slice_angle[0][i]; tmp_slice[num_slice_angles++].angle = MOD_2PI( pie_3D_rad+M_PI_2 ); } if( slice_angle[1][i]MOD_2PI(pie_3D_rad+3.0*M_PI_2) ) { tmp_slice[num_slice_angles].i = i; tmp_slice[num_slice_angles].hidden = FALSE; tmp_slice[num_slice_angles].slice = slice_angle[0][i]; tmp_slice[num_slice_angles++].angle = MOD_2PI( pie_3D_rad+3.0*M_PI_2 ); } } qsort( tmp_slice, num_slice_angles, sizeof(struct tmp_slice_t), ocmpr ); for( t=0; t M_PI ) /* which semicircle */ { lblx -= lbl_wdth; pctx = lblx; ++linex; } else --linex; switch( GDCPIE_percent_labels ) { case GDCPIE_PCT_LEFT: if( slice_angle[0][i] > M_PI ) pctx -= lbl_wdth-1; else lblx += pct_wdth+1; pcty = IY(i,0,0) - ( 1+GDC_fontc[GDCPIE_label_size].h ) / 2; break; case GDCPIE_PCT_RIGHT: if( slice_angle[0][i] > M_PI ) lblx -= pct_wdth-1; else pctx += lbl_wdth+1; pcty = IY(i,0,0) - ( 1+GDC_fontc[GDCPIE_label_size].h ) / 2; break; case GDCPIE_PCT_ABOVE: lbly += (1+GDC_fontc[GDCPIE_label_size].h) / 2; pcty = lbly - (GDC_fontc[GDCPIE_label_size].h); break; case GDCPIE_PCT_BELOW: lbly -= (1+GDC_fontc[GDCPIE_label_size].h) / 2; pcty = lbly + (GDC_fontc[GDCPIE_label_size].h) * num_nl; break; case GDCPIE_PCT_NONE: default: } if( GDCPIE_percent_labels != GDCPIE_PCT_NONE ) gdImageString( im, GDC_fontc[GDCPIE_label_size].f, slice_angle[0][i] <= M_PI? pctx: pctx+lbl_wdth-pct_wdth, pcty, pct_str, LineColor ); if( lbl[i] ) GDCImageStringNL( im, &GDC_fontc[GDCPIE_label_size], lblx, lbly, lbl[i], LineColor, slice_angle[0][i] <= M_PI? GDC_JUSTIFY_LEFT: GDC_JUSTIFY_RIGHT ); if( GDCPIE_label_line ) { float rad = liner; gdImageLine( im, linex, liney, IX(i,0,0), IY(i,0,0), LineColor ); } } } rad -= GDCPIE_label_dist; } fflush( img_fptr ); switch( GDC_image_type ) { case GDC_PNG: gdImagePng( im, img_fptr ); break; #ifdef HAVE_JPEG case GDC_JPEG: gdImageJpeg( im, img_fptr, GDC_jpeg_quality ); break; #endif case GDC_WBMP: gdImageWBMP( im, PlotColor, img_fptr ); break; case GDC_GIF: default: gdImageGif( im, img_fptr); } gdImageDestroy(im); return; }