/* GDCHART 0.10.0dev GDCHART.C 2 Nov 2000 */ /* Copyright Bruce Verderaime 1998, 1999, 2000 */ /* vi:set tabstop=4 */ #include #include #include #include #include #include #define GDC_INCL #define GDC_LIB #include "gdc.h" #include "gdchart.h" #define HIGHSET 0 #define LOWSET 1 #define CLOSESET 2 // scaled translation onto graph #define PX( x ) (int)( xorig + (setno*xdepth_3D) + (x)*xscl ) #define PY( y ) (int)( yorig - (setno*ydepth_3D) + (y)*yscl ) #define PV( y ) (int)( vyorig - (setno*ydepth_3D) + (y)*vyscl ) #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 #ifdef THUMB_VALS // ------------------------------------------------------------------- // draw an arrow at (x,y)-upper left in arrwclr to the size of SmallFont // could, with just a little difficulty, be made to accept a font size // ------------------------------------------------------------------- void smallarrow( gdImagePtr im, int x, int y, char up, int arrwclr ) { gdImageLine( im, x+2, y, x+2, y+GDC_fontc[GDC_SMALL].h, arrwclr ); gdImageLine( im, x+3, y, x+3, y+GDC_fontc[GDC_SMALL].h, arrwclr ); if( up ) /* oo */ { /* uoou */ gdImageSetPixel( im, x, y+2, arrwclr ); /* uuoouu */ gdImageSetPixel( im, x+1, y+2, arrwclr ); /* oo */ gdImageSetPixel( im, x+4, y+2, arrwclr ); /* oo */ gdImageSetPixel( im, x+5, y+2, arrwclr ); /* oo */ gdImageSetPixel( im, x+1, y+1, arrwclr ); /* oo */ gdImageSetPixel( im, x+4, y+1, arrwclr ); /* oo */ } /* oo */ else /* ddoodd */ { /* dood */ gdImageSetPixel( im, x, y+(GDC_fontc[GDC_SMALL].h-2), arrwclr ); /* oo */ gdImageSetPixel( im, x+1, y+(GDC_fontc[GDC_SMALL].h-2), arrwclr ); gdImageSetPixel( im, x+4, y+(GDC_fontc[GDC_SMALL].h-2), arrwclr ); gdImageSetPixel( im, x+5, y+(GDC_fontc[GDC_SMALL].h-2), arrwclr ); gdImageSetPixel( im, x+1, y+(GDC_fontc[GDC_SMALL].h-1), arrwclr ); gdImageSetPixel( im, x+4, y+(GDC_fontc[GDC_SMALL].h-1), arrwclr ); } } #endif #define SET_3D_POLY( gdp, x1, x2, y1, y2, xoff, yoff ) \ gdp[0].x = x1, gdp[0].y = y1, \ gdp[1].x = x1+(xoff), gdp[1].y = y1-yoff, \ gdp[2].x = x2+(xoff), gdp[2].y = y2-yoff, \ gdp[3].x = x2, gdp[3].y = y2 /* ------------------------------------------------------------------------- */ /* vals in pixels */ /* ref is front plane */ /* allows for intersecting 3D lines */ /* (also used for single 3D lines >:-Q */ struct YS { int y1; int y2; float slope; int lnclr; int shclr; }; static int qcmpr( const void *a, const void *b ) { if( ((struct YS*)a)->y2 < ((struct YS*)b)->y2 ) return 1; if( ((struct YS*)a)->y2 > ((struct YS*)b)->y2 ) return -1; return 0; } void draw_3d_line( gdImagePtr im, int y0, int x1, int x2, int y1[], int y2[], int xdepth, int ydepth, int num_sets, int clr[], int clrshd[] ) { #define F(x,i) (int)( (float)((x)-x1)*slope[i]+(float)y1[i] ) float depth_slope = xdepth==0? MAXFLOAT: (float)ydepth/(float)xdepth; float slope[num_sets]; int lnclr[num_sets], shclr[num_sets]; int i; int x; gdPoint poly[4]; struct YS ypts[num_sets]; for( i=0; idepth_slope? ypts[i].shclr: ypts[i].lnclr ); if( x == x1+1 ) // edging gdImageLine( im, x-1, ypts[i].y2, x-1+xdepth, ypts[i].y2-ydepth, -ypts[i].slope<=depth_slope? ypts[i].shclr: ypts[i].lnclr ); } } } /* ------------------------------------------------------------------------- */ /* vals in pixels */ /* ref is front plane */ void draw_3d_area( gdImagePtr im, int x1, int x2, int y0, // drawn from 0 int y1, int y2, int xdepth, int ydepth, int clr, int clrshd ) { gdPoint poly[4]; int y_intercept = 0; // if xdepth || ydepth if( xdepth || ydepth ) { float line_slope = x2==x1? MAXFLOAT: (float)-(y2-y1) / (float)(x2-x1); float depth_slope = xdepth==0? MAXFLOAT: (float)ydepth/(float)xdepth; y_intercept = (y1 > y0 && y2 < y0) || // line crosses y0 (y1 < y0 && y2 > y0)? (int)((1.0/ABS(line_slope))*(float)(ABS(y1-y0)))+x1: 0; // never // edging along y0 depth gdImageLine( im, x1+xdepth, y0-ydepth, x2+xdepth, y0-ydepth, clrshd ); SET_3D_POLY( poly, x1, x2, y1, y2, xdepth, ydepth ); // top gdImageFilledPolygon( im, poly, 4, line_slope>depth_slope? clrshd: clr ); SET_3D_POLY( poly, x1, x2, y0, y0, xdepth, ydepth+1 ); // along y axis gdImageFilledPolygon( im, poly, 4, clr ); SET_3D_POLY( poly, x2, x2, y0, y2, xdepth, ydepth ); // side gdImageFilledPolygon( im, poly, 4, clrshd ); if( y_intercept ) gdImageLine( im, y_intercept, y0, y_intercept+xdepth, y0-ydepth, clrshd ); // edging gdImageLine( im, x1, y0, x1+xdepth, y0-ydepth, clrshd ); // edging gdImageLine( im, x2, y0, x2+xdepth, y0-ydepth, clrshd ); // edging // SET_3D_POLY( poly, x2, x2, y0, y2, xdepth, ydepth ); // side // gdImageFilledPolygon( im, poly, 4, clrshd ); gdImageLine( im, x1, y1, x1+xdepth, y1-ydepth, clrshd ); // edging gdImageLine( im, x2, y2, x2+xdepth, y2-ydepth, clrshd ); // edging } if( y1 == y2 ) // bar rect SET_RECT( poly, x1, x2, y0, y1 ); // front else { poly[0].x = x1; poly[0].y = y0; poly[1].x = x2; poly[1].y = y0; poly[2].x = x2; poly[2].y = y2; poly[3].x = x1; poly[3].y = y1; } gdImageFilledPolygon( im, poly, 4, clr ); gdImageLine( im, x1, y0, x2, y0, clrshd ); // edging along y0 if( (xdepth || ydepth) && // front edging only on 3D (y1 y0 && y2 < y0 ) // line crosses from below y0 gdImageLine( im, y_intercept, y0, x2, y2, clrshd ); else if( y1 < y0 && y2 > y0 ) // line crosses from above y0 gdImageLine( im, x1, y1, y_intercept, y0, clrshd ); else // completely above gdImageLine( im, x1, y1, x2, y2, clrshd ); } } /* ------------------------------------------------------------------------- */ /* vals in pixels */ /* ref is front plane */ void draw_3d_bar( gdImagePtr im, int x1, int x2, int y0, int yhigh, int xdepth, int ydepth, int clr, int clrshd ) { #define SET_3D_BAR( gdp, x1, x2, y1, y2, xoff, yoff ) \ gdp[0].x = x1, gdp[0].y = y1, \ gdp[1].x = x1+(xoff), gdp[1].y = y1-yoff, \ gdp[2].x = x2+(xoff), gdp[2].y = y2-yoff, \ gdp[3].x = x2, gdp[3].y = y2 gdPoint poly[4]; int usd = MIN( y0, yhigh ); // up-side-down bars if( xdepth || ydepth ) { if( y0 != yhigh ) // 0 height? { SET_3D_BAR( poly, x2, x2, y0, yhigh, xdepth, ydepth ); // side gdImageFilledPolygon( im, poly, 4, clrshd ); } SET_3D_BAR( poly, x1, x2, usd, usd, xdepth, ydepth ); // top gdImageFilledPolygon( im, poly, 4, clr ); } SET_RECT( poly, x1, x2, y0, yhigh ); // front gdImageFilledPolygon( im, poly, 4, clr ); if( xdepth || ydepth ) gdImageLine( im, x1, usd, x2, usd, clrshd ); } /* ------------------------------------------------------------------------- */ struct BS { float y1; float y2; int clr; int shclr; }; static int barcmpr( const void *a, const void *b ) { if( ((struct BS*)a)->y2 < ((struct BS*)b)->y2 ) return -1; if( ((struct BS*)a)->y2 > ((struct BS*)b)->y2 ) return 1; return 0; } /* ------------------------------------------------------------------------- */ // simple two-point linear interpolation // attempts between first, then nearest void do_interpolations( int num_points, int interp_point, float vals[] ) { int i, j; float v1 = GDC_NOVALUE, v2 = GDC_NOVALUE; int p1 = -1, p2 = -1; // find backwards for( i=interp_point-1; i>=0 && p1==-1; --i ) if( vals[i] != GDC_NOVALUE && vals[i] != GDC_INTERP_VALUE ) { v1 = vals[i]; p1 = i; } // find forwards for( j=interp_point+1; j=0 && p2==-1; --i ) if( vals[i] != GDC_NOVALUE && vals[i] != GDC_INTERP_VALUE ) { v2 = vals[i]; p2 = i; } // no backwards sample, find forwards for( ; j 1) float *combo_data ) // only used on COMBO chart types { int i, j, k; int graphwidth; int grapheight; gdImagePtr im; gdImagePtr bg_img = NULL; float xorig, yorig, vyorig; float yscl = 0.0; float vyscl = 0.0; float xscl = 0.0; float vhighest = -MAXFLOAT; float vlowest = MAXFLOAT; float highest = -MAXFLOAT; float lowest = MAXFLOAT; gdPoint volpoly[4]; char do_vol = ( type == GDC_COMBO_HLC_BAR || // aka: combo type == GDC_COMBO_HLC_AREA || type == GDC_COMBO_LINE_BAR || type == GDC_COMBO_LINE_AREA || type == GDC_COMBO_LINE_LINE || type == GDC_3DCOMBO_HLC_BAR || type == GDC_3DCOMBO_HLC_AREA|| type == GDC_3DCOMBO_LINE_BAR|| type == GDC_3DCOMBO_LINE_AREA || type == GDC_3DCOMBO_LINE_LINE ); char threeD = ( type == GDC_3DAREA || type == GDC_3DLINE || type == GDC_3DBAR || type == GDC_3DFLOATINGBAR || type == GDC_3DHILOCLOSE || type == GDC_3DCOMBO_HLC_BAR || type == GDC_3DCOMBO_HLC_AREA|| type == GDC_3DCOMBO_LINE_BAR|| type == GDC_3DCOMBO_LINE_AREA || type == GDC_3DCOMBO_LINE_LINE ); char num_groups = num_sets; // set before num_sets gets adjusted char set_depth = ( GDC_stack_type == GDC_STACK_DEPTH )? num_groups: 1; char do_bar = ( type == GDC_3DBAR || // offset X objects to leave type == GDC_BAR || // room at X(0) and X(n) type == GDC_3DFLOATINGBAR || // i.e., not up against Y axes type == GDC_FLOATINGBAR); char do_ylbl_fractions = // %f format not given, or ( !GDC_ylabel_fmt || // format doesn't have a %,g,e,E,f or F strlen(GDC_ylabel_fmt) == strcspn(GDC_ylabel_fmt,"%geEfF") ); float ylbl_interval = 0.0; int num_lf_xlbls = 0; int xdepth_3Dtotal = 0; int ydepth_3Dtotal = 0; int xdepth_3D = 0; // affects PX() int ydepth_3D = 0; // affects PY() and PV() int hlf_barwdth = 0; // half bar widths int hlf_hlccapwdth = 0; // half cap widths for HLC_I_CAP and DIAMOND int annote_len = 0, annote_hgt = 0; /* args */ int setno = 0; // affects PX() and PY() float *uvals[ type == GDC_HILOCLOSE || type == GDC_3DHILOCLOSE || type == GDC_3DCOMBO_HLC_BAR || type == GDC_3DCOMBO_HLC_AREA || type == GDC_COMBO_HLC_BAR || type == GDC_COMBO_HLC_AREA? num_sets *= 3: // 1 more last set is vol type == GDC_FLOATINGBAR || type == GDC_3DFLOATINGBAR? num_sets *= 2: type == GDC_COMBO_LINE_BAR || type == GDC_3DCOMBO_LINE_BAR || type == GDC_3DCOMBO_LINE_AREA|| type == GDC_3DCOMBO_LINE_LINE|| type == GDC_COMBO_LINE_AREA || type == GDC_COMBO_LINE_LINE? num_sets: // 1 more last set is vol num_sets ]; float *uvol; int BGColor, LineColor, PlotColor, GridColor, VolColor, ExtVolColor[num_points], ThumbDColor, ThumbLblColor, ThumbUColor, // ArrowDColor, // ArrowUColor, AnnoteColor, ExtColor[num_sets][num_points]; // shade colors only with 3D // int ExtColorShd[threeD?1:num_sets][threeD?1:num_points]; // compiler limitation int ExtColorShd[num_sets][num_points]; /* idiot checks */ if( IMGWIDTH<=0 || IMGHEIGHT<=0 || (!img_fptr && GDC_generate_img) ) return -1; if( num_points <= 0 ) { out_err( IMGWIDTH, IMGHEIGHT, img_fptr, GDC_BGColor, GDC_LineColor, "No Data Available" ); return 1; } load_font_conversions(); if( GDC_thumbnail ) { GDC_grid = FALSE; GDC_xaxis = FALSE; GDC_yaxis = FALSE; } // ----- get args ----- for( i=0; ival, highest ); lowest = MIN( (GDC_scatter+i)->val, lowest ); } if( do_vol ) // for now only one combo set allowed { // vhighest = 1.0; // vlowest = 0.0; for( j=0; j 0.0 ) vlowest = 0.0; // bar, area should always start at 0 } if( lowest == MAXFLOAT ) lowest = 0.0; if( highest == -MAXFLOAT ) highest = 1.0; // need a range if( type == GDC_AREA || // bars and area should always start at 0 type == GDC_BAR || type == GDC_3DBAR || type == GDC_3DAREA ) if( highest < 0.0 ) highest = 0.0; else if( lowest > 0.0 ) // negs should be drawn from 0 lowest = 0.0; if( GDC_requested_ymin != GDC_NOVALUE && GDC_requested_ymin < lowest ) lowest = GDC_requested_ymin; if( GDC_requested_ymax != GDC_NOVALUE && GDC_requested_ymax > highest ) highest = GDC_requested_ymax; /* ----- graph height and width within the img height width ----- */ /* grapheight/height is the actual size of the scalable graph */ { int title_hgt = GDC_title? 2 /* title? horizontal text line(s) */ + cnt_nl(GDC_title,(int*)NULL)*GDC_fontc[GDC_title_size].h + 2: 2; int xlabel_hgt = 0; int xtitle_hgt = GDC_xtitle? 1+GDC_fontc[GDC_xtitle_size].h+1: 0; int ytitle_hgt = GDC_ytitle? 1+GDC_fontc[GDC_ytitle_size].h+1: 0; int vtitle_hgt = do_vol&&GDC_ytitle2? 1+GDC_fontc[GDC_ytitle_size].h+1: 0; int ylabel_wth = 0; int vlabel_wth = 0; int xtics = GDC_ticks && (GDC_grid||GDC_xaxis)? 1+2: 0; int ytics = GDC_ticks && (GDC_grid||GDC_yaxis)? 1+3: 0; int vtics = GDC_ticks && (GDC_yaxis&&do_vol)? 3+1: 0; #define HYP_DEPTH ( (double)((IMGWIDTH+IMGHEIGHT)/2) * ((double)GDC_3d_depth)/100.0 ) #define RAD_DEPTH ( (double)GDC_3d_angle*2*M_PI/360 ) xdepth_3D = threeD? (int)( cos(RAD_DEPTH) * HYP_DEPTH ): 0; ydepth_3D = threeD? (int)( sin(RAD_DEPTH) * HYP_DEPTH ): 0; xdepth_3Dtotal = xdepth_3D*set_depth; ydepth_3Dtotal = ydepth_3D*set_depth; annote_hgt = GDC_annotation && *(GDC_annotation->note)? 1 + /* space to note */ (1+GDC_fontc[GDC_annotation_font].h) * /* number of '\n' substrs */ cnt_nl(GDC_annotation->note,&annote_len) + 1 + /* space under note */ 2: 0; /* space to chart */ annote_len *= GDC_fontc[GDC_annotation_font].w; if( GDC_xaxis && xlbl ) { int biggest = -MAXINT; for( i=0; i ylbl_interval ) // break; if( (highest-lowest)/ypoints[i] < ((float)max_num_ylbls-(1.0+1.0)) * (float)GDC_ylabel_density/100.0 ) break; /* gotta go through the above loop to catch the 'tweeners :-| */ ylbl_interval = GDC_requested_yinterval != GDC_NOVALUE && GDC_requested_yinterval > ypoints[i-1]? GDC_requested_yinterval: ypoints[i-1]; /* perform floating point remainders */ /* gonculate largest interval-point < lowest */ if( lowest != 0.0 && lowest != GDC_requested_ymin ) { if( lowest < 0.0 ) lowest -= ylbl_interval; // lowest = (lowest-ypoints[0]) - // ( ( ((lowest-ypoints[0])/ylbl_interval)*ylbl_interval ) - // ( (float)((int)((lowest-ypoints[0])/ylbl_interval))*ylbl_interval ) ); lowest = ylbl_interval * (float)(int)((lowest-ypoints[0])/ylbl_interval); } /* find smallest interval-point > highest */ tmp_highest = lowest; do // while( (tmp_highest += ylbl_interval) <= highest ) { int nmrtr, dmntr, whole; char *price_to_str( float, int*, int*, int*, char* ); int lbl_len; char foo[32]; if( GDC_yaxis ) { /* XPG2 compatibility */ sprintf( foo, do_ylbl_fractions? "%.0f": GDC_ylabel_fmt, tmp_highest ); lbl_len = ylbl_interval<1.0? strlen( price_to_str(tmp_highest, &nmrtr, &dmntr, &whole, do_ylbl_fractions? NULL: GDC_ylabel_fmt) ): strlen( foo ); longest_ylblen = MAX( longest_ylblen, lbl_len ); } } while( (tmp_highest += ylbl_interval) <= highest ); ylabel_wth = longest_ylblen * GDC_fontc[GDC_yaxisfont_size].w; highest = GDC_requested_ymax==GDC_NOVALUE? tmp_highest: MAX( GDC_requested_ymax, highest ); if( do_vol ) { float num_yintrvls = (highest-lowest) / ylbl_interval; /* no skyscrapers */ if( vhighest != 0.0 ) vhighest += (vhighest-vlowest) / (num_yintrvls*2.0); if( vlowest != 0.0 ) vlowest -= (vhighest-vlowest) / (num_yintrvls*2.0); if( GDC_yaxis2 ) { char svlongest[32]; int lbl_len_low = sprintf( svlongest, GDC_ylabel2_fmt? GDC_ylabel2_fmt: "%.0f", vlowest ); int lbl_len_high = sprintf( svlongest, GDC_ylabel2_fmt? GDC_ylabel2_fmt: "%.0f", vhighest ); vlabel_wth = 1 + MAX( lbl_len_low,lbl_len_high ) * GDC_fontc[GDC_yaxisfont_size].w; } } } graphwidth = IMGWIDTH - ( ( (GDC_hard_size && GDC_hard_xorig)? GDC_hard_xorig: ( ytitle_hgt + ylabel_wth + ytics ) ) + vtics + vtitle_hgt + vlabel_wth + xdepth_3Dtotal ); if( GDC_hard_size && GDC_hard_graphwidth ) /* user wants to use his */ graphwidth = GDC_hard_graphwidth; GDC_hard_graphwidth = graphwidth; /* ----- scale to img size ----- */ /* offset to 0 at lower left (where it should be) */ xscl = (float)(graphwidth-xdepth_3Dtotal) / (float)(num_points + (do_bar?2:0)); yscl = -((float)grapheight) / (float)(highest-lowest); if( do_vol ) { float hilow_diff = vhighest-vlowest==0.0? 1.0: vhighest-vlowest; vyscl = -((float)grapheight) / hilow_diff; vyorig = (float)grapheight + ABS(vyscl) * MIN(vlowest,vhighest) + ydepth_3Dtotal + title_hgt + annote_hgt; } xorig = (float)( IMGWIDTH - ( graphwidth + vtitle_hgt + vtics + vlabel_wth ) ); if( GDC_hard_size && GDC_hard_xorig ) xorig = GDC_hard_xorig; GDC_hard_xorig = xorig; // yorig = (float)grapheight + ABS(yscl * lowest) + ydepth_3Dtotal + title_hgt; yorig = (float)grapheight + ABS(yscl) * MIN(lowest,highest) + ydepth_3Dtotal + title_hgt + annote_hgt; //???? if( GDC_hard_size && GDC_hard_yorig ) /* vyorig too? */ //???? yorig = GDC_hard_yorig; GDC_hard_yorig = yorig; hlf_barwdth = (int)( (float)(PX(2)-PX(1)) * (((float)GDC_bar_width/100.0)/2.0) ); // used only for bars hlf_hlccapwdth = (int)( (float)(PX(2)-PX(1)) * (((float)GDC_HLC_cap_width/100.0)/2.0) ); } // scaled, sized, ready /* ----- OK start the graphic ----- */ if( (GDC_hold_img & GDC_REUSE_IMAGE) && GDC_image != (void*)NULL ) im = GDC_image; else im = gdImageCreate( IMGWIDTH, IMGHEIGHT ); BGColor = gdImageColorAllocate( im, l2gdcal(GDC_BGColor) ); LineColor = clrallocate( im, GDC_LineColor ); PlotColor = clrallocate( im, GDC_PlotColor ); GridColor = clrallocate( im, GDC_GridColor ); if( do_vol ) { VolColor = clrallocate( im, GDC_VolColor ); for( i=0; icolor ); /* attempt to import optional background image */ if( GDC_BGImage ) { FILE *in = fopen(GDC_BGImage, "rb"); if( !in ) { ; // Cant load background image, drop it } else { // assume GIF // should determine type by file extension, option, ... if( bg_img = gdImageCreateFromGif(in) ) // = { int bgxpos = gdImageSX(bg_img) IMGWIDTH || // resize only if too big gdImageSY(bg_img) > IMGHEIGHT ) // [and center] { gdImageCopyResized( im, bg_img, // dst, src bgxpos, bgypos, // dstX, dstY 0, 0, // srcX, srcY IMGWIDTH, IMGHEIGHT, // dstW, dstH IMGWIDTH, IMGHEIGHT ); // srcW, srcH } else // just center gdImageCopy( im, bg_img, // dst, src bgxpos, bgypos, // dstX, dstY 0, 0, // srcX, srcY IMGWIDTH, IMGHEIGHT ); // W, H } fclose(in); } } for( j=0; j= 0.0 ) // all pos plotting continue; else tmp_y = MIN( 0, highest ); // step down to lowest if( i == 1 ) if( highest <= 0.0 ) // all neg plotting continue; else tmp_y = MAX( 0, lowest ); // step up to highest // if( !(highest > 0 && lowest < 0) ) // doesn't straddle 0 // { // if( i == -1 ) // only do once: normal // continue; // } // else // tmp_y = 0; do // while( (tmp_y (+-)= ylbl_interval) < [highest,lowest] ) { int n, d, w; char *price_to_str( float, int*, int*, int*, char* ); char nmrtr[3+1], dmntr[3+1], whole[8]; char all_whole = ylbl_interval<1.0? FALSE: TRUE; char *ylbl_str = price_to_str( tmp_y,&n,&d,&w, do_ylbl_fractions? NULL: GDC_ylabel_fmt ); if( do_ylbl_fractions ) { sprintf( nmrtr, "%d", n ); sprintf( dmntr, "%d", d ); sprintf( whole, "%d", w ); } if( GDC_grid || GDC_ticks ) { int x1, x2, y1, y2; // int gridline_clr = tmp_y == 0.0? LineColor: GridColor; // tics x1 = PX(0); y1 = PY(tmp_y); if( GDC_ticks ) gdImageLine( im, x1-2, y1, x1, y1, GridColor ); if( GDC_grid ) { setno = set_depth; x2 = PX(0); y2 = PY(tmp_y); // w/ new setno gdImageLine( im, x1, y1, x2, y2, GridColor ); // depth for 3Ds gdImageLine( im, x2, y2, PX(num_points-1+(do_bar?2:0)), y2, GridColor ); setno = 0; // set back to foremost } } if( GDC_yaxis ) if( do_ylbl_fractions ) { if( w || (!w && !n && !d) ) { gdImageString( im, GDC_fontc[GDC_yaxisfont_size].f, PX(0)-2-strlen(whole)*GDC_fontc[GDC_yaxisfont_size].w - ( (!all_whole)? (strlen(nmrtr)*GDC_fontc[GDC_yaxisfont_size-1].w + GDC_fontc[GDC_yaxisfont_size].w + strlen(nmrtr)*GDC_fontc[GDC_yaxisfont_size-1].w) : 1 ), PY(tmp_y)-GDC_fontc[GDC_yaxisfont_size].h/2, whole, labelcolor ); } if( n ) { gdImageString( im, GDC_fontc[GDC_yaxisfont_size-1].f, PX(0)-2-strlen(nmrtr)*GDC_fontc[GDC_yaxisfont_size-1].w -GDC_fontc[GDC_yaxisfont_size].w -strlen(nmrtr)*GDC_fontc[GDC_yaxisfont_size-1].w + 1, PY(tmp_y)-GDC_fontc[GDC_yaxisfont_size].h/2 + 1, nmrtr, labelcolor ); gdImageString( im, GDC_fontc[GDC_yaxisfont_size].f, PX(0)-2-GDC_fontc[GDC_yaxisfont_size].w -strlen(nmrtr)*GDC_fontc[GDC_yaxisfont_size-1].w, PY(tmp_y)-GDC_fontc[GDC_yaxisfont_size].h/2, "/", labelcolor ); gdImageString( im, GDC_fontc[GDC_yaxisfont_size-1].f, PX(0)-2-strlen(nmrtr)*GDC_fontc[GDC_yaxisfont_size-1].w - 2, PY(tmp_y)-GDC_fontc[GDC_yaxisfont_size].h/2 + 3, dmntr, labelcolor ); } } else gdImageString( im, GDC_fontc[GDC_yaxisfont_size].f, PX(0)-2-strlen(ylbl_str)*GDC_fontc[GDC_yaxisfont_size].w, PY(tmp_y)-GDC_fontc[GDC_yaxisfont_size].h/2, ylbl_str, labelcolor ); if( do_vol && GDC_yaxis2 ) { char vylbl[16]; /* opposite of PV(y) */ sprintf( vylbl, GDC_ylabel2_fmt? GDC_ylabel2_fmt: "%.0f", ((float)(PY(tmp_y)+(setno*ydepth_3D)-vyorig))/vyscl ); setno = set_depth; if( GDC_ticks ) gdImageLine( im, PX(num_points-1+(do_bar?2:0)), PY(tmp_y), PX(num_points-1+(do_bar?2:0))+3, PY(tmp_y), GridColor ); if( atof(vylbl) == 0.0 ) /* rounding can cause -0 */ strcpy( vylbl, "0" ); gdImageString( im, GDC_fontc[GDC_yaxisfont_size].f, PX(num_points-1+(do_bar?2:0))+6, PY(tmp_y)-GDC_fontc[GDC_yaxisfont_size].h/2, vylbl, label2color ); setno = 0; } } while( ((i>0) && ((tmp_y += ylbl_interval) < highest)) || ((i<0) && ((tmp_y -= ylbl_interval) > lowest)) ); } /* catch last (bottom) grid line - specific to an "off" requested interval */ if( GDC_grid && threeD ) { setno = set_depth; gdImageLine( im, PX(0), PY(lowest), PX(num_points-1+(do_bar?2:0)), PY(lowest), GridColor ); setno = 0; // set back to foremost } /* vy axis title */ if( do_vol && GDC_ytitle2 ) { int titlecolor = GDC_YTitle2Color==GDC_DFLTCOLOR? VolColor: clrallocate( im, GDC_YTitle2Color ); gdImageStringUp( im, GDC_fontc[GDC_ytitle_size].f, IMGWIDTH-(1+GDC_fontc[GDC_ytitle_size].h), strlen(GDC_ytitle2)*GDC_fontc[GDC_ytitle_size].w/2 + grapheight/2, GDC_ytitle2, titlecolor ); } /* y axis title */ if( GDC_yaxis && GDC_ytitle ) { int ytit_len = strlen(GDC_ytitle)*GDC_fontc[GDC_ytitle_size].w; int titlecolor = GDC_YTitleColor==GDC_DFLTCOLOR? PlotColor: clrallocate( im, GDC_YTitleColor ); gdImageStringUp( im, GDC_fontc[GDC_ytitle_size].f, 0, IMGHEIGHT/2 + ytit_len/2, GDC_ytitle, titlecolor ); } } /* interviening set grids */ /* 0 < setno < num_sets non-inclusive, they've already been covered */ if( GDC_grid && threeD ) { for( setno=set_depth - 1; setno > 0; --setno ) { gdImageLine( im, PX(0), PY(lowest), PX(0), PY(highest), GridColor ); gdImageLine( im, PX(0), PY(lowest), PX(num_points-1+(do_bar?2:0)), PY(lowest), GridColor ); } setno = 0; } if( ( GDC_grid || GDC_0Shelf ) && /* line color grid at 0 */ ( (lowest < 0.0 && highest > 0.0) || ( (lowest == 0.0 || highest == 0.0) && !(GDC_border&GDC_BORDER_X) ) ) ) { int x1, x2, y1, y2; // tics x1 = PX(0); y1 = PY(0); if( GDC_ticks ) gdImageLine( im, x1-2, y1, x1, y1, LineColor ); setno = set_depth; x2 = PX(0); y2 = PY(0); // w/ new setno gdImageLine( im, x1, y1, x2, y2, LineColor ); // depth for 3Ds gdImageLine( im, x2, y2, PX(num_points-1+(do_bar?2:0)), y2, LineColor ); setno = 0; // set back to foremost } /* x ticks and xlables */ if( GDC_grid || GDC_xaxis ) { int num_xlbls = /* maximum x lables that'll fit */ /* each xlbl + avg due to num_lf_xlbls */ graphwidth / ( (GDC_xlabel_spacing==MAXSHORT?0:GDC_xlabel_spacing)+GDC_fontc[GDC_xaxisfont_size].h + (num_lf_xlbls*(GDC_fontc[GDC_xaxisfont_size].h-1))/num_points ); int labelcolor = GDC_XLabelColor==GDC_DFLTCOLOR? LineColor: clrallocate( im, GDC_XLabelColor ); for( i=0; i= num_points || GDC_xlabel_spacing == MAXSHORT )) || (GDC_xlabel_ctl && xi>=0 && *(GDC_xlabel_ctl+xi)) ) { DO_TICK(x1,y1); // labeled points tick & grid DO_GRID(x1,y1,x2,y2); if( !do_bar || (i>0 && xi0 ) { if( GDC_grid == GDC_TICK_POINTS ) /* --- GRID --- */ DO_GRID( x1, y1, x2, y2 ); else if( GDC_grid > GDC_TICK_NONE ) { int k; int xt; int prevx = PX(i-1); int intrv_dist = (x1-prevx)/(GDC_grid+1); DO_GRID( x1, y1, x2, y2 ); for( k=0, xt=prevx + intrv_dist; k GDC_TICK_NONE ) { int k; int xt; int prevx=PX(i-1); int intrv_dist = (x1-prevx)/(GDC_ticks+1); DO_TICK( x1, y1 ); for( k=0, xt=prevx + intrv_dist; kpoint+(do_bar?1:0)), y1 = PY(lowest); setno = set_depth; gdImageLine( im, x1, y1, PX(GDC_annotation->point+(do_bar?1:0)), PY(lowest), AnnoteColor ); gdImageLine( im, PX(GDC_annotation->point+(do_bar?1:0)), PY(lowest), PX(GDC_annotation->point+(do_bar?1:0)), PY(highest)-2, AnnoteColor ); setno = 0; } /* ---------- start plotting the data ---------- */ switch( type ) { case GDC_3DBAR: /* depth, width, y interval need to allow for whitespace between bars */ case GDC_BAR: /* --------- */ switch( GDC_stack_type ) { case GDC_STACK_DEPTH: for( setno=num_sets-1; setno>=0; --setno ) // back sets first PX, PY depth for( i=0; i=0; --setno ) // back sets first PX, PY depth for( i=0; i uvals[0+setno*2][i] ) draw_3d_bar( im, PX(i+(do_bar?1:0))-hlf_barwdth, PX(i+(do_bar?1:0))+hlf_barwdth, PY(uvals[0+setno*2][i]), PY(uvals[1+setno*2][i]), xdepth_3D, ydepth_3D, ExtColor[setno][i], threeD? ExtColorShd[setno][i]: ExtColor[setno][i] ); setno = 0; break; case GDC_STACK_BESIDE: { // h/.5, h/1, h/1.5, h/2, ... int new_barwdth = (int)( (float)hlf_barwdth / ((float)num_groups/2.0) ); for( i=0; i uvals[0+j*2][i] ) draw_3d_bar( im, PX(i+(do_bar?1:0))-hlf_barwdth+new_barwdth*j+1, PX(i+(do_bar?1:0))-hlf_barwdth+new_barwdth*(j+1), PY(uvals[0+j*2][i]), PY(uvals[1+j*2][i]), xdepth_3D, ydepth_3D, ExtColor[j][i], threeD? ExtColorShd[j][i]: ExtColor[j][i] ); } break; } break; case GDC_LINE: case GDC_COMBO_LINE_BAR: case GDC_COMBO_LINE_AREA: case GDC_COMBO_LINE_LINE: for( j=num_sets-1; j>=0; --j ) for( i=1; i=0; --j ) if( uvals[j][i-1] != GDC_NOVALUE && uvals[j][i] != GDC_NOVALUE ) { setno = j; y1[j] = PY(uvals[j][i-1]); y2[j] = PY(uvals[j][i]); draw_3d_line( im, PY(0), PX(i-1), PX(i), &(y1[j]), &(y2[j]), xdepth_3D, ydepth_3D, 1, &(ExtColor[j][i]), &(ExtColorShd[j][i]) ); setno = 0; } } else if( GDC_stack_type == GDC_STACK_BESIDE || GDC_stack_type == GDC_STACK_SUM ) // all same plane { int set; int clr[num_sets], clrshd[num_sets]; float usey1 = 0.0, usey2 = 0.0; for( j=0,set=0; j=0; --j ) // back sets 1st (setno = 0) for( i=1; i=0; --setno ) // back sets first PX, PY depth for( i=1; i=0; --j ) { for( i=1; i PY(uvals[CLOSESET+j*3][i-1])-hlf_hlccapwdth) ) && uvals[HIGHSET+j*3][i-1] != GDC_NOVALUE ) if( GDC_HLC_style & GDC_HLC_I_CAP ) { SET_3D_POLY( poly, PX(i-1)-hlf_hlccapwdth, PX(i-1)+hlf_hlccapwdth, PY(uvals[HIGHSET+j*3][i-1]), PY(uvals[HIGHSET+j*3][i-1]), xdepth_3D, ydepth_3D ); gdImageFilledPolygon( im, poly, 4, ExtColor[HIGHSET+j*3][i-1] ); gdImagePolygon( im, poly, 4, ExtColorShd[HIGHSET+j*3][i-1] ); } if( i < num_points && uvals[CLOSESET+j*3][i] != GDC_NOVALUE ) { if( GDC_HLC_style & GDC_HLC_CLOSE_CONNECTED ) /* line from prev close */ { SET_3D_POLY( poly, PX(i-1), PX(i), PY(uvals[CLOSESET+j*3][i-1]), PY(uvals[CLOSESET+j*3][i-1]), xdepth_3D, ydepth_3D ); gdImageFilledPolygon( im, poly, 4, ExtColor[CLOSESET+j*3][i] ); gdImagePolygon( im, poly, 4, ExtColorShd[CLOSESET+j*3][i] ); } else // CLOSE_CONNECTED and CONNECTING are mutually exclusive if( GDC_HLC_style & GDC_HLC_CONNECTING ) /* thin connecting line */ { int y1 = PY(uvals[CLOSESET+j*3][i-1]), y2 = PY(uvals[CLOSESET+j*3][i]); draw_3d_line( im, PY(0), PX(i-1), PX(i), &y1, &y2, // rem only 1 set xdepth_3D, ydepth_3D, 1, &(ExtColor[CLOSESET+j*3][i]), &(ExtColorShd[CLOSESET+j*3][i]) ); // edge font of it gdImageLine( im, PX(i-1), PY(uvals[CLOSESET+j*3][i-1]), PX(i), PY(uvals[CLOSESET+j*3][i]), ExtColorShd[CLOSESET+j*3][i] ); } // top half 'I' again if( PY(uvals[CLOSESET+j*3][i-1]) <= PY(uvals[CLOSESET+j*3][i]) && uvals[HIGHSET+j*3][i-1] != GDC_NOVALUE ) if( GDC_HLC_style & GDC_HLC_I_CAP ) { SET_3D_POLY( poly, PX(i-1)-hlf_hlccapwdth, PX(i-1)+hlf_hlccapwdth, PY(uvals[HIGHSET+j*3][i-1]), PY(uvals[HIGHSET+j*3][i-1]), xdepth_3D, ydepth_3D ); gdImageFilledPolygon( im, poly, 4, ExtColor[HIGHSET+j*3][i-1] ); gdImagePolygon( im, poly, 4, ExtColorShd[HIGHSET+j*3][i-1] ); } } if( GDC_HLC_style & GDC_HLC_DIAMOND ) { // front poly[0].x = PX(i-1)-hlf_hlccapwdth; poly[0].y = PY(uvals[CLOSESET+j*3][i-1]); poly[1].x = PX(i-1); poly[1].y = PY(uvals[CLOSESET+j*3][i-1])+hlf_hlccapwdth; poly[2].x = PX(i-1)+hlf_hlccapwdth; poly[2].y = PY(uvals[CLOSESET+j*3][i-1]); poly[3].x = PX(i-1); poly[3].y = PY(uvals[CLOSESET+j*3][i-1])-hlf_hlccapwdth; gdImageFilledPolygon( im, poly, 4, ExtColor[CLOSESET+j*3][i-1] ); gdImagePolygon( im, poly, 4, ExtColorShd[CLOSESET+j*3][i-1] ); // bottom side SET_3D_POLY( poly, PX(i-1), PX(i-1)+hlf_hlccapwdth, PY(uvals[CLOSESET+j*3][i-1])+hlf_hlccapwdth, PY(uvals[CLOSESET+j*3][i-1]), xdepth_3D, ydepth_3D ); gdImageFilledPolygon( im, poly, 4, ExtColorShd[CLOSESET+j*3][i-1] ); // gdImagePolygon( im, poly, 4, ExtColor[CLOSESET+j*3][i-1] ); // top side SET_3D_POLY( poly, PX(i-1), PX(i-1)+hlf_hlccapwdth, PY(uvals[CLOSESET+j*3][i-1])-hlf_hlccapwdth, PY(uvals[CLOSESET+j*3][i-1]), xdepth_3D, ydepth_3D ); gdImageFilledPolygon( im, poly, 4, ExtColor[CLOSESET+j*3][i-1] ); gdImagePolygon( im, poly, 4, ExtColorShd[CLOSESET+j*3][i-1] ); } } } } break; case GDC_HILOCLOSE: case GDC_COMBO_HLC_BAR: case GDC_COMBO_HLC_AREA: for( j=num_groups-1; j>=0; --j ) { for( i=0; iwidth)/100.0)/2.0) ); int scat_x = PX( (GDC_scatter+i)->point + (do_bar?1:0) ), scat_y = PY( (GDC_scatter+i)->val ); if( (GDC_scatter+i)->point >= num_points || // invalid point (GDC_scatter+i)->point < 0 ) continue; scatter_clr[i] = clrallocate( im, (GDC_scatter+i)->color ); switch( (GDC_scatter+i)->ind ) { case GDC_SCATTER_CIRCLE: { long uniq_clr = get_uniq_color( im ); int s = 0, e = 360, fo = 0; if( !do_bar ) if( (GDC_scatter+i)->point == 0 ) { s = 270; e = 270+180; fo = 1; } else if( (GDC_scatter+i)->point == num_points-1 ) { s = 90; e = 90+180; fo = -1; } if( uniq_clr != -1L ) // the safe way { int uc = gdImageColorAllocate( im, l2gdcal(uniq_clr) ); gdImageArc( im, scat_x, scat_y, hlf_scatterwdth*2, hlf_scatterwdth*2, s, e, uc ); if( fo ) // close off semi-circle case gdImageLine( im, scat_x, scat_y+hlf_scatterwdth, scat_x, scat_y-hlf_scatterwdth, uc ); gdImageFillToBorder( im, scat_x+fo, scat_y, uc, scatter_clr[i] ); gdImageArc( im, scat_x, scat_y, hlf_scatterwdth*2, hlf_scatterwdth*2, s, e, scatter_clr[i] ); if( fo ) gdImageLine( im, scat_x, scat_y+hlf_scatterwdth, scat_x, scat_y-hlf_scatterwdth, scatter_clr[i] ); gdImageColorDeallocate( im, uc ); } else // chance it { gdImageArc( im, scat_x, scat_y, hlf_scatterwdth*2, hlf_scatterwdth*2, s, e, scatter_clr[i] ); if( fo ) gdImageLine( im, scat_x, scat_y+hlf_scatterwdth, scat_x, scat_y-hlf_scatterwdth, scatter_clr[i] ); gdImageFillToBorder( im, scat_x+fo, scat_y, scatter_clr[i], scatter_clr[i] ); } } break; case GDC_SCATTER_TRIANGLE_UP: ct[0].x = scat_x; ct[0].y = scat_y; ct[1].x = scat_x - hlf_scatterwdth; ct[1].y = scat_y + hlf_scatterwdth;; ct[2].x = scat_x + hlf_scatterwdth; ct[2].y = scat_y + hlf_scatterwdth; if( !do_bar ) if( (GDC_scatter+i)->point == 0 ) ct[1].x = scat_x; else if( (GDC_scatter+i)->point == num_points-1 ) ct[2].x = scat_x; gdImageFilledPolygon( im, ct, 3, scatter_clr[i] ); break; case GDC_SCATTER_TRIANGLE_DOWN: ct[0].x = scat_x; ct[0].y = scat_y; ct[1].x = scat_x - hlf_scatterwdth; ct[1].y = scat_y - hlf_scatterwdth;; ct[2].x = scat_x + hlf_scatterwdth; ct[2].y = scat_y - hlf_scatterwdth; if( !do_bar ) if( (GDC_scatter+i)->point == 0 ) ct[1].x = scat_x; else if( (GDC_scatter+i)->point == num_points-1 ) ct[2].x = scat_x; gdImageFilledPolygon( im, ct, 3, scatter_clr[i] ); break; } } } // overlay with a value and an arrow (e.g., total daily change) #ifdef THUMB_VALS /* put thmbl and thumbval over vol and plot lines */ if( thumbnail ) { int n, d, w; char thmbl[32]; char *price_to_str( float, int*, int*, int* ); char nmrtr[3+1], dmntr[3+1], whole[8]; char *dbg = price_to_str( ABS(thumbval),&n,&d,&w ); sprintf( nmrtr, "%d", n ); sprintf( dmntr, "%d", d ); sprintf( whole, "%d", w ); gdImageString( im, gdFontSmall, graphwidth/2-strlen(thumblabel)*SFONTWDTH/2, 1, thumblabel, ThumbLblColor ); if( w || n ) { int chgcolor = thumbval>0.0? ThumbUColor: ThumbDColor; int thmbvalwidth = SFONTWDTH + // up/down arrow (w?strlen(whole)*SFONTWDTH: 0) + // whole (n?strlen(nmrtr)*TFONTWDTH + // numerator SFONTWDTH + // / strlen(dmntr)*TFONTWDTH: // denominator 0); // no frac part smallarrow( im, graphwidth/2-thmbvalwidth/2, SFONTHGT, thumbval>0.0, chgcolor ); if( w ) { gdImageString( im, gdFontSmall, (graphwidth/2-thmbvalwidth/2)+SFONTWDTH, SFONTHGT+2, whole, chgcolor ); } if( n ) { gdImageString( im, gdFontTiny, (graphwidth/2-thmbvalwidth/2) + // start SFONTWDTH + // arrow (w? strlen(whole)*SFONTWDTH: 0) + // whole 2, SFONTHGT+2-2, nmrtr, chgcolor ); gdImageChar ( im, gdFontSmall, (graphwidth/2-thmbvalwidth/2) + // start SFONTWDTH + // arrow (w? strlen(whole)*SFONTWDTH: 0) + // whole strlen(nmrtr)*TFONTWDTH, // numerator SFONTHGT+2, '/', chgcolor ); gdImageString( im, gdFontTiny, (graphwidth/2-thmbvalwidth/2) + // start SFONTWDTH + // arrow (w? strlen(whole)*SFONTWDTH: 0) + // whole strlen(nmrtr)*TFONTWDTH + // numerator SFONTWDTH - 3, // / SFONTHGT+2+4, dmntr, chgcolor ); } } } // thumblabel, thumbval #endif /* box it off */ /* after plotting so the outline covers any plot lines */ { if( GDC_border == GDC_BORDER_ALL || (GDC_border & GDC_BORDER_X) ) gdImageLine( im, PX(0), PY(lowest), PX(num_points-1+(do_bar?2:0)), PY(lowest), LineColor ); if( GDC_border == GDC_BORDER_ALL || (GDC_border & GDC_BORDER_TOP) ) { setno = set_depth; gdImageLine( im, PX(0), PY(highest), PX(num_points-1+(do_bar?2:0)), PY(highest), LineColor ); setno = 0; } } if( GDC_border ) { int x1, y1, x2, y2; x1 = PX(0); y1 = PY(highest); x2 = PX(num_points-1+(do_bar?2:0)); y2 = PY(lowest); if( GDC_border == GDC_BORDER_ALL || (GDC_border & GDC_BORDER_Y) ) gdImageLine( im, x1, PY(lowest), x1, y1, LineColor ); setno = set_depth; if( GDC_border == GDC_BORDER_ALL || (GDC_border & GDC_BORDER_Y) || (GDC_border & GDC_BORDER_TOP) ) gdImageLine( im, x1, y1, PX(0), PY(highest), LineColor ); // if( !GDC_grid || do_vol || GDC_thumbnail ) // grid leaves right side Y open { if( GDC_border == GDC_BORDER_ALL || (GDC_border & GDC_BORDER_X) || (GDC_border & GDC_BORDER_Y2) ) gdImageLine( im, x2, y2, PX(num_points-1+(do_bar?2:0)), PY(lowest), LineColor ); if( GDC_border == GDC_BORDER_ALL || (GDC_border & GDC_BORDER_Y2) ) gdImageLine( im, PX(num_points-1+(do_bar?2:0)), PY(lowest), PX(num_points-1+(do_bar?2:0)), PY(highest), LineColor ); } setno = 0; } if( GDC_0Shelf && threeD && /* front of 0 shelf */ ( (lowest < 0.0 && highest > 0.0) || ( (lowest == 0.0 || highest == 0.0) && !(GDC_border&GDC_BORDER_X) ) ) ) { int x2 = PX( num_points-1+(do_bar?2:0) ), y2 = PY( 0 ); gdImageLine( im, PX(0), PY(0), x2, y2, LineColor ); // front line setno = set_depth; // depth for 3Ds gdImageLine( im, x2, y2, PX(num_points-1+(do_bar?2:0)), PY(0), LineColor ); setno = 0; // set back to foremost } if( GDC_annotation ) /* front half of annotation line */ { int x1 = PX(GDC_annotation->point+(do_bar?1:0)), y1 = PY(highest); int x2; // front line gdImageLine( im, x1, PY(lowest)+1, x1, y1, AnnoteColor ); if( threeD ) { // on back plane setno = set_depth; x2 = PX(GDC_annotation->point+(do_bar?1:0)); // prspective line gdImageLine( im, x1, y1, x2, PY(highest), AnnoteColor ); } else // for 3D done with back line { x2 = PX(GDC_annotation->point+(do_bar?1:0)); gdImageLine( im, x1, y1, x1, y1-2, AnnoteColor ); } /* line-to and note */ if( *(GDC_annotation->note) ) // any note? { if( GDC_annotation->point >= (num_points/2) ) /* note to the left */ { gdImageLine( im, x2, PY(highest)-2, x2-annote_hgt/2, PY(highest)-2-annote_hgt/2, AnnoteColor ); GDCImageStringNL( im, &GDC_fontc[GDC_annotation_font], x2-annote_hgt/2-1-annote_len - 1, PY(highest)-annote_hgt+1, GDC_annotation->note, AnnoteColor, GDC_JUSTIFY_RIGHT ); } else /* note to right */ { gdImageLine( im, x2, PY(highest)-2, x2+annote_hgt/2, PY(highest)-2-annote_hgt/2, AnnoteColor ); GDCImageStringNL( im, &GDC_fontc[GDC_annotation_font], x2+annote_hgt/2+1 + 1, PY(highest)-annote_hgt+1, GDC_annotation->note, AnnoteColor, GDC_JUSTIFY_LEFT ); } } setno = 0; } /* usually GDC_generate_img is used in conjunction with hard or hold options */ if( GDC_generate_img ) { fflush(img_fptr); // clear anything buffered 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); } } if( bg_img ) gdImageDestroy(bg_img); if( GDC_hold_img & GDC_EXPOSE_IMAGE ) GDC_image = (void*)im; else gdImageDestroy(im); return 0; }