fe26ea11179837ba80f1e2dd12b9128eb9058b58 jcasper Fri May 13 16:10:49 2022 -0700 Revised method of barChart width calculation does a better job accounting for padding, refs #29425 diff --git src/hg/hgTracks/barChartTrack.c src/hg/hgTracks/barChartTrack.c index 3c967e2..d73326a 100644 --- src/hg/hgTracks/barChartTrack.c +++ src/hg/hgTracks/barChartTrack.c @@ -420,30 +420,40 @@ } static int chartWidth(struct track *tg, struct barChartItem *itemInfo) /* How wide is the chart? */ { struct bed *bed = itemInfo->bed; int itemSize = windowsTotalIntersection(windows, bed->chrom, bed->chromStart, bed->chromEnd); int standardSize = chartStandardWidth(tg, itemInfo); struct barChartTrack *extras = tg->extraUiData; if (extras->stretchToItem) return max(standardSize, itemSize); else return standardSize; } +static int chartWidthToBarWidth(struct track *tg, int graphWidth) +/* After possibly expanding the size of a particular item's graph to fit the item, + * we may need a revised calculation for the size of the bars in that chart */ +{ +struct barChartTrack *extras = (struct barChartTrack *)tg->extraUiData; +int barCount = filteredCategoryCount(extras); +int spaceForBars = graphWidth - 2 - (barCount-1)*extras->padding; +return spaceForBars/barCount; +} + static void mergeBedScores( struct facetedTableMergedOffset *facetsMergeList, struct bed *bedList) /* Return a merged down version of bed. Bed serves as both input and output. */ /* Maybe a use for a nondestructive-to-inputs version will come up soon. This one is faster * for the first use case though. */ { if (bedList == NULL) return; int outSize = slCount(facetsMergeList); if (outSize > bedList->expCount) { errAbort("problem in mergeBedScores(): bed->expCount = %d, outSize = %d", bedList->expCount, outSize); } @@ -737,89 +747,81 @@ 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 = itemInfo->bed; int topGraphHeight = chartHeight(tg, itemInfo); int graphWidth = chartWidth(tg, itemInfo); -#ifdef OLD -#endif /* OLD */ topGraphHeight = max(topGraphHeight, tl.fontHeight); int yZero = topGraphHeight + y - 1; // yZero is bottom of graph int graphX = barChartX(bed); int x0 = xOff + graphX; // x0 is at left of graph int x1 = x0; drawGraphBase(tg, itemInfo, hvg, x0, yZero+1); if (!extras->noWhiteout) drawGraphBox(tg, itemInfo, hvg, x0, yZero+1); struct rgbColor lineColor = {.r=0}; int lineColorIx = hvGfxFindColorIx(hvg, lineColor.r, lineColor.g, lineColor.b); -int barWidth = extras->barWidth; +int barWidth = chartWidthToBarWidth(tg, graphWidth); char *colorScheme = cartUsualStringClosestToHome(cart, tg->tdb, FALSE, BAR_CHART_COLORS, BAR_CHART_COLORS_DEFAULT); Color clipColor = MG_MAGENTA; // draw bar graph int i; int expCount = bed->expCount; struct barChartCategory *categ; -int barCount = filteredCategoryCount(extras), barsDrawn = 0; -double invCount = 1.0/barCount; for (i=0, categ=extras->categories; i<expCount && categ != NULL; i++, categ=categ->next) { if (!filterCategory(extras, categ->name)) continue; - int cStart = barsDrawn * graphWidth * invCount; - int cEnd = (barsDrawn+1) * graphWidth * invCount; // note - removed userMinBarWidth here, as it's already been imposed in the initial barWidth calculation. // Stretch mode only ever extends bars, not shrinks them, so the minimum doesn't need to be re-imposed here. - barWidth = cEnd - cStart - extras->padding; // stop before going off the right edge if (x1 + barWidth > x0 + graphWidth) break; struct rgbColor fillColor = extras->colors[i]; int fillColorIx = hvGfxFindColorIx(hvg, fillColor.r, fillColor.g, fillColor.b); double expScore = bed->expScores[i]; int height = valToClippedHeight(expScore, extras->maxMedian, extras->maxViewLimit, extras->maxHeight, extras->doLogTransform); boolean isClipped = (!extras->doLogTransform && expScore > extras->maxViewLimit); int barTop = yZero - height + 1; if (extras->padding == 0 || sameString(colorScheme, BAR_CHART_COLORS_USER)) hvGfxBox(hvg, x1, barTop, barWidth, height, fillColorIx); else hvGfxOutlinedBox(hvg, x1, barTop, barWidth, height, fillColorIx, lineColorIx); // mark clipped bar with magenta tip if (isClipped) hvGfxBox(hvg, x1, barTop, barWidth, 2, clipColor); x1 += barWidth + extras->padding; - barsDrawn += 1; } } static char *chartMapText(struct track *tg, struct barChartCategory *categ, double expScore) /* Construct mouseover text for a chart bar */ { static char buf[256]; 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 */ @@ -898,49 +900,43 @@ tg->track, mapItemName, itemName); int graphX = barChartX(bed); int graphWidth = chartWidth(tg, itemInfo); int barCount = filteredCategoryCount(extras); char label[256]; if (barCount <= graphWidth) // Don't create map boxes if less than one pixel per bar { // add maps to category bars struct barChartCategory *categs = getCategories(tg); struct barChartCategory *categ = NULL; int x0 = insideX + graphX; int x1 = x0; - double invCount = 1.0/barCount; - int i = 0, barsDrawn = 0, width = 0; + int i = 0, barWidth = max(1,chartWidthToBarWidth(tg, graphWidth)); int extraAtTop = 4; for (categ = categs; categ != NULL; categ = categ->next, i++) { if (!filterCategory(extras, categ->name)) continue; - x1 += width; - int cStart = barsDrawn * graphWidth * invCount; - barsDrawn += 1; - int cEnd = barsDrawn * graphWidth * invCount; - width = max(1, cEnd - cStart); - int boxWidth = min(1, width - extras->padding); // forcing min 1 shouldn't ever actually happen, I think double expScore = bed->expScores[i]; int height = valToClippedHeight(expScore, extras->maxMedian, extras->maxViewLimit, extras->maxHeight, extras->doLogTransform); height = min(height+extraAtTop, extras->maxHeight); - mapBoxHc(hvg, itemStart, itemEnd, x1, yZero-height, boxWidth, height, + mapBoxHc(hvg, itemStart, itemEnd, x1, yZero-height, barWidth, height, tg->track, mapItemName, chartMapText(tg, categ, expScore)); + x1 += barWidth + extras->padding; } safef(label, sizeof(label), "%s - click for faceted view or hover over a bar for sample values", itemName); } else // We should never get here; chartWidth always returns a value where at least one pixel // of width is allocated to every category being drawn safef(label, sizeof(label), "%s - zoom in to resolve individual bars or click for details", itemName); // map over background of chart getItemX(start, end, &x1, &x2); mapBoxHc(hvg, itemStart, itemEnd, x1, y, graphWidth, itemHeight-3, tg->track, mapItemName, label);