3b51940eefa1013e69a0d3bbd18855aeec472855 kent Sat Jan 23 17:43:48 2021 -0800 Adding mapBoxes and a little spacing between bars once zoomed in enough. Removed a bunch of unneeded casts. diff --git src/hg/hgTracks/barChartTrack.c src/hg/hgTracks/barChartTrack.c index f0a425d..7b3e8f4 100644 --- src/hg/hgTracks/barChartTrack.c +++ src/hg/hgTracks/barChartTrack.c @@ -11,30 +11,31 @@ #include "hubConnect.h" #include "barChartBed.h" #include "barChartCategory.h" #include "barChartUi.h" // If a category contributes more than this percentage, its color is displayed in squish mode // Could be a trackDb setting #define SPECIFICITY_THRESHOLD 10 struct barChartTrack /* Track extras */ { boolean noWhiteout; /* Suppress whiteout of graph background (allow highlight, blue lines) */ double maxMedian; /* Maximum median across all categories */ boolean doLogTransform; /* Log10(x+1) */ + boolean fitToGene; /* Fit our width to gene */ char *unit; /* Units for category values (e.g. RPKM) */ struct barChartCategory *categories; /* Category names, colors, etc. */ int categCount; /* Count of categories - derived from above */ char **categNames; /* Category names - derived from above */ char **categLabels; /* Category labels - derived from above */ struct rgbColor *colors; /* Colors for all categories */ struct hash *categoryFilter; /* NULL out excluded factors */ int maxViewLimit; // dimensions for drawing char *maxGraphSize; /* optionally limit graph size (override semantic zoom) small, medium, or large (default) */ int winMaxGraph; /* Draw large graphs if window size smaller than this */ int winMedGraph; /* Draw medium graphs if window size greater than this and smaller than winMaxGraph */ @@ -196,31 +197,31 @@ maxScore = max(maxScore, expScore); maxNum = i; } totalScore += expScore; } // threshold to consider this item category specific -- a category contributes > threshold % to // total level if (totalScore < 1 || maxScore <= (totalScore * threshold * .01)) return -1; return maxNum; } static Color barChartItemColor(struct track *tg, void *item, struct hvGfx *hvg) /* A bit of category-specific coloring in squish mode only, on bed item */ { -struct bed *bed = (struct bed *)item; +struct bed *bed = item; int id = maxCategoryForItem(bed, SPECIFICITY_THRESHOLD); if (id < 0) return MG_BLACK; struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; struct rgbColor color = extras->colors[id]; return hvGfxFindColorIx(hvg, color.r, color.g, color.b); } // Convenience functions for draw static int valToHeight(double val, double maxVal, int maxHeight, boolean doLogTransform) /* Log-scale and convert a value from 0 to maxVal to 0 to maxHeight-1 */ { if (val == 0.0) return 0; @@ -240,31 +241,31 @@ { double useVal = val; double useMax = maxVal; if (!doLogTransform) { useMax = maxView; if (val > maxView) useVal = maxView; } return valToHeight(useVal, useMax, maxHeight, doLogTransform); } static int chartHeight(struct track *tg, struct barChartItem *itemInfo) /* Determine height in pixels of graph. This will be the box for category with highest value */ { -struct bed *bed = (struct bed *)itemInfo->bed; +struct bed *bed = itemInfo->bed; struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; int i; double maxExp = 0.0; int expCount = bed->expCount; double expScore; for (i=0; iexpScores[i]; maxExp = max(maxExp, expScore); } return valToClippedHeight(maxExp, extras->maxMedian, extras->maxViewLimit, extras->maxHeight, extras->doLogTransform); } @@ -327,30 +328,31 @@ struct barChartTrack *extras; if (!tg->extraUiData) { AllocVar(extras); tg->extraUiData = extras; } extras = (struct barChartTrack *)tg->extraUiData; extras->colors = getCategoryColors(tg); struct trackDb *tdb = tg->tdb; extras->doLogTransform = barChartIsLogTransformed(cart, tdb->track, tdb); extras->maxMedian = barChartUiMaxMedianScore(tdb); extras->noWhiteout = cartUsualBooleanClosestToHome(cart, tdb, FALSE, BAR_CHART_NO_WHITEOUT, BAR_CHART_NO_WHITEOUT_DEFAULT); +extras->fitToGene = trackDbSettingClosestToHomeOn(tdb, BAR_CHART_FIT_TO_GENE); extras->maxViewLimit = barChartCurViewMax(cart, tg->track, tg->tdb); extras->maxGraphSize = trackDbSettingClosestToHomeOrDefault(tdb, BAR_CHART_MAX_GRAPH_SIZE, BAR_CHART_MAX_GRAPH_SIZE_DEFAULT); extras->unit = trackDbSettingClosestToHomeOrDefault(tdb, BAR_CHART_UNIT, ""); /* Set barchart dimensions to draw. For three window sizes */ #define MAX_BAR_CHART_MODEL_HEIGHT 2 #define MED_BAR_CHART_MODEL_HEIGHT 2 #define MIN_BAR_CHART_MODEL_HEIGHT 1 #define WIN_MAX_GRAPH_DEFAULT 50000 #define MAX_GRAPH_HEIGHT 175 #define MAX_BAR_WIDTH 5 #define MAX_GRAPH_PADDING 2 @@ -456,33 +458,41 @@ slAddHead(&list, itemInfo); bed = bed->next; itemInfo->bed->next = NULL; itemInfo->height = barChartItemHeight(tg, itemInfo); } slReverse(&list); tg->items = list; } /***********************************************/ /* Draw */ static int chartWidth(struct track *tg, struct barChartItem *itemInfo) { struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; +if (extras->fitToGene) + { + struct bed *bed = itemInfo->bed; + return scaleForWindow(bed->chromEnd - bed->chromStart, winStart, winEnd); + } +else + { int count = filteredCategoryCount(extras); return (extras->barWidth * count) + (extras->padding * (count-1)) + 2; } +} static int barChartX(struct bed *bed) /* Locate chart on X, relative to viewport. */ { int start = max(bed->chromStart, winStart); double scale = scaleForWindow(insideWidth, winStart, winEnd); int x1 = round((start - winStart) * scale); return x1; } static void drawGraphBox(struct track *tg, struct barChartItem *itemInfo, struct hvGfx *hvg, int x, int y) /* Draw white background for graph */ { Color lighterGray = MAKECOLOR_32(0xF3, 0xF3, 0xF3); @@ -493,31 +503,31 @@ static void drawGraphBase(struct track *tg, struct barChartItem *itemInfo, struct hvGfx *hvg, int x, int y) /* Draw faint line under graph to delineate extent when bars are missing (category w/ 0 value) */ { Color lightGray = MAKECOLOR_32(0xD1, 0xD1, 0xD1); int graphWidth = chartWidth(tg, itemInfo); hvGfxBox(hvg, x, y, graphWidth, 1, lightGray); } static void barChartDrawAt(struct track *tg, void *item, struct hvGfx *hvg, int xOff, int y, double scale, MgFont *font, Color color, enum trackVisibility vis) /* Draw bar chart over item */ { struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; struct barChartItem *itemInfo = (struct barChartItem *)item; -struct bed *bed = (struct bed *)itemInfo->bed; +struct bed *bed = itemInfo->bed; if (vis == tvDense) { bedDrawSimpleAt(tg, bed, hvg, xOff, y, scale, font, MG_WHITE, vis); // color ignored (using grayscale) return; } if (vis == tvSquish) { Color color = barChartItemColor(tg, bed, hvg); int height = extras->squishHeight; drawScaledBox(hvg, bed->chromStart, bed->chromEnd, scale, xOff, y, height, color); return; } int graphX = barChartX(bed); if (graphX < 0) @@ -539,31 +549,31 @@ static int barChartNonPropPixelWidth(struct track *tg, void *item) /* Return end chromosome coordinate of item, including graph */ { struct barChartItem *itemInfo = (struct barChartItem *)item; int graphWidth = chartWidth(tg, itemInfo); return graphWidth; } static void barChartNonPropDrawAt(struct track *tg, void *item, struct hvGfx *hvg, int xOff, int y, double scale, MgFont *font, Color color, enum trackVisibility vis) { if (vis != tvFull && vis != tvPack) return; struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; struct barChartItem *itemInfo = (struct barChartItem *)item; -struct bed *bed = (struct bed *)itemInfo->bed; +struct bed *bed = itemInfo->bed; int topGraphHeight = chartHeight(tg, itemInfo); topGraphHeight = max(topGraphHeight, tl.fontHeight); int yZero = topGraphHeight + y - 1; // yZero is bottom of graph int graphX = barChartX(bed); int x1 = xOff + graphX; // x1 is at left of graph int keepX = x1; drawGraphBase(tg, itemInfo, hvg, keepX, yZero+1); if (!extras->noWhiteout) drawGraphBox(tg, itemInfo, hvg, keepX, yZero+1); struct rgbColor lineColor = {.r=0}; int lineColorIx = hvGfxFindColorIx(hvg, lineColor.r, lineColor.g, lineColor.b); int barWidth = extras->barWidth; @@ -589,77 +599,78 @@ else hvGfxOutlinedBox(hvg, x1, yZero-height+1, barWidth, height, fillColorIx, lineColorIx); // mark clipped bar with magenta tip if (!extras->doLogTransform && expScore > extras->maxViewLimit) hvGfxBox(hvg, x1, yZero-height+1, barWidth, 2, clipColor); x1 = x1 + barWidth + extras->padding; } } static char *chartMapText(struct track *tg, struct barChartCategory *categ, double expScore) /* Construct mouseover text for a chart bar */ { static char buf[128]; struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; safef(buf, sizeof(buf), "%s (%.1f %s)", categ->label, expScore, extras->unit); +subChar(buf, '_', ' '); return buf; } static int barChartItemStart(struct track *tg, void *item) /* Return end chromosome coordinate of item, including graph */ { struct barChartItem *itemInfo = (struct barChartItem *)item; -struct bed *bed = (struct bed *)itemInfo->bed; +struct bed *bed = itemInfo->bed; return bed->chromStart; } static int barChartItemEnd(struct track *tg, void *item) /* Return end chromosome coordinate of item, including graph */ { struct barChartItem *itemInfo = (struct barChartItem *)item; -struct bed *bed = (struct bed *)itemInfo->bed; +struct bed *bed = itemInfo->bed; double scale = scaleForWindow(insideWidth, winStart, winEnd); int graphWidth = chartWidth(tg, itemInfo); return max(bed->chromEnd, max(winStart, bed->chromStart) + graphWidth/scale); } static void getItemX(int start, int end, int *x1, int *x2) /* Return startX, endX based on item coordinates and current window */ // Residual (largely replaced by drawScaledBox -- still used by gene model bmap box { int s = max(start, winStart); int e = min(end, winEnd); double scale = scaleForWindow(insideWidth, winStart, winEnd); assert(x1); *x1 = round((double)((int)s-winStart)*scale + insideX); assert(x2); *x2 = round((double)((int)e-winStart)*scale + insideX); } static void barChartMapItem(struct track *tg, struct hvGfx *hvg, void *item, char *itemName, char *mapItemName, int start, int end, int x, int y, int width, int height) /* Create a map box on item and label, and one for each category (bar in the graph) in * pack or full mode. Just single map for squish/dense modes */ { if (tg->limitedVis == tvDense) { genericMapItem(tg, hvg, item, itemName, itemName, start, end, x, y, width, height); return; } struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; struct barChartItem *itemInfo = (struct barChartItem *)item; -struct bed *bed = (struct bed *)itemInfo->bed; +struct bed *bed = itemInfo->bed; int itemStart = bed->chromStart; int itemEnd = bed->chromEnd; int x1, x2; if (tg->limitedVis == tvSquish) { int categId = maxCategoryForItem(bed, SPECIFICITY_THRESHOLD); char *maxCateg = ""; if (categId > 1) maxCateg = getCategoryLabel(tg, categId); char buf[128]; safef(buf, sizeof buf, "%s %s", bed->name, maxCateg); getItemX(itemStart, itemEnd, &x1, &x2); int width = max(1, x2-x1); mapBoxHc(hvg, itemStart, itemEnd, x1, y, width, height, tg->track, mapItemName, buf); @@ -788,123 +799,154 @@ MgFont *font, Color color, enum trackVisibility vis) { if (vis == tvSquish || vis == tvDense) { // NonProp routines not relevant to these modes, and they interfere // NOTE: they must be installed by barChartMethods() for pack mode tg->nonPropDrawItemAt = NULL; tg->nonPropPixelWidth = NULL; } } static char *barChartMapItemName(struct track *tg, void *item) /* Return item name for click handler */ { struct barChartItem *chartItem = (struct barChartItem *)item; -struct bed *bed = (struct bed *)chartItem->bed; +struct bed *bed = chartItem->bed; return bed->name; } static char *barChartItemName(struct track *tg, void *item) /* Return item name for labeling */ { struct barChartItem *chartItem = (struct barChartItem *)item; -struct bed *bed = (struct bed *)chartItem->bed; +struct bed *bed = chartItem->bed; if (tg->isBigBed) return bigBedItemName(tg, bed); return bed->name; } +void findBarPosX(struct bed *bed, double scale, int barIx, int barCount, int *pStart, int *pEnd) +/* Figure out start/end position of our bar's region */ +{ +int baseWidth = bed->chromEnd - bed->chromStart; +double scaledSize = baseWidth *scale; +double oneWidth = scaledSize/barCount; +double barThinner = 1.0; +if (oneWidth >= 5.0) + { + if (oneWidth > 7.0) + barThinner = 0.8; + else + barThinner = 0.75; + } + +int iStart = baseWidth*barIx/barCount; +int iEnd = baseWidth*(barIx+1)/barCount; +int iWidth = iEnd - iStart; +int start = *pStart = iStart + bed->chromStart; +*pEnd = start + iWidth*barThinner; +} + static void barsToGeneDrawAt(struct track *tg, void *item, struct hvGfx *hvg, int xOff, int y, double scale, MgFont *font, Color color, enum trackVisibility vis) /* Draw bar chart over item in proportional to gene way*/ { /* Fetch our item and the bed from it */ struct barChartItem *itemInfo = (struct barChartItem *)item; struct bed *bed = itemInfo->bed; -double baseWidth = bed->chromEnd - bed->chromStart; /* Figure out where to draw things in Y dimension */ struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; int topGraphHeight = chartHeight(tg, itemInfo); topGraphHeight = max(topGraphHeight, tl.fontHeight); int yZero = topGraphHeight + y - 1; // yZero is bottom of graph int yGene = yZero + extras->margin; /* Figure out width between bars */ -double xStart = (bed->chromStart- winStart) * scale; -double xEnd = (bed->chromEnd - winStart)* scale; -int pixelWidth = xEnd - xStart; int barCount = bed->expCount; -double oneWidth = pixelWidth/barCount; - -/* Figure out how much space to leave between bars */ -int barSpacing = round(oneWidth/10); -if (barSpacing == 0) - { - if (oneWidth >= 3) - barSpacing = 1; - } // drawScaledBox(hvg, bed->chromStart, bed->chromEnd, scale, xOff, y, topGraphHeight, MG_YELLOW); // ugly debug int i; -// int lastEnd = xStart + xOff; Color clipColor = MG_MAGENTA; for (i=0; icolors[i]; int color = hvGfxFindColorIx(hvg, rgb.r, rgb.g, rgb.b); - int cStart = bed->chromStart + baseWidth*i/barCount; - int cEnd = bed->chromStart + baseWidth*(i+1)/barCount; + int cStart, cEnd; + findBarPosX(bed, scale, i, barCount, &cStart, &cEnd); double expScore = bed->expScores[i]; int height = valToClippedHeight(expScore, extras->maxMedian, extras->maxViewLimit, extras->maxHeight, extras->doLogTransform); drawScaledBox(hvg, cStart, cEnd, scale, xOff, y+topGraphHeight-height, height, color); if (!extras->doLogTransform && expScore > extras->maxViewLimit) drawScaledBox(hvg, cStart, cEnd, scale, xOff, yZero-height+1, 2, clipColor); } // Draw the line our graph sits on top of drawScaledBox(hvg, bed->chromStart, bed->chromEnd, scale, xOff, yGene+1, extras->boxModelHeight, MG_GRAY); } static void barsToGeneMapItem(struct track *tg, struct hvGfx *hvg, void *item, char *itemName, char *mapItemName, int start, int end, int x, int y, int width, int height) /* Create a map box on item and label, and one for each category (bar in the graph) */ { -struct barChartItem *chartItem = (struct barChartItem *)item; -struct bed *bed = (struct bed *)chartItem->bed; -genericMapItem(tg, hvg, item, bed->name, bed->name, start, end, x, y, width, height); +struct barChartTrack *extras = tg->extraUiData; +struct barChartItem *itemInfo = item; +struct bed *bed = itemInfo->bed; +if (width > bed->expCount) // When get down to less than a pixel suppress the bar-by-bar map boxes*/ + { + int i = 0; + struct barChartCategory *categs = getCategories(tg); + struct barChartCategory *categ; + int topGraphHeight = chartHeight(tg, itemInfo); + double scale = scaleForWindow(insideWidth, winStart, winEnd); + for (categ = categs; categ != NULL; categ = categ->next, i++) + { + int cStart, cEnd; + findBarPosX(bed, scale, i, bed->expCount, &cStart, &cEnd); + int x1,x2; + if (scaledBoxToPixelCoords(cStart, cEnd, scale, 0, &x1, &x2)) + { + double expScore = bed->expScores[i]; + int height = valToClippedHeight(expScore, extras->maxMedian, extras->maxViewLimit, + extras->maxHeight, extras->doLogTransform); + mapBoxHc(hvg, bed->chromStart, bed->chromEnd, x1+insideX, y+topGraphHeight-height, x2-x1, height, + tg->track, mapItemName, chartMapText(tg, categ, expScore)); + } + } + } +mapBoxHc(hvg, bed->chromStart, bed->chromEnd, x, y, width, height, tg->track, itemName, NULL); } void barChartMethods(struct track *tg) /* Bar Chart track type: draw fixed width chart of colored bars over a BED item */ { tg->bedSize = 8; bedMethods(tg); tg->canPack = TRUE; tg->loadItems = barChartLoadItems; tg->itemName = barChartItemName; tg->mapItemName = barChartMapItemName; tg->itemHeight = barChartItemHeight; tg->itemStart = barChartItemStart; tg->itemEnd = barChartItemEnd; tg->totalHeight = barChartTotalHeight; -if (trackDbSettingClosestToHomeOn(tg->tdb, "barChartFitGene")) +if (trackDbSettingClosestToHomeOn(tg->tdb, BAR_CHART_FIT_TO_GENE)) { tg->drawItemAt = barsToGeneDrawAt; tg->mapItem = barsToGeneMapItem; } else { tg->preDrawItems = barChartPreDrawItems; tg->drawItemAt = barChartDrawAt; tg->mapItem = barChartMapItem; tg->nonPropDrawItemAt = barChartNonPropDrawAt; tg->nonPropPixelWidth = barChartNonPropPixelWidth; } } void barChartCtMethods(struct track *tg)