/* correlatePlot - produce correlation plot graphs */ #include "common.h" #include "memgfx.h" #include "vGfx.h" #include "linefile.h" #include "hash.h" #include "portable.h" #include "cheapcgi.h" #include "cart.h" #include "jksql.h" #include "trackDb.h" #include "obscure.h" #include "hgTables.h" #include "correlate.h" #include "histogram.h" #include "trashDir.h" #define CLIP(p,limit) if (p < 0) p = 0; if (p >= (limit)) p = (limit)-1; static void verticalTextCentered(struct vGfx *vg, int x, int y, int width, int height, int colorIx, MgFont *font, char *string) /* Draw a vertical line of text in middle of the box. * The string will read from bottom to top */ { /* don't let this run wild */ CLIP(width, vg->width); CLIP(height, vg->height); if ((width > 0) && (height > 0)) { struct vGfx *vgHoriz; int i, j; /* reversed meanings of width and height here since this is going * to rotate 90 degrees struct memGfx *mgHoriz; mgHoriz = mgNew(height, width); mgTextCentered(mgHoriz, 0, 0, height, width, colorIx, font, string); */ vgHoriz = vgOpenPng(height, width, "/dev/null", FALSE); vgTextCentered(vgHoriz, 0, 0, height, width, colorIx, font, string); /* now, blit from the horizontal to the vertical, rotate -90 (CCW) */ for (i = 0; i < height; ++i) /* xSrc -> yDest */ { int yDest = height - i; for (j = 0; j < width; ++j) /* ySrc -> xDest */ { vgDot(vg,j+x,yDest+y, vgGetDot(vgHoriz, i, j)); /*vgDot(vg,j+x,yDest+y, ((int)mgGetDot(mgHoriz,i,j)));*/ } } vgClose(&vgHoriz); /*mgFree(&mgHoriz);*/ } } #ifdef NOT_NEEDED and should be corrected if it is needed ... static void verticalText(struct vGfx *vg, int x, int y, int colorIx, MgFont *font, char *string) /* Draw a vertical line of text with upper left corner x,y. * The string ends at that upper coordinate y, thus the * string reads from bottom up. */ { int textWidth = mgFontStringWidth(font, string); int fontHeight = mgFontLineHeight(font); int xClip = textWidth; int yClip = fontHeight; struct vGfx *vgHoriz; CLIP(xClip,vg->width); CLIP(yClip,vg->height); /* can't be larger than these either after rotating 90 degrees */ CLIP(xClip, vg->height); CLIP(yClip, vg->width); if ((yClip > 0) && (xClip > 0)) { int i, j; int xx, yy; int dot; vgHoriz = vgOpenGif(xClip, yClip, "/dev/null", FALSE); vgText(vgHoriz, 0, 0, colorIx, font, string); for (i = 0; i < xClip; ++i) { yy = xClip - i; for (j = 0; j < yClip; ++j) { xx = j; dot = vgGetDot(vgHoriz, i, j); vgDot(vg,xx+x,yy+y, dot); } } vgClose(&vgHoriz); } } #endif static void addLabels(struct vGfx *vg, MgFont *font, char *minXStr, char *maxXStr, char *minYStr, char *maxYStr, char *shortLabel, char *longLabel, int totalWidth, int totalHeight, int leftMargin, int fontHeight, char *vertLabel) { int x1 = 0; int y1 = 0; int x2 = 0; int y2 = 0; int shortLabelSize = 0; int longLabelSize = 0; int textWidth = 0; /* border around the whole thing */ vgLine(vg, 0, 0, totalWidth-1, 0, MG_MAGENTA); /* top */ vgLine(vg, 0, 0, 0, totalHeight-1, MG_MAGENTA); /* left */ vgLine(vg, totalWidth-1, 0, totalWidth-1, totalHeight-1, MG_MAGENTA); /* right */ vgLine(vg, 0, totalHeight-1, totalWidth-1, totalHeight-1, MG_MAGENTA);/* bottom */ /* border just around the graph */ vgLine(vg, leftMargin-PLOT_MARGIN, PLOT_MARGIN, leftMargin-PLOT_MARGIN, PLOT_MARGIN+GRAPH_HEIGHT, MG_CYAN); /* left */ #ifdef NOT vgLine(vg, leftMargin, PLOT_MARGIN, totalWidth-PLOT_MARGIN, PLOT_MARGIN, MG_BLACK); /* top */ vgLine(vg, totalWidth-PLOT_MARGIN, PLOT_MARGIN, /* right */ totalWidth-PLOT_MARGIN, PLOT_MARGIN+GRAPH_HEIGHT, MG_BLACK); #endif vgLine(vg, leftMargin, PLOT_MARGIN+GRAPH_HEIGHT+1, totalWidth-PLOT_MARGIN, PLOT_MARGIN+GRAPH_HEIGHT+1, MG_CYAN); /* bottom */ x1 = leftMargin; y1 = totalHeight-PLOT_MARGIN-fontHeight; x2 = totalWidth-PLOT_MARGIN; y2 = totalHeight-PLOT_MARGIN; if (shortLabel) shortLabelSize = mgFontStringWidth(font, shortLabel); if (longLabel) longLabelSize = mgFontStringWidth(font, longLabel); if ((longLabel != NULL) && (longLabelSize < (x2-x1))) vgTextCentered(vg, x1, y1, x2-x1, y2-y1, MG_BLACK, font, longLabel); else if (shortLabel != NULL) vgTextCentered(vg, x1, y1, x2-x1, y2-y1, MG_BLACK, font, shortLabel); x1 = leftMargin; y1 = totalHeight-PLOT_MARGIN-fontHeight-fontHeight+2; vgText(vg, x1, y1, MG_BLACK, font, minXStr); textWidth = mgFontStringWidth(font, maxXStr); x1 = totalWidth - PLOT_MARGIN - textWidth; vgText(vg, x1, y1, MG_BLACK, font, maxXStr); x1 = 0; x2 = leftMargin - PLOT_MARGIN; y1 = PLOT_MARGIN; y2 = PLOT_MARGIN + fontHeight; if (maxYStr) vgTextRight(vg, x1, y1, x2-x1, y2-y1, MG_BLACK, font, maxYStr); y1 = PLOT_MARGIN+GRAPH_HEIGHT-fontHeight; y2 = PLOT_MARGIN+GRAPH_HEIGHT; if (minYStr) vgTextRight(vg, x1, y1, x2-x1, y2-y1, MG_BLACK, font, minYStr); x1 = PLOT_MARGIN; y1 = PLOT_MARGIN + fontHeight; x2 = leftMargin - PLOT_MARGIN - PLOT_MARGIN; y2 = GRAPH_HEIGHT - fontHeight; if (vertLabel) verticalTextCentered(vg, x1, y1, x2-x1, y2-y1, MG_BLACK, font, vertLabel); } static void ordinaryPlot(int **densityCounts, struct vGfx *vg, int totalHeight, int totalWidth, int leftMargin) /* a simple point plot, not density, the inversion of the Y axis takes * place here. leftMargin is what has been passed in, the top margin is * fixed at PLOT_MARGIN */ { int i, j; /* the dots are going to overlap because of DOT_SIZE, but that is * OK for this situation, they are all MG_BLACK */ for (j = 0; j < GRAPH_HEIGHT; ++j) for (i = 0; i < GRAPH_WIDTH; ++i) if (densityCounts[j][i]) { vgBox(vg, i+leftMargin, PLOT_MARGIN+(GRAPH_HEIGHT-j), DOT_SIZE, DOT_SIZE, MG_BLACK); } /* the (totalHeight-j) is the inversion of the Y axis */ } static void densityPlot(int **densityCounts, struct vGfx *vg, int totalHeight, int totalWidth, int leftMargin) /* density plot shading, the inversion of the Y axis takes place here. * leftMargin is what has been passed in, the top margin is * fixed at PLOT_MARGIN */ { /* shade code borrowed from hgTracks */ int maxShade = 9; Color shadesOfGray[10+1]; double logMin = 0.0; double logMax = 0.0; double logRange = 0.0; double log_2 = log(2.0); int i, j; int densityMin = BIGNUM; int densityMax = 0; int densityRange = 0; #define LOG2(x) (log(x)/log_2) /* the painted dots are twice as big as the data, so lower the * resolution of this data by adding together adjacent counts */ for (j = 0; j < GRAPH_HEIGHT-1; j += 2) for (i = 0; i < GRAPH_WIDTH-1; i += 2) { int count = densityCounts[j][i]; count += densityCounts[j+1][i]; count += densityCounts[j][i+1]; count += densityCounts[j+1][i+1]; densityCounts[j][i] = count; densityCounts[j+1][i] = count; densityCounts[j][i+1] = count; densityCounts[j+1][i+1] = count; if (count > 0) { densityMin = min(densityMin,count); densityMax = max(densityMax,count); } } //hPrintf("

residuals min,max: %d, %d

\n", densityMin, densityMax); logMin = LOG2((double)densityMin); logMax = LOG2((double)densityMax); logRange = logMax - logMin; //hPrintf("

residuals log2(min,max): %g, %g

\n", logMin, logMax); /* shades of gray borrowed from hgTracks.c */ for (i=0; i<=maxShade; ++i) { struct rgbColor rgb; int level = 255 - (255*i/maxShade); if (level < 0) level = 0; rgb.r = rgb.g = rgb.b = level; shadesOfGray[i] = vgFindColorIx(vg, rgb.r, rgb.g, rgb.b); } shadesOfGray[maxShade+1] = MG_RED; densityRange = densityMax - densityMin; /* only need to draw every other one since they were reduced by two above */ for (j = 0; j < GRAPH_HEIGHT; ++j) for (i = 0; i < GRAPH_WIDTH; ++i) if (densityCounts[j][i]) { Color color; int level = ((LOG2((double)densityCounts[j][i]) - logMin)*maxShade) / logRange; if (level <= 0) level = 1; if (level > maxShade) level = maxShade; color = shadesOfGray[level]; vgBox(vg, i+leftMargin, PLOT_MARGIN+(GRAPH_HEIGHT-j), DOT_SIZE, DOT_SIZE, color); } /* the (GRAPH_HEIGHT-j) is the inversion of the Y axis */ } static MgFont *fontSetup(int *height) /* Select font from textSize in cart. */ { #define textSizeVar "textSize" /* from hgTracks.h */ char *textSize = cartUsualString(cart, textSizeVar, "small"); MgFont *font = mgFontForSize(textSize); if (height) *height = mgFontLineHeight(font); return (font); } struct tempName *scatterPlot(struct trackTable *yTable, struct trackTable *xTable, struct dataVector *result, int *width, int *height) /* create scatter plot gif file in trash, return path name */ { static struct tempName gifFileName; MgFont *font = NULL; struct dataVector *y = yTable->vSet; struct dataVector *x = xTable->vSet; double yMin = INFINITY; double xMin = INFINITY; double yMax = -INFINITY; double xMax = -INFINITY; double yRange = 0.0; double xRange = 0.0; struct lm *lm = lmInit(GRAPH_WIDTH); int **densityCounts; /* densityCounts[GRAPH_HEIGHT] [GRAPH_WIDTH] */ int i, j; struct vGfx *vg; int pointsPlotted = 0; int fontHeight = 0; char minXStr[32]; char maxXStr[32]; char minYStr[32]; char maxYStr[32]; char bottomLabel[256]; int leftLabelSize = 0; int bottomLabelSize = 0; int leftMargin = 0; int bottomMargin = 0; int totalWidth = 0; int totalHeight = 0; font = fontSetup(&fontHeight); /* Initialize density plot count array */ /* space for the row pointers, first */ lmAllocArray(lm, densityCounts, GRAPH_HEIGHT); /* then space for each row */ for (j = 0; j < GRAPH_HEIGHT; ++j) lmAllocArray(lm, densityCounts[j], GRAPH_WIDTH); for (j = 0; j < GRAPH_HEIGHT; ++j) for (i = 0; i < GRAPH_WIDTH; ++i) densityCounts[j][i] = 0; /* find overall min and max */ for ( ; (y != NULL) && (x !=NULL); y = y->next, x=x->next) { yMin = min(yMin,y->min); xMin = min(xMin,x->min); yMax = max(yMax,y->max); xMax = max(xMax,x->max); } y = yTable->vSet; x = xTable->vSet; yRange = yMax - yMin; xRange = xMax - xMin; if (xRange == 0.0) xRange = 1.0; if (yRange == 0.0) yRange = 1.0; safef(minXStr,ArraySize(minXStr), "%.4g", xMin); safef(maxXStr,ArraySize(maxXStr), "%.4g", xMax); safef(minYStr,ArraySize(minYStr), "%.4g", yMin); safef(maxYStr,ArraySize(maxYStr), "%.4g", yMax); /* the axes labels will be horizontal, thus the widest establishes * the left margin. */ leftLabelSize = max(mgFontStringWidth(font, minYStr), mgFontStringWidth(font, maxYStr)); /* the vertical text may be even wider than the numbers */ leftLabelSize = max(leftLabelSize,fontHeight); /* The bottom label is one line for the axes labels, and a second * line for the X axis table longLabel */ bottomLabelSize = fontHeight * 2; /* y data is the Y axis, x data is the X axis */ /* Do not worry about the fact that the Y axis is 0 at the top * here, that will get translated during the actual plot. */ for ( ; (y != NULL) && (x !=NULL); y = y->next, x=x->next) { int i; pointsPlotted += y->count; for( i = 0; i < y->count; ++i) { float yValue = y->value[i]; float xValue = x->value[i]; int x1 = ((xValue - xMin)/xRange) * GRAPH_WIDTH; int y1 = ((yValue - yMin)/yRange) * GRAPH_HEIGHT; CLIP(x1,GRAPH_WIDTH); CLIP(y1,GRAPH_HEIGHT); densityCounts[y1][x1]++; } } leftMargin = PLOT_MARGIN + leftLabelSize + PLOT_MARGIN + PLOT_MARGIN; bottomMargin = PLOT_MARGIN + bottomLabelSize + PLOT_MARGIN; totalWidth = leftMargin + GRAPH_WIDTH + PLOT_MARGIN; totalHeight = PLOT_MARGIN + GRAPH_HEIGHT + bottomMargin; trashDirFile(&gifFileName, "hgtData", "hgtaScatter", ".gif"); vg = vgOpenPng(totalWidth, totalHeight, gifFileName.forCgi, FALSE); /* x,y, w,h, drawing area only */ vgSetClip(vg, leftMargin, PLOT_MARGIN, GRAPH_WIDTH, GRAPH_HEIGHT); /* more than 100,000 points, show them as density */ if (pointsPlotted > 100000) densityPlot(densityCounts, vg, totalWidth, totalHeight, leftMargin); else ordinaryPlot(densityCounts, vg, totalWidth, totalHeight, leftMargin); /* draw regression line */ { int x1 = leftMargin + (((xMin - xMin)/xRange) * GRAPH_WIDTH); int x2 = leftMargin + (((xMax - xMin)/xRange) * GRAPH_WIDTH); int y1 = 0; int y2 = 0; double y; y = (result->m * xMin) + result->b; y1 = PLOT_MARGIN + (GRAPH_HEIGHT - (((y - yMin)/yRange) * GRAPH_HEIGHT)); y = (result->m * xMax) + result->b; y2 = PLOT_MARGIN + (GRAPH_HEIGHT - (((y - yMin)/yRange) * GRAPH_HEIGHT)); if ((x1 != x2) || (y1 != y2)) vgLine(vg, x1, y1, x2, y2, MG_RED); } safef(bottomLabel,ArraySize(bottomLabel),"%s", xTable->shortLabel); /* allow the entire area to be drawn into for the labels */ vgSetClip(vg, 0, 0, totalWidth, totalHeight); addLabels(vg, font, minXStr, maxXStr, minYStr, maxYStr, xTable->shortLabel, xTable->longLabel, totalWidth, totalHeight, leftMargin, fontHeight, yTable->shortLabel); vgUnclip(vg); vgClose(&vg); lmCleanup(&lm); if (width) *width = totalWidth; if (height) *height = totalHeight; return &gifFileName; } struct tempName *residualPlot(struct trackTable *yTable, struct trackTable *xTable, struct dataVector *result, double *F_statistic, double *fitMin, double *fitMax, int *width, int *height) /* create residual plot gif file in trash, return path name */ { static struct tempName gifFileName; struct dataVector *y = yTable->vSet; struct dataVector *x = xTable->vSet; double fittedMin = INFINITY; double fittedMax = -INFINITY; double yMin = INFINITY; double xMin = INFINITY; double yMax = -INFINITY; double xMax = -INFINITY; double residualMin = INFINITY; double residualMax = -INFINITY; double fittedRange = 0.0; double residualRange = 0.0; register double m = result->m; /* slope m */ register double b = result->b; /* intercept m */ struct vGfx *vg; int resultIndex = 0; /* for reference within result */ boolean debugOn = FALSE; double F = 0.0; double MSR = 0.0; /* aka MSM - mean square residual */ double SSE = 0.0; /* mean squared error */ double ySum = 0.0; double yBar = 0.0; struct lm *lm = lmInit(GRAPH_WIDTH); int **densityCounts; /* densityCounts[GRAPH_HEIGHT] [GRAPH_WIDTH] */ int i, j; int pointsPlotted = 0; MgFont *font = NULL; int fontHeight = 0; char minXStr[32]; char maxXStr[32]; char minYStr[32]; char maxYStr[32]; int leftLabelSize = 0; int bottomLabelSize = 0; int leftMargin = 0; int bottomMargin = 0; int totalWidth = 0; int totalHeight = 0; font = fontSetup(&fontHeight); if (result->count < 1300) debugOn = TRUE; debugOn = FALSE; if(debugOn) { hPrintf("
\n");
    hPrintf("#position, x, y, fitted, residual\n");
    }

/*	Initialize density plot count array	*/
/*	space for the row pointers, first	*/
lmAllocArray(lm, densityCounts, GRAPH_HEIGHT);
/*	then space for each row	*/
for (j = 0; j < GRAPH_HEIGHT; ++j)
    lmAllocArray(lm, densityCounts[j], GRAPH_WIDTH);
for (j = 0; j < GRAPH_HEIGHT; ++j)
    for (i = 0; i < GRAPH_WIDTH; ++i)
	densityCounts[j][i] = 0;

/*	find overall min and max for the "fitted" values	*/
for (y = yTable->vSet, x = xTable->vSet ;
	(y != NULL) && (x !=NULL); y = y->next, x=x->next)
    {
    yMin = min(yMin,y->min);
    xMin = min(xMin,x->min);
    yMax = max(yMax,y->max);
    xMax = max(xMax,x->max);
    }

fittedMin = (m * xMin) + b;
fittedMax = (m * xMax) + b;
residualMin = result->min;
residualMax = result->max;
fittedRange = fittedMax - fittedMin;
residualRange = residualMax - residualMin;

safef(minXStr,ArraySize(minXStr), "%.4g", fittedMin);
safef(maxXStr,ArraySize(maxXStr), "%.4g", fittedMax);
safef(minYStr,ArraySize(minYStr), "%.4g", residualMin);
safef(maxYStr,ArraySize(maxYStr), "%.4g", residualMax);
/*	the axes labels will be horizontal, thus the widest establishes
 *	the left margin.
 */
leftLabelSize = max(mgFontStringWidth(font, minYStr),
    mgFontStringWidth(font, maxYStr));
/*	the vertical text may be even wider than the numbers	*/
leftLabelSize = max(leftLabelSize,fontHeight);
/*	The bottom label is one line for the axes labels, and a second
 *	line for the X axis table longLabel
 */
bottomLabelSize = fontHeight * 2;

for (y = yTable->vSet; y != NULL; y = y->next)
    {
    ySum += y->sumData;
    }

yBar = ySum / result->count;

/*	Do not worry about the fact that the Y axis is 0 at the top
 *	here, that will get translated during the actual plot.
 */
for (y = yTable->vSet, x = xTable->vSet ; (y != NULL) && (x !=NULL);
	y=y->next, x=x->next)
    {
    int i;

    pointsPlotted += y->count;

    for( i = 0; i < x->count; ++i, ++resultIndex)
	{
	float residual = result->value[resultIndex];
	float xValue = x->value[i];
	float fitted = (m * xValue) + b;
	int x1 = ((fitted - fittedMin)/fittedRange) * GRAPH_WIDTH;
	int y1;
	if (residualRange != 0)
	    y1 = ((residual - residualMin)/residualRange) * GRAPH_HEIGHT;
	else
	    y1 = 0;

if(debugOn)
    hPrintf("%d\t%g\t%g\t%g\t%g\n", x->position[i], x->value[i], y->value[i], fitted, residual);

	MSR += (fitted - yBar) * (fitted - yBar);
	SSE += (y->value[i] - fitted) * (y->value[i] - fitted);

	CLIP(x1,GRAPH_WIDTH);
	CLIP(y1,GRAPH_HEIGHT);

	densityCounts[y1][x1]++;
	}
    }

leftMargin = PLOT_MARGIN + leftLabelSize + PLOT_MARGIN + PLOT_MARGIN;
bottomMargin = PLOT_MARGIN + bottomLabelSize + PLOT_MARGIN;
totalWidth = leftMargin + GRAPH_WIDTH + PLOT_MARGIN;
totalHeight = PLOT_MARGIN + GRAPH_HEIGHT + bottomMargin;

trashDirFile(&gifFileName, "hgtData", "hgtaResidual", ".gif");

vg = vgOpenPng(totalWidth, totalHeight, gifFileName.forCgi, FALSE);

/*	x,y, w,h, drawing area only	*/
vgSetClip(vg, leftMargin, PLOT_MARGIN, GRAPH_WIDTH, GRAPH_HEIGHT);

/*	more than 100,000 points, show them as density	*/
if (pointsPlotted > 100000)
    densityPlot(densityCounts, vg, totalWidth, totalHeight, leftMargin);
else
    ordinaryPlot(densityCounts, vg, totalWidth, totalHeight, leftMargin);


/*	draw y = 0.0 line	*/
    {
    int x1 = leftMargin;
    int x2 = leftMargin + GRAPH_WIDTH - PLOT_MARGIN;
    int y1, y2;
    if (residualRange != 0)
	y1 = PLOT_MARGIN + (GRAPH_HEIGHT -
                    (((0.0 - residualMin)/residualRange) * GRAPH_HEIGHT));
    else
	y1 = 0;
    y2 = y1;
    if ((x1 != x2) || (y1 != y2))
	vgLine(vg, x1, y1, x2, y2, MG_RED);
    }

/*	allow the entire area to be drawn into for the labels	*/
vgSetClip(vg, 0, 0, totalWidth, totalHeight);

addLabels(vg, font, minXStr, maxXStr, minYStr, maxYStr,
    "Fitted", NULL, totalWidth, totalHeight, leftMargin, fontHeight,
	"Residuals");

vgUnclip(vg);
vgClose(&vg);

if(debugOn)
    hPrintf("
\n"); if (F_statistic) { if ((result->count - 2) > 0) F = MSR / (SSE / (result->count - 2)); else F = 0.0; *F_statistic = F; } lmCleanup(&lm); if (fitMin) *fitMin = fittedMin; if (fitMax) *fitMax = fittedMax; if (width) *width = totalWidth; if (height) *height = totalHeight; return &gifFileName; } struct tempName *histogramPlot(struct trackTable *table, int *width, int *height) /* create histogram plot gif file in trash, return path name */ { struct tempName *histoFileName; struct vGfx *vg; MgFont *font = NULL; int totalWidth = 400; int totalHeight = 300; int fontHeight = 0; int leftLabelSize = 0; int leftMargin = 0; int bottomMargin = 0; int bottomLabelSize = 0; char bottomLabelStr[128]; char minXStr[32]; char maxXStr[32]; char minYStr[32]; char maxYStr[32]; struct histoResult *histo = NULL; struct dataVector *dv; int totalCounts = 0; unsigned maxBinCount = 0; unsigned minBinCount = BIGNUM; int binCountRange = 0; int i; for (dv = table->vSet; dv != NULL; dv = dv->next) { histo = histoGram(dv->value, (size_t) dv->count, NAN, (unsigned) GRAPH_WIDTH, NAN, table->min, table->max, histo); totalCounts += dv->count; } if (histo == NULL) return NULL; for (i = 0; i < histo->binCount; ++i) { maxBinCount = max(maxBinCount,histo->binCounts[i]); minBinCount = min(minBinCount,histo->binCounts[i]); } binCountRange = maxBinCount - minBinCount; if (histo->binCount != GRAPH_WIDTH) errAbort("histogram is not correctly sized ? %d vs %d", histo->binCount, GRAPH_WIDTH); /* struct histoResult *histoGram(float *values, size_t N, float binSize, unsigned binCount, float minValue, float min, float max, struct histoResult *accumHisto); */ AllocVar(histoFileName); trashDirFile(histoFileName, "hgtData", "hgtaHisto", ".gif"); safef(bottomLabelStr,ArraySize(bottomLabelStr), "# of data values: %d", totalCounts); safef(minYStr,ArraySize(minYStr), "%u", minBinCount); safef(maxYStr,ArraySize(maxYStr), "%u", maxBinCount); safef(minXStr,ArraySize(minXStr), "%.4g", table->min); safef(maxXStr,ArraySize(maxXStr), "%.4g", table->max); font = fontSetup(&fontHeight); fontHeight = mgFontLineHeight(font); /* the vertical text size depends on the labels */ leftLabelSize = max(mgFontStringWidth(font, minYStr), mgFontStringWidth(font, maxYStr)); /* or perhaps the fontHeight */ leftLabelSize = max(leftLabelSize,fontHeight); /* the horizontal text is two label height */ bottomLabelSize = fontHeight * 2; leftMargin = PLOT_MARGIN + leftLabelSize + PLOT_MARGIN + PLOT_MARGIN; bottomMargin = PLOT_MARGIN + bottomLabelSize + PLOT_MARGIN; totalWidth = leftMargin + GRAPH_WIDTH + PLOT_MARGIN; totalHeight = PLOT_MARGIN + GRAPH_HEIGHT + bottomMargin; vg = vgOpenPng(totalWidth, totalHeight, histoFileName->forCgi, FALSE); /* x,y, w,h, drawing area only */ vgSetClip(vg, leftMargin, PLOT_MARGIN, GRAPH_WIDTH, GRAPH_HEIGHT); if (binCountRange > 0) { int y1 = PLOT_MARGIN + GRAPH_HEIGHT; for (i = 0; i < histo->binCount; ++i) { int x1 = leftMargin + i; int y2 = PLOT_MARGIN + GRAPH_HEIGHT - (GRAPH_HEIGHT * ((double)(histo->binCounts[i] - minBinCount) / (double)binCountRange)); if (y1 != y2) vgLine(vg, x1, y1, x1, y2, MG_BLACK); /* top */ } } /* allow the entire area to be drawn into for the labels */ vgSetClip(vg, 0, 0, totalWidth, totalHeight); addLabels(vg, font, minXStr, maxXStr, minYStr, maxYStr, bottomLabelStr, NULL, totalWidth, totalHeight, leftMargin, fontHeight, "counts in bin"); vgUnclip(vg); vgClose(&vg); freeHistoGram(&histo); if (width) *width = totalWidth; if (height) *height = totalHeight; return histoFileName; }