/* 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;
}